From ca5bccce933e1570053d4f7335fd33f669e13b3b Mon Sep 17 00:00:00 2001 From: Elizabeth Cray Date: Wed, 4 Oct 2023 01:26:33 -0400 Subject: [PATCH] OAuth2 Saved Login Session Mechanism --- auth/api/index.php | 71 ++++++++ auth/auth.js | 289 ++++++++++++++++++++++++++++++ auth/index.php | 134 ++------------ auth/setKey.php | 42 ++--- footer.php => elements/footer.php | 0 elements/head.php | 16 ++ elements/logo.php | 9 + elements/oldError.php | 18 ++ elements/unauth.php | 8 + index.js | 238 ++++-------------------- index.php | 49 +---- secret/helpers.php | 18 ++ secret/oauth.php | 12 +- secret/rsa.php | 72 ++++++++ send.php | 63 ------- 15 files changed, 582 insertions(+), 457 deletions(-) create mode 100644 auth/api/index.php create mode 100644 auth/auth.js rename footer.php => elements/footer.php (100%) create mode 100644 elements/head.php create mode 100644 elements/logo.php create mode 100644 elements/oldError.php create mode 100644 elements/unauth.php delete mode 100755 send.php diff --git a/auth/api/index.php b/auth/api/index.php new file mode 100644 index 0000000..4a540f7 --- /dev/null +++ b/auth/api/index.php @@ -0,0 +1,71 @@ + $config->oauth->key)); + exit(); + break; + case "login": + if (isset($_REQUEST["code"])){ + // Mastodon callback (Authorization Code from /oauth/authorize) + $authCode = $_REQUEST["code"]; + $Auth = oauthToken($authCode, $config); + if(isset($Auth->token_type)){ + // Valid Auth? + $User = verifyCredentials($Auth->access_token); + if (gettype($User) == "object" && isset($User->id)) { + // Congrats! + returnSuccess($User, buildEncToken($Auth->access_token, $User->id, $_SERVER["REMOTE_ADDR"], $_SERVER["HTTP_USER_AGENT"])); + }else{ + // invalid auth + // $User contains error string + returnError($User); + } + }else{ + // invalid auth + returnError($Auth); + } + }else{ + returnError("Incorrect Login Query"); + } + break; + case "verify": + if (isset($_REQUEST["token"])){ + // Verify Encrypted Token + $EncTokenData = $_REQUEST["token"]; + $TokenData = verifyEncToken($EncTokenData); + if (gettype($TokenData) == "string") { + // Invalid Token + returnError($TokenData); + }else{ + // Valid Token + returnSuccess($TokenData["MastodonData"], + buildEncToken($TokenData["AuthToken"], + $TokenData["UserID"], + $_SERVER["REMOTE_ADDR"], + $_SERVER["HTTP_USER_AGENT"] + ) + ); + } + }else{ + returnError("Incorrect Verify Query"); + } + break; + default: + returnError("Incorrect Action Query"); + break; + } +}else { + returnError("Incorrect Empty Query"); +} +?> diff --git a/auth/auth.js b/auth/auth.js new file mode 100644 index 0000000..4e64c65 --- /dev/null +++ b/auth/auth.js @@ -0,0 +1,289 @@ +var DEBUG = false; +var isMobile = false; +var USE_ORIGIN = ""; + +const SwalConfig = { + color: "#79F257", + background: "#022601", + buttonsStyling: false, +}; + +const invalidChars = ["/", "\\", ">", "<", ":", "*", "|", '"', "'", "?", "\0"]; + +const failMsg = (msg) => { + Swal.fire({ + ...SwalConfig, + title: "Error!", + text: msg, + }); +}; + +const replaceInvalid = (str) => { + var cache = str; + invalidChars.forEach((ch) => { + cache = cache.replaceAll(ch, "#"); + }); + return cache; +}; + +const post = (url, data, callback) => { + var settings = { + url: url, + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded" + }, + data: data + }; + $.ajax(settings).done(callback); +}; + +const saveFile = (name, type, data) => { + if (data !== null && navigator.msSaveBlob) + return navigator.msSaveBlob(new Blob([data], { type: type }), name); + var a = $(""); + var url = window.URL.createObjectURL(new Blob([data], { type: type })); + a.attr("href", url); + a.attr("download", name); + $("body").append(a); + a[0].click(); + window.URL.revokeObjectURL(url); + a.remove(); +}; + +const validatePubKey = (key) => { + return /^(ssh-rsa AAAAB3NzaC1yc2|ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNT|ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzOD|ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1Mj|ssh-ed25519 AAAAC3NzaC1lZDI1NTE5|ssh-dss AAAAB3NzaC1kc3)[0-9A-Za-z+\/]+[=]{0,3}( .*)?$/.test( + key + ); +}; + +const sendSSH = (key, id, token) => { + var payload = { + pubkey: key, + token: token, + }; + post(USE_ORIGIN + "/auth/setKey.php", payload, (response) => { + if (!response.error) { + localStorage.setItem("tty_token", response.refreshToken); + Swal.fire({ + ...SwalConfig, + title: "Success!", + text: "Your key has been uploaded to the server.", + }); + } else { + Swal.fire({ + ...SwalConfig, + title: "Failed!", + text: response.error, + }).then(() => { + window.location.reload(); + }); + } + }); +}; + +const generateSSH = async () => { + if (window.location.protocol === "http:") { + Swal.fire({ + ...SwalConfig, + title: "Error!", + text: "You must use HTTPS to generate keys.", + }); + return; + } + var token = localStorage.getItem("tty_token"); + var userData = localStorage.getItem("tty_userData"); + if (!token || !userData) { + failMsg("Not Logged In"); + return; + } + userData = JSON.parse(userData); + generateKeyPair("RSASSA-PKCS1-v1_5", 4096, "WebGen " + Date()) + .then((keys) => { + var KeyExport = new JSZip(); + var name = replaceInvalid(userData.username); + KeyExport.file("HackersTownTTY-" + name, keys[0]); + KeyExport.file("HackersTownTTY-" + name + ".pub", keys[1]); + KeyExport.generateAsync({ type: "blob" }).then((content) => { + saveFile("HackersTownTTY-" + name + ".zip", "application/zip", content); + }); + sendSSH(keys[1], userData.id, token); + }) + .catch((err) => { + console.log(err); + failMsg("Failed to generate keypair locally."); + }); +}; + +const testSwal = () => { + Swal.fire({ + ...SwalConfig, + title: "Success!", + }); +}; + +const uploadSSH = () => { + var token = localStorage.getItem("tty_token"); + var userData = localStorage.getItem("tty_userData"); + if (!token || !userData) { + failMsg("Not Logged In"); + return; + } + userData = JSON.parse(userData); + //request local file + var kf = document.getElementById("keyfile"); + kf.onchange = function (e) { + // File selected + var file = e.target.files[0]; + if (file) { + var reader = new FileReader(); + reader.readAsText(file, "UTF-8"); + reader.onload = function (evt) { + var pubkey = evt.target.result; + pubkey = pubkey.replace(/\r\n/g, "\n"); + pubkey = pubkey.replace(/\n/g, ""); + if (validatePubKey(pubkey)) { + sendSSH(pubkey, userData.id, token); + } else { + failMsg("Invalid key"); + } + }; + reader.onerror = function (evt) { + failMsg("Unable to load Keyfile"); + }; + } + }; + kf.click(); +}; + +const displayFingerprints = () => { + // Get SSH Fingerprints and display them + $.get(USE_ORIGIN + "/fingerprint.php", (response) => { + dbp(response); + if (response) { + var html = + '
'; + response.split("\n").forEach((line) => { + var parts = line.split(" "); + if (parts.length === 4) { + html += + '"; + } + }); + html += "
BitsFingerprint (SHA256)Algorithm
' + + parts[0] + + '' + + parts[1].replace("SHA256:", "") + + '' + + parts[3].replace("(", "").replace(")", "") + + "
"; + Swal.fire({ + ...SwalConfig, + title: "SSH Fingerprints", + html: html, + }); + } else { + Swal.fire({ + ...SwalConfig, + title: "Unable to Lookup SSH Fingerprints", + text: response.error, + }); + } + }); +}; + +const checkLogin = () => { + // check login status + var token = localStorage.getItem("tty_token"); + if (token){ + var settings = { + url: USE_ORIGIN + "/auth/api/index.php", + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded" + }, + data: { + act: "verify", + token: token + } + }; + $.ajax(settings).done(function (response) { + if(response.error){ + localStorage.removeItem("tty_token"); + localStorage.removeItem("tty_userData"); + window.location.href = "/?error=" + response.error; + }else { + localStorage.setItem("tty_token", response.refreshToken); + localStorage.setItem("tty_userData", JSON.stringify(response.data)); + document.getElementById("resizer").innerHTML = response.data.display_name; + } + }); + }else { + window.location.href = "/"; + } +}; + +const logout = () => { + localStorage.removeItem("tty_token"); + localStorage.removeItem("tty_userData"); + window.location.href = "/?msg=End%20Of%20Line."; +}; + +$(() => { + // On Page Load + // Override domain + $.get("/DOMAIN_OVERRIDE", function (data) { + USE_ORIGIN = data.replaceAll("\n", ""); + DEBUG = true; + }).fail(() => { + USE_ORIGIN = "https://tty.hackers.town"; + }).always(() => { + // Handle Mobile + if ( /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i + .test(navigator.userAgent ) || + /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i + .test(navigator.userAgent.substr(0, 4) + )) { + isMobile = true; + } + if (isMobile) { + disableNonDesktopElements(); + } + // Handle Auth + var authCode = new URL(window.location.href).searchParams.get("code"); + if (authCode) { + // Mastodon OAuth2 Redirected here + var settings = { + "url": USE_ORIGIN + "/auth/api/index.php", + "method": "POST", + "headers": { + "Content-Type": "application/x-www-form-urlencoded" + }, + "data": { + act: "login", + code: authCode + } + }; + $.ajax(settings).done(function (response) { + if(response.error){ + checkLogin(); + }else{ + localStorage.setItem("tty_token", response.refreshToken); + localStorage.setItem("tty_userData", JSON.stringify(response.data)); + document.getElementById("resizer").innerHTML = response.data.display_name; + } + }); + } else { + // direct connect or from / + checkLogin(); + } + // Console Welcome + console.log("%cWelcome Hacker!", "color: #ff0000; font-size: 7em; font-style: italic; font-family: 'Times New Roman', Times, serif;"); + // Enable Extra Debug Stuff + if(DEBUG){ + $('.debug').each((i,e)=>{ + e.style.display = "unset"; + }); + } + }); +}); diff --git a/auth/index.php b/auth/index.php index db7f212..e971795 100644 --- a/auth/index.php +++ b/auth/index.php @@ -1,149 +1,43 @@ $config->oauth->key)); - exit(); - break; - default: - break; - } -}else if (isset($_REQUEST["code"])){ - // Mastodon callback (Authorization Code from /oauth/authorize) - $MastCode = $_REQUEST["code"]; - // var_dump($_REQUEST); -} + $config = json_decode(file_get_contents("/var/www/usergen/secret/config.json", true)); + require_once("/var/www/usergen/secret/helpers.php"); ?> - - HackersTown Server Access - - - - - - - - - + + - - - - - - - +
+ - token_type)){ - // Valid Auth? - $User = verifyCredentials($Auth->access_token); - if (gettype($User) == "object" && isset($User->id)) { - // Congrats! - $AuthToken = $Auth->access_token; - $UserName = $User->display_name; - $UserId = $User->id; - }else{ - // invalid auth - $AuthToken = "BadUser"; - $ErrorDesc = "User Not Found"; - } - - }else{ - // invalid auth - // TODO: Replace with direct to index logout with error msg - if(isset($_COOKIE["oa_retries"])){ - $retries = $_COOKIE["oa_retries"]; - if($retries >= 3){ - $AuthToken = "BadUser"; - $ErrorDesc = "Invalid OAuth"; - setcookie("oa_retries", 0, time()+3600); - }else{ - $retries++; - setcookie("oa_retries", $retries, time()+3600); - $AuthToken = "BadOauthRetry"; - $ErrorDesc = "Invalid OAuth Retry"; - } - }else{ - $AuthToken = "BadOauth"; - $ErrorDesc = "Invalid OAuth Retry"; - setcookie("oa_retries", 1, time()+3600); - } - } - - // revoke token after usage - ?> -
-
> - - Invalid - - - try again - -
- -
-
> - -
-
> -
Setup an account SSH key
-
> - - +
+ +
- +
+ +
+
diff --git a/auth/setKey.php b/auth/setKey.php index f9618d0..557dfe9 100644 --- a/auth/setKey.php +++ b/auth/setKey.php @@ -2,6 +2,10 @@ // Create an account and apply SSH key $config = json_decode(file_get_contents("/var/www/usergen/secret/config.json", true)); +require_once("/var/www/usergen/secret/helpers.php"); +require_once("/var/www/usergen/secret/oauth.php"); +require_once("/var/www/usergen/secret/rsa.php"); + function checkParameters($parameterArray){ $error = false; foreach($parameterArray as $parameter){ @@ -18,12 +22,13 @@ function apiResult($result){ exit(); } -function success(){ - apiResult(array("status" => true)); +function success($encryptedToken){ + $Auth = verifyEncToken($encryptedToken); + returnSuccess(true, buildEncToken($Auth["AuthToken"], $Auth["UserID"], $_SERVER["REMOTE_ADDR"], $_SERVER["HTTP_USER_AGENT"])); } function error($error){ - apiResult(array("status" => false, "error" => $error)); + returnError($error); } function validateUsername($username){ @@ -34,33 +39,30 @@ function validatePublicKey($key){ return (preg_match("/^(ssh-rsa AAAAB3NzaC1yc2|ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNT|ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzOD|ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1Mj|ssh-ed25519 AAAAC3NzaC1lZDI1NTE5|ssh-dss AAAAB3NzaC1kc3)[0-9A-Za-z+\/]+[=]{0,3}( .*)?$/", $key) == 1); } -if (checkParameters(array("pubkey", "userId", "authToken"))){ +if (checkParameters(array("pubkey", "token"))){ error("Missing parameters"); } -$userToken = $_POST["authToken"]; -$userId = $_POST["userId"]; +$userToken = $_POST["token"]; $pubkey = $_POST["pubkey"]; if(!validatePublicKey($pubkey)){ error("Invalid public key"); } -$request = curl_init(); -curl_setopt($request, CURLOPT_URL, "https://hackers.town/api/v1/accounts/verify_credentials"); -curl_setopt($request, CURLOPT_RETURNTRANSFER, 1); -curl_setopt($request, CURLOPT_HTTPHEADER, array( - "Authorization: Bearer ".$userToken -)); -$response = curl_exec($request); -curl_close($request); -$User = json_decode($response); +$User = verifyEncToken($userToken); // Check User -if($User->id != $userId){ - error("User Mismatch"); + +if (gettype($User) == "string") { + // Invalid Token + error($User); +}else{ + // Valid Token + $User = $User["MastodonData"]; } + if(!validateUsername($User->username)){ - error("Invalid Username"); + error("Invalid POSIX Username"); } // Create temporary pubkey holding file $TempFileName = "/etc/ttyserver/tmp/".uniqid("ssh-", true).".pub"; @@ -68,11 +70,11 @@ if(!file_put_contents($TempFileName, $pubkey."\n")){ error("Key Addition Failed: Temp"); } // Run User Generation Tool -// TODO: Replace with custom Rust PHP Extension +// TODO: Replace with custom Rust PHP Extension? $UserGenCode = shell_exec("/usr/bin/sudo /etc/ttyserver/bin/mkuser \"".$User->username."\" \"".$TempFileName."\" 2>&1; echo $?"); if($UserGenCode != "0"){ error("Key Addition Failed: MK-".$UserGenCode); } -success(); +success($userToken); ?> diff --git a/footer.php b/elements/footer.php similarity index 100% rename from footer.php rename to elements/footer.php diff --git a/elements/head.php b/elements/head.php new file mode 100644 index 0000000..601b16e --- /dev/null +++ b/elements/head.php @@ -0,0 +1,16 @@ + + HackersTown Server Access + + + + + + + + + + + + + + diff --git a/elements/logo.php b/elements/logo.php new file mode 100644 index 0000000..6a1d659 --- /dev/null +++ b/elements/logo.php @@ -0,0 +1,9 @@ +
+ "; + }else{ + echo ""; + }?> + + +
diff --git a/elements/oldError.php b/elements/oldError.php new file mode 100644 index 0000000..8ad8ade --- /dev/null +++ b/elements/oldError.php @@ -0,0 +1,18 @@ + +
> + + Invalid + + + try again + +
+ +
+
> + +
diff --git a/elements/unauth.php b/elements/unauth.php new file mode 100644 index 0000000..27af09e --- /dev/null +++ b/elements/unauth.php @@ -0,0 +1,8 @@ +
+ + <TTY ACCESS> + +
+
+ +
diff --git a/index.js b/index.js index 47114ea..e3ebc45 100644 --- a/index.js +++ b/index.js @@ -2,50 +2,15 @@ var DEBUG = false; var isMobile = false; var USE_ORIGIN = ""; -const dbp = (msg) => { - if(DEBUG){ - console.log(msg); - } -}; - -const dbd = (msg) => { - if(DEBUG){ - console.dir(msg); - } -}; - const SwalConfig = { color: "#79F257", background: "#022601", buttonsStyling: false, } -const invalidChars = [ - '/', '\\', '>', - '<', ':', '*', - '|', '"', '\'', - '?', '\0' -]; - -const replaceInvalid = (str) => { - var cache = str; - invalidChars.forEach(ch => { - cache = cache.replaceAll(ch, "#"); - }); - return cache; -} - const isOverflown = ({ clientHeight, scrollHeight }) => scrollHeight > clientHeight -const setCookie = (cname, cvalue, exdays) => { - const d = new Date(); - d.setTime(d.getTime() + (exdays*24*60*60*1000)); - let expires = "expires="+ d.toUTCString(); - document.cookie = cname + "=" + cvalue + ";" + expires + ";SameSite=Strict;path=/auth"; -} - const resizeText = ({ element, elements, minSize = 10, maxSize = 512, step = 1, unit = 'px' }) => { - dbp("Resize"); (elements || [element]).forEach(el => { let i = minSize; let overflow = false; @@ -60,19 +25,6 @@ const resizeText = ({ element, elements, minSize = 10, maxSize = 512, step = 1, }); } -const saveFile = (name, type, data) => { - if (data !== null && navigator.msSaveBlob) - return navigator.msSaveBlob(new Blob([data], { type: type }), name); - var a = $(""); - var url = window.URL.createObjectURL(new Blob([data], {type: type})); - a.attr("href", url); - a.attr("download", name); - $("body").append(a); - a[0].click(); - window.URL.revokeObjectURL(url); - a.remove(); -} - const disableNonDesktopElements = () => { var disableElements = document.getElementsByClassName("desktopOnly"); for(var i=0; i< disableElements.length; i++){ @@ -94,140 +46,25 @@ const disableNonDesktopElements = () => { } const failMsg = (msg) => { - $("#resizer").html(msg); - $("#resizer").css("color", "#400112"); - $("#resizer").css("background-color", "#79F257"); - $("#resizer").animate({ - color: "#79F257", - backgroundColor: "#022601" - }, 1000); -} - -const validatePubKey = (key) => { - return /^(ssh-rsa AAAAB3NzaC1yc2|ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNT|ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzOD|ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1Mj|ssh-ed25519 AAAAC3NzaC1lZDI1NTE5|ssh-dss AAAAB3NzaC1kc3)[0-9A-Za-z+\/]+[=]{0,3}( .*)?$/.test(key); -} - -const sendSSH = (key, id, token) => { - var payload = { - pubkey: key, - userId: id, - authToken: token - }; - $.post(USE_ORIGIN+"/auth/setKey.php", payload, (response) => { - dbp(response); - if(response.status){ - Swal.fire({ - ...SwalConfig, - title: "Success!", - text: "Your key has been uploaded to the server." - }).then(()=>{ - window.location.reload(); - }); - }else{ - Swal.fire({ - ...SwalConfig, - title: "Failed!", - text: response.error - }).then(()=>{ - window.location.reload(); - }); - } - }).fail((resp) => { - dbp("Failed"); - dbd(resp); - Swal.fire({ - ...SwalConfig, - title: "Failed!", - text: resp.toString() - }).then(()=>{ - window.location.reload(); - }); - }); -} - -const generateSSH = async (name, id, token) => { - dbp("Generate Key"); - if (window.location.protocol === "http:") { - Swal.fire({ - ...SwalConfig, - title: "Error!", - text: "You must use HTTPS to generate keys." - }); - return; - } - generateKeyPair("RSASSA-PKCS1-v1_5", 4096, "WebGen " + Date()) - .then((keys) => { - var KeyExport = new JSZip(); - name = replaceInvalid(name); - KeyExport.file("HackersTownTTY-"+name, keys[0]); - KeyExport.file("HackersTownTTY-"+name+".pub", keys[1]); - KeyExport.generateAsync({type:"blob"}) - .then((content) => { - saveFile("HackersTownTTY-"+name+".zip", "application/zip", content); - }); - sendSSH(keys[1], id, token); - }).catch((err) => { - dbp(err); - }); -} - -const testSwal = () => { Swal.fire({ ...SwalConfig, - title: "Success!", + title: "Error!", + text: msg, }); } -const uploadSSH = (id, token) => { - //request local file - var kf = document.getElementById("keyfile"); - kf.onchange = function(e) { - // File selected - var file = e.target.files[0]; - if(file){ - dbd(file); - var reader = new FileReader(); - reader.readAsText(file, "UTF-8"); - reader.onload = function (evt) { - var pubkey = evt.target.result; - pubkey = pubkey.replace(/\r\n/g, "\n"); - dbp(pubkey); - pubkey = pubkey.replace(/\n/g, ""); - dbp(pubkey); - if(validatePubKey(pubkey)){ - sendSSH(pubkey, id, token); - }else{ - failMsg("Invalid key"); - } - } - reader.onerror = function (evt) { - failMsg("Unable to load Keyfile"); - } - - } - } - kf.click(); -} - const beginOauth = () => { - dbp("Auth"); $.ajax({ - url: USE_ORIGIN+"/auth?act=id" + url: USE_ORIGIN+"/auth/api?act=id" }).then((data) => { - dbd(data); if(data.id){ var redirect = "https://hackers.town/oauth/authorize?"+ "response_type=code&client_id="+data.id+"&redirect_uri="+ USE_ORIGIN+"/auth&scope=read:accounts"; - dbp(redirect); - dbp(window.location.pathname); - if(window.location.pathname.includes("auth")){ - setCookie("oa_retries", 0, 0.1); - } window.location.href = redirect; }else{ // Auth Failed - failMsg("AUTH FAILED"); + failMsg("Server ID Error"); } }); } @@ -262,8 +99,9 @@ const displayFingerprints = () => { // On Page Load... $(() => { - dbp("Begin Init Content"); - + if (localStorage.getItem("tty_token")) { + window.location.href = "/auth"; + } // Override domain $.get("/DOMAIN_OVERRIDE", function (data) { USE_ORIGIN = data.replaceAll("\n", ""); @@ -272,41 +110,35 @@ $(() => { USE_ORIGIN = "https://tty.hackers.town"; }).always(() => { - // Device Detection - if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent) || - /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0, 4))) { - isMobile = true; - } - // Adjust for Mobile - if(isMobile){ - dbp("Is Mobile 👍🏻"); - disableNonDesktopElements(); - } - - // Auto Retry - // TODO: Replace with redirect to home with logout set if error - var isRetry = false; - if(window.location.pathname.includes("auth")){ - var ErrorMsg = document.getElementById("ErrorResult"); - dbp(typeof ErrorMsg); - if(typeof ErrorMsg !== 'undefined' && ErrorMsg.innerText.includes("Retry")){ - dbp("attempt retry"); - isRetry = true; - beginOauth(); + // Device Detection + if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent) || + /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0, 4))) { + isMobile = true; } - } - - // Console Welcome - if(!isRetry){ + // Adjust for Mobile + if(isMobile){ + disableNonDesktopElements(); + } + // Console Welcome console.log("%cWelcome Hacker!", "color: #ff0000; font-size: 7em; font-style: italic; font-family: 'Times New Roman', Times, serif;"); - } + // Enable Extra Debug Stuff + if(DEBUG){ + $('.debug').each((i,e)=>{ + e.style.display = "unset"; + }); + } + var parameters = new URL(window.location.href).searchParams; + var message = parameters.get("msg"); + var error = parameters.get("error"); + if(error){ + failMsg(error); + } + if(message){ + Swal.fire({ + ...SwalConfig, + text: message + }); + } - // Enable Extra Debug Stuff - if(DEBUG){ - dbp("Debug Mode Enabled"); - $('.debug').each((i,e)=>{ - e.style.display = "unset"; - }); - } - }) + }) }); diff --git a/index.php b/index.php index c8c713c..1ce0545 100755 --- a/index.php +++ b/index.php @@ -2,56 +2,17 @@ - - HackersTown Server Access - - - - - - - - - - - - - - + +
- -
- - <TTY ACCESS> - -
-
- -
- + + +
diff --git a/secret/helpers.php b/secret/helpers.php index 7511378..3f9da87 100644 --- a/secret/helpers.php +++ b/secret/helpers.php @@ -19,4 +19,22 @@ function enableDebug(){ ini_set('display_startup_errors', 1); error_reporting(E_ALL); } + +function returnError($error){ + if (gettype($error) == "object") { + $error = json_encode($error); + } + header('Content-type: application/json'); + echo json_encode(array("error" => $error)); + exit(); +} + +function returnSuccess($success, $refreshToken = ""){ + header('Content-type: application/json'); + echo json_encode(array( + "data" => $success, + "refreshToken" => $refreshToken + )); + exit(); +} ?> diff --git a/secret/oauth.php b/secret/oauth.php index 582300b..f092baf 100644 --- a/secret/oauth.php +++ b/secret/oauth.php @@ -10,13 +10,6 @@ function verifyCredentials($Auth) { $response = curl_exec($request); curl_close($request); $User = json_decode($response); - - // if (isset($User->id)){ - // // Congrats! - // $UserName = $User->display_name; - // $UserId = $User->id; - // }else{ - // } return (isset($User->error) ? $User->error : $User); } @@ -41,4 +34,9 @@ function oauthToken($AuthCode, $config){ $Auth = json_decode($response); return (isset($Auth->error) ? $Auth->error_description : $Auth); } + +function getHello() { + $Welcomes = array("Welcome", "Dobrodošli", "Vitejte", "Welkom", "Tervetuloa", "Willkommen", "Fáilte", "Benvenuto", "Bienvenidos", "Välkommen", "ようこそ"); + return $Welcomes[array_rand($Welcomes)]; +} ?> diff --git a/secret/rsa.php b/secret/rsa.php index 009e20b..4c34240 100755 --- a/secret/rsa.php +++ b/secret/rsa.php @@ -38,6 +38,26 @@ function getFingerprint() { return $fingerprint; } +// Object -> JSON -> Base84 -> Split -> Encrypt -> Combined -> re-base64 -> Sent + +function encryptPayload($input){ + $holdingArray = str_split(base64_encode($input), 64); + $holdingArray = array_map(function($value){ + return encrypt($value); + }, $holdingArray); + $holdingArray = implode("%", $holdingArray); + return $holdingArray; +} + +function decryptPayload($input){ + $holdingArray = explode("%", $input); + $holdingArray = array_map(function($value){ + return decrypt($value); + }, $holdingArray); + $holdingArray = implode("", $holdingArray); + return base64_decode($holdingArray); +} + function encrypt($input){ // Encrypt with public key ensureKey(); @@ -61,4 +81,56 @@ function decrypt($input){ return $decrypted; } +function buildEncToken($AuthToken, $UserID, $UserIP, $UserAgent ){ + // Token Data: + // - HTown AuthToken + // - UserID + // - UserIP + // - UserAgent + // - Timestamp + $TokenData = array( + "AuthToken" => $AuthToken, + "UserID" => $UserID, + "UserIP" => $UserIP, + "UserAgent" => $UserAgent, + "Timestamp" => time() // Unix Time in Seconds + ); + $TokenData = json_encode($TokenData); + $EncTokenData = encryptPayload($TokenData); + return $EncTokenData; +} + +function verifyEncToken($EncTokenData){ + $TokenData = decryptPayload($EncTokenData); + $TokenData = json_decode($TokenData); + if ($TokenData != null && isset($TokenData->AuthToken) && isset($TokenData->UserID) && isset($TokenData->UserIP) && isset($TokenData->UserAgent) && isset($TokenData->Timestamp)) { + // Valid Token + if (time() - $TokenData->Timestamp > 900) { // 15-minute max login session + // Token Expired + return "Login Expired"; + } + if ($TokenData->UserIP != $_SERVER["REMOTE_ADDR"]) { + // IP Mismatch + return "IP Mismatch"; + } + if ($TokenData->UserAgent != $_SERVER["HTTP_USER_AGENT"]) { + // User Agent Mismatch + return "UserAgent Mismatch"; + } + $credentialResults = verifyCredentials($TokenData->AuthToken); + if (gettype($credentialResults) == "string") { + // Invalid AuthToken + return "Invalid Mastodon Account"; + } + return array( + "AuthToken" => $TokenData->AuthToken, + "UserID" => $TokenData->UserID, + "MastodonData" => $credentialResults + ); + }else{ + // Invalid Token + return "Invalid Token"; + } +} + ?> diff --git a/send.php b/send.php deleted file mode 100755 index 0dfac45..0000000 --- a/send.php +++ /dev/null @@ -1,63 +0,0 @@ - $config.oauth.key, - 'clientSecret' => $config.oauth.secret, - 'redirectUri' => $origin.'/auth', - 'instance' => 'https://hackers.town', - 'scope' => 'read:accounts', -]); - - -if (!isset($_GET['code'])) { - - $authUrl = $provider->getAuthorizationUrl(); - - $_SESSION['oauth2state'] = $provider->getState(); - header('Location: '.$authUrl); - exit; - -// Check given state against previously stored one to mitigate CSRF attack -} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { - - unset($_SESSION['oauth2state']); - exit('Invalid state'); - -} else { - - // Try to get an access token (using the authorization code grant) - $token = $provider->getAccessToken('authorization_code', [ - 'code' => $_GET['code'] - ]); - - // Optional: Now you have a token you can look up a users profile data - try { - - $user = $provider->getResourceOwner($token); - - echo $user->getName(); - - } catch(Exception $e) { - - - exit('Oh dear...'); - } - - - echo $token->getToken(); -} - -?>