diff options
Diffstat (limited to 'prog')
-rw-r--r-- | prog/studisfri/makefile | 10 | ||||
-rwxr-xr-x | prog/studisfri/screenshot.sh | 13 | ||||
-rw-r--r-- | prog/studisfri/script.js | 1 | ||||
-rw-r--r-- | prog/studisfri/studis_account.php | 323 | ||||
-rw-r--r-- | prog/studisfri/studisfri | 28 |
5 files changed, 375 insertions, 0 deletions
diff --git a/prog/studisfri/makefile b/prog/studisfri/makefile new file mode 100644 index 0000000..363ee2f --- /dev/null +++ b/prog/studisfri/makefile @@ -0,0 +1,10 @@ +default: fetchsrc + +fetchsrc: + sftp s@t <<<"get /etc/nginx/sites/studisfri" + sftp s@t <<<"get studisfri/studis_account.php" + sftp s@t <<<"get studisfri/script.js" + sftp s@t <<<"get studisfri/screenshot.sh" + + +.PHONY: default fetchsrc diff --git a/prog/studisfri/screenshot.sh b/prog/studisfri/screenshot.sh new file mode 100755 index 0000000..5fdc8a1 --- /dev/null +++ b/prog/studisfri/screenshot.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -xe +umask 0077 +p=`rev <<<$0 | cut -d/ -f1 | rev` +t=`mktemp -p "" -d $p.XXX` +librewolf --headless --profile $t --no-remote --new-instance --screenshot $t/screenshot.png $1 +mount | grep "on /proc type proc" | grep hidepid=invisible || echo POZOR! leakal bom ime datoteke v procfs! POPRAVI!!! +h=`sha256sum $t/screenshot.png | cut -d\ -f1` +[ -f ../www/studisfri/$h.png ] && echo datoteka_že_obstaja +mv $t/screenshot.png ../www/studisfri/$h.png +chmod o+r ../www/studisfri/$h.png +echo zgoščena_vrednost $h +rm -r $t diff --git a/prog/studisfri/script.js b/prog/studisfri/script.js new file mode 100644 index 0000000..db25d21 --- /dev/null +++ b/prog/studisfri/script.js @@ -0,0 +1 @@ +console.log("studisfri hijacker loaded - NOOP"); diff --git a/prog/studisfri/studis_account.php b/prog/studisfri/studis_account.php new file mode 100644 index 0000000..2605da0 --- /dev/null +++ b/prog/studisfri/studis_account.php @@ -0,0 +1,323 @@ +<?php +function studis_get ($cookie) { + $string = ""; + $resp = @file_get_contents("https://studisfri.uni-lj.si/StudentProfil/KontaktniPodatki", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$cookie}"]])); + if (strpos($resp, "/Account/Logout") === false) + return false; + $x = new DOMDocument(); + @$x->loadHTML($resp); + $un = trim(explode(" ", trim($x->getElementsByTagName("address")[0]->nodeValue))[0]); + $string .= $resp; + $resp = @file_get_contents("https://studisfri.uni-lj.si/DashboardStudent", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$cookie}"]])); + if (strpos($resp, "/Account/Logout") === false) + return false; + $string .= $resp; + $resp = @file_get_contents("https://studisfri.uni-lj.si/Student/ElektronskiIndeksStudent", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$cookie}"]])); + if (strpos($resp, "/Account/Logout") === false) + return false; + $string .= $resp; + if (strpos($un, "@") !== false) { + global $db; + $stmt = $db->prepare("update users set cookies=:cookies where username=:username"); + $stmt->bindParam(":username", $un, PDO::PARAM_STR); + $stmt->bindParam(":cookies", $cookie, PDO::PARAM_STR); + $stmt->execute(); + $cookies = []; + foreach ($http_response_header as $h) { + if (strtolower(explode(": ", $h)[0]) == "set-cookie") { + $cookie = explode("; ", explode(": ", $h)[1])[0]; + $cookies[] = $cookie; + add_infinite_cookie($cookie); + } else + if (strtolower(explode(": ", $h)[0]) != "location") + header($h); + } + if (sizeof($cookies)) { + $stmt = $db->prepare("update users set cookies=:cookies where username=:username"); + $stmt->bindParam(":username", $un, PDO::PARAM_STR); + $cookies = implode("; ", $cookies); + $stmt->bindParam(":cookies", $cookies, PDO::PARAM_STR); + $stmt->execute(); + } + } + return ["hash" => hash("sha256", $string, true), "username" => $un]; +} +function add_infinite_cookie ($cookie) { + header("Set-Cookie: $cookie; Path=/; Expires=Fri, 31 Dec 9999 23:59:59 GMT; Secure; HttpOnly", false); +} +function make_login_page ($resp) { + $replace = <<<HEREDOC + <details style=margin:1cm> + <summary> + ▶ Pokaži polje za nalaganje obstoječe seje na + strežnik (za napredne uporabnike) + </summary> + + <div class="form-group"> + <label for=Session> + Sejni piškotek <code>.ASPXAUTH</code> prilepite v spodnje polje obliki, kot bi bil poslan v <code>Cookie:</code> headerju. Primer: <code>.ASPXAUTH=713851603</code> + </label> + <div class="col-sm-offset-2 col-sm-8"> + <div class="input-group"> + <div class="input-group-addon"> + <i class="fa fa-fw fa-unlock-alt"></i> + </div> + <input autocomplete="off" class="form-control" id="Session" name="Session" placeholder="Sejni piškotki" type="text" /> + </div> + <p>Uporabniško ime in geslo morate prav tako vnesti. Posebej bodite pazljivi, da je geslo pravnilno vnešeno, saj strežnik njegove pravilnosti ne bo preverjal.</p> + </div> + + </details> + <div class="modal-footer"> +HEREDOC; + echo str_replace('<div class="modal-footer">', $replace, str_replace("studisfri.uni-lj.si", $_SERVER["HTTP_HOST"], $resp)); +} +function waste_login ($tekst) { + if (!empty($_REQUEST["potrdilo"])) { + $resp = file_get_contents("https://studisfri.uni-lj.si/Account/Login", false, stream_context_create(["http" => ["follow_location" => 0, "method" => "POST", "header" => "Content-Type: application/x-www-form-urlencoded\r\nCookie: {$_SERVER["HTTP_COOKIE"]}", "content" => "__RequestVerificationToken=" . urlencode($_POST["rvt"]) . "&Username=" . urlencode($_POST["username"]) . "&Password=" . urlencode($_POST["password"])]])); + # file_put_contents("/tmp/resp.html", $resp); + # file_put_contents("/tmp/http_response_header.txt", implode("\r\n", $http_response_header)); + if (strpos($http_response_header[0], "302") !== false) { + http_response_code(303); + $cookies = []; + foreach ($http_response_header as $h) { + if (strtolower(explode(": ", $h)[0]) == "set-cookie") { + $cookie = explode("; ", explode(": ", $h)[1])[0]; + $cookies[] = $cookie; + add_infinite_cookie($cookie); + } else + if (strtolower(explode(": ", $h)[0]) != "location") + header($h); + } + global $db; + $stmt = $db->prepare("insert into users (username, cookies, password, last) values (:username, :cookies, :password, CURRENT_TIMESTAMP) on conflict(username) do update set username=:username, cookies=:cookies, password=:password, last=CURRENT_TIMESTAMP"); + $stmt->bindParam(":username", $_POST["username"], PDO::PARAM_STR); + $cookies = implode("; ", $cookies); + $stmt->bindParam(":cookies", $cookies, PDO::PARAM_STR); + $password = password_hash($_POST["password"], PASSWORD_DEFAULT); + $stmt->bindParam(":password", $password, PDO::PARAM_STR); + $stmt->execute(); + header("Location: /"); + } else { + echo make_login_page($resp); + } + } else { + echo $tekst . ' Če nadaljujete s prijavo z uporabniškim imenom in geslom, bo porabljena ena vaša prijava brez kvalificiranega potrdila.<form method=post><input type=hidden name=username value="' . htmlspecialchars($_POST["Username"]) . '" /><input type=hidden name=password value="' . htmlspecialchars($_POST["Password"]) . '" /><input type=hidden name=rvt value=' . htmlspecialchars($_POST["__RequestVerificationToken"]) . ' /><input type=submit name=potrdilo value="Nadaljuj s prijavo v STUDIS >>>" /></form>Če se vam večkrat zaporedoma kaže to sporočilo, ohranjanje sej na strežniku mogoče ne deluje. Prosim, da mi v tem primeru pošljete pismo na naslov <a href=mailto:anton@sijanec.eu>anton@šijanec.eu</a> in do popravila sistema uporabljate <a href=https://studisfri.uni-lj.si>uradni portal STUDIS</a>.'; + } +} +umask(0077); +if (!empty($_REQUEST["src"])) { + die(file_get_contents($_SERVER["SCRIPT_FILENAME"])); +} +$db = new PDO("sqlite:studis.sqlite3", null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); +$did = false; +if (!$db || !empty($_REQUEST["dberror"])) { + http_response_code(503); + echo "Strežnik ne more odpreti podatkovne zbirke. Prosim, kontaktirajte me na naslov <a href=mailto:anton@sijanec.eu>anton@šijanec.eu</a>. Med nedelovanjem moje storitve lahko uporabljate uradni portal na spletni strani <a href=https://studisfri.uni-lj.si>studisfri.uni-lj.si</a>, vendar boste koristili preostale prijave."; + $did = true; +} +$db->query("create table if not exists users (username TEXT PRIMARY KEY UNIQUE NOT NULL CHECK(length(username) > 0), cookies TEXT UNIQUE NOT NULL, password TEXT NOT NULL, last default CURRENT_TIMESTAMP, mail INTEGER, hash TEXT CHECK(length(hash) == 32)) +"); +if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "script") !== false) { + studis_get($_SERVER["HTTP_COOKIE"]); + die(file_get_contents("script.js")); +} +if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "odjava") !== false) { + if ($_SERVER["REQUEST_METHOD"] == "POST") { + $stmt = $db->prepare("update users set mail=0 where hash=:hash"); + $stmt->bindParam(":hash", $_REQUEST["hash"], PDO::PARAM_LOB); + $stmt->execute(); + echo "Zahteva po odjavi uspešno prejeta."; + } else { + echo "S klikom na gumb Odjava se odjavite od pridobivanja sporočil na ta elektronski naslov. Ponovno se lahko prijavite na sporočila samo v <a href=nastavitve>nastavitvah</a> med tem, ko ste prijavljeni. Ta odjava deluje tudi takrat, ko niste prijavljeni, vendar je povezava za odjavo delujoča le od zadnje vzpostavljene seje.<form method=post><input type=submit name=odjava value='Odjavi se od prejemanja elektronskih sporočil' /></form>"; + } + $did = true; +} +if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "nastavitve") !== false) { + $r = studis_get($_SERVER["HTTP_COOKIE"]); + if ($r) { + if ($_SERVER["REQUEST_METHOD"] == "POST") { + if (password_verify($_SERVER["HTTP_COOKIE"], $_POST["csrf"])) { + $value = 0; + if (@$_POST["mail"] == "prosim") + $value = 1; + $stmt = $db->prepare("update users set mail=$value, hash=:hash where username=:username"); + $stmt->bindParam(":username", $r["username"], PDO::PARAM_STR); + $stmt->bindParam(":hash", $r["hash"], PDO::PARAM_LOB); + $stmt->execute(); + } else { + echo "Zgodil se je CSRF napad ali pa napaka na strežniku. Ne spreminjam nobenih nastavitev. Kontaktirate lahko administratorja na naslov <a href=mailto:anton@sijanec.eu>anton@šijanec.eu</a>."; + } + } + $csrf = password_hash($_SERVER["HTTP_COOKIE"], PASSWORD_DEFAULT); + $stmt = $db->prepare("select mail from users where username=:username"); + $stmt->bindParam(":username", $r["username"], PDO::PARAM_STR); + $stmt->execute(); + echo "<h1>dodatne nastavitve posredniškega strežnika studisfri</h1><form method=POST><input type=hidden name=csrf value=$csrf><input type=checkbox id=mail name=mail value=prosim " . ($stmt->fetchColumn(0) ? "checked" : "") . " /><label for=mail>pošlji obvestilo na moj elektronski naslov ({$r["username"]}), ko se spremeni kaj na Studisu (preverjanje štirikrat dnevno) oziroma če prijava v STUDIS ne uspe</label><br><input type=submit name=submit value=shrani /></form><a href=/>Nazaj na glavno stran <<<</a>"; + } else { + http_response_code(303); + header("Location: /Account/Login"); + } + $did = true; +} +if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "logout") !== false) { + http_response_code(303); + setcookie(".ASPXAUTH", "pls expire my digga", 1, "/", null, true, true); + header("Location: /"); + // echo "Odjava uspešna. Morebitno sejo ohranjam na strežniku. Preusmerjam na prijavno stran v petih sekundah. <meta http-equiv=refresh content=5;/ />"; + $did = true; +} +if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "cookies") !== false) { + if (strpos($_REQUEST["cookies"], "\n") !== false || strpos($_REQUEST["location"], "\n") !== false) + die("hacker reported to the fbi"); + header("Location: " . $_REQUEST["location"]); + foreach (explode("; ", $_REQUEST["cookies"]) as $c) + add_infinite_cookie($c); +} +if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "cron") !== false) { + $ret = $db->query("select username, cookies, mail, hash from users"); + header("Content-Type: text/plain"); + foreach ($ret as $row) { + $g = studis_get($row[1]); + if ($g === false) { + echo "neuspelo\t" . $row[0]; + if ($row[2]) { + $uehash = urlencode($row[3]); + mail($row[0], "Neuspela prijava v portal STUDIS", "Spoštovani,\r\n\r\nobveščam vas, da se posredniški strežnik STUDISa v vaš profil ni uspel prijaviti. Lahko gre le za začasno napako (izpad omrežne povezave), ali pa je potekla vaša seja na strežniku. Poštna obvestila lahko izklopite brez prijave na povezavi https://studisfri.4a.si/Account/odjava?hash=$uehash ali pa s pismom administratorju na naslov anton@sijanec.eu.\r\n\r\nLep pozdrav\r\nPHP\r\n", "From: studisfri@4a.si\r\nReply-To: anton@sijanec.eu"); + echo "\tmail"; + } + echo PHP_EOL; + } else { + $uc = urlencode($row[1]); + $izhod = `./screenshot.sh 'https://studisfri.4a.si/Account/cookies?cookies=$uc&location=/' 2>&1`; + $h = ""; + foreach (explode("\n", $izhod) as $v) { + $x = explode(" ", $v); + if ($x[0] == "zgoščena_vrednost") + $h = " Posnetek zaslona vaše nadzorne plošče na portalu STUDIS si lahko ogledate na https://s.4a.si/studisfri/{$x[1]}.png"; + } + echo "uspelo\t" . $g["username"]; + if ($row[2] && $g["hash"] != $row[3] && strpos($izhod, "datoteka_že_obstaja") === false) { + $uehash = urlencode($g["hash"]); + mail($row[0], "Sprememba na portalu STUDIS", "Spoštovani,\r\n\r\nobveščam vas, da se je na vašem STUDIS portalu {$row[0]} pojavila sprememba. Portal STUDIS je dostopen na povezavi https://studisfri.4a.si/. Ta obvestila lahko izklopite brez prijave na naslovu https://studisfri.4a.si/Account/odjava?hash=$uehash ali pa s pismom administratorju na naslov anton@sijanec.eu.$h\r\n\r\nLep pozdrav\r\nPHP\r\n\r\n\r\n---------\r\nDiagnostične informacije sledijo:\r\nPrejšnja zgoščena vrednost STUDIS: " . bin2hex($row[3]) . "\r\nTrenutna zgoščena vrednost STUDIS: " . bin2hex($g["hash"]) . "\r\n\r\nIzhod programa screenshot.sh:\r\n$izhod", "From: studisfri@4a.si\r\nReply-To: anton@sijanec.eu"); + echo "\tmail"; + } + $stmt = $db->prepare("update users set last=CURRENT_TIMESTAMP, hash=:hash where username=:username"); + $stmt->bindParam(":username", $row[0], PDO::PARAM_STR); + $stmt->bindParam(":hash", $g["hash"], PDO::PARAM_LOB); + $stmt->execute(); + echo PHP_EOL; + } + } + die(); +} +if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "setculture") !== false) { + @file_get_contents("https://studisfri.uni-lj.si/Account/SetCulture?culture={$_GET['culture']}", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$_SERVER["HTTP_COOKIE"]}"]])); + http_response_code(303); + header("Location: {$_GET["ReturnUrl"]}"); +} +if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "registercertificate") !== false) { + http_response_code(404); + echo "Kvalificiranega digitalnega potrdila ne morete registrirati preko tega posredniškega strežnika. Za registracijo potrdila uporabite uradno spletno stran na naslovu <a href=https://studisfri.uni-lj.si/Account/RegisterCertificate>studisfri.uni-lj.si</a>."; + $did = true; +} +if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "login") !== false) { + $resp = @file_get_contents("https://studisfri.uni-lj.si/Account/Login", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$_SERVER["HTTP_COOKIE"]}"]])); + if (strpos($resp, "/Account/Logout") !== false) { + http_response_code(303); + header("Location: /"); + } else { + if ($_SERVER["REQUEST_METHOD"] != "POST") { + foreach ($http_response_header as $h) { + if (strtolower(explode(": ", $h)[0]) == "set-cookie") { + $cookie = explode("; ", explode(": ", $h)[1])[0]; + $cookies[] = $cookie; + add_infinite_cookie($cookie); + } else { + header($h); + } + } + echo make_login_page($resp); + } else { + $stmt = $db->prepare("select cookies, password from users where username=:username"); + $stmt->bindParam(":username", $_POST["Username"], PDO::PARAM_STR); + // $stmt->debugDumpParams(); + $stmt->execute(); + $row = $stmt->fetch(); + if (!empty($_POST["Session"])) { + $resp = @file_get_contents("https://studisfri.uni-lj.si/StudentProfil/KontaktniPodatki", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$_POST["Session"]}"]])); + if (strpos($resp, "/Account/Logout") !== false) { + $x = new DOMDocument(); + @$x->loadHTML($resp); + $un = trim(explode(" ", trim($x->getElementsByTagName("address")[0]->nodeValue))[0]); + if ($un == $_POST["Username"]) { + $stmt = $db->prepare("insert into users (username, cookies, password, last) values (:username, :cookies, :password, CURRENT_TIMESTAMP) ON CONFLICT(username) DO UPDATE SET username=:username, cookies=:cookies, password=:password"); + $stmt->bindParam(":username", $_POST["Username"]); + $stmt->bindParam(":cookies", $_POST["Session"]); + $pwhash = password_hash($_POST["Password"], PASSWORD_DEFAULT); + $stmt->bindParam(":password", $pwhash); + $stmt->execute(); + http_response_code(303); + foreach (explode("; ", $_POST["Session"]) as $cookie) { + add_infinite_cookie($cookie); + } + header("Location: /"); + } else { + echo "Uporabniško ime podane seje ($un) se ne ujema z vašim podanim uporabniškim imenom v prijavnem obrazcu ({$_POST["Username"]}). <a href=/>Nazaj na prijavno stran <<<</a>"; + } + } else { + waste_login("Poslani sejni piškotki vas ne prijavijo v STUDIS."); + } + } else { + if ($row == false || $row["cookies"] == false) { + waste_login("Strežnik nima shranjene vaše seje."); + } else { + $resp = @file_get_contents("https://studisfri.uni-lj.si/Account/Login", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$row["cookies"]}"]])); + if (strpos($resp, "/Account/Logout") !== false) { + if (password_verify($_POST["Password"], $row["password"])) { + http_response_code(303); + foreach (explode("; ", $row["cookies"]) as $cookie) { + add_infinite_cookie($cookie); + } + header("Location: /"); + } else { + waste_login("Napačno geslo. <b>Na strežniku obstaja aktivna seja.</b> Če ste menjali svoje ID UL geslo, se lahko prijavite s starim, kar bo nadaljevalo vašo obstoječo sejo, če se pa starega gesla ne spomnite, pa lahko nadaljujete s prijavo z novim geslom in s tem porabite eno prijavo (v kolikor je geslo pravilno)."); + } + } else { + waste_login("Seja, shranjena na strežniku, je potekla."); + } + } + } + } + } + $did = true; +} +if (!$did) { + echo "Program ni naredil ničesar. Če vidite to sporočilo, se je zgodila napaka. Prosim, kontaktirajte me na naslov <a href=mailto:anton@sijanec.eu>anton@šijanec.eu</a>. <a href=/>Nazaj na glavno stran</a>"; +} +?> +<meta name=viewport content='width=device-width, initial-scale=1.0'> +<br><br><br> +<hr> +<details><summary>Prikaži diagnostične informacije</summary> +<pre> +<?php htmlspecialchars(var_export($_SERVER) . var_export($_REQUEST)); ?> +</pre> +Trenutno upravljam s sejami naslednjih uporabnikov: +<style> +table, tr, th, td { + border: 1px solid red; +} +</style> +<table> +<tr> +<th>uporabniško ime</th> +<th>čas zadnje uspešne osvežitve seje v UTC</th> +<th>želi elektronsko pošto</th> +</tr> +<?php +$ret = $db->query("select username, last, mail from users"); +foreach ($ret as $row) { + echo "<tr><td>" . htmlspecialchars($row["username"]) . "</td><td>{$row['last']}</td><td>" . ($row["mail"] ? "da" : "ne") . "</td>"; +} +?> diff --git a/prog/studisfri/studisfri b/prog/studisfri/studisfri new file mode 100644 index 0000000..76a628d --- /dev/null +++ b/prog/studisfri/studisfri @@ -0,0 +1,28 @@ +server { + include listen_http_internal; + server_name ~studisfri; + return 301 https://$host$request_uri; + port_in_redirect off; + server_name_in_redirect off; +} +server { + include listen_https_internal; + server_name ~studisfri; + location /Account/ { + default_type text/html; + add_header content-security-policy "script-src 'none'"; + include fastcgi.conf; + fastcgi_param SCRIPT_FILENAME /home/s/studisfri/studis_account.php; + # fastcgi_param SCRIPT_NAME /home/s/www/studis_account.php; + fastcgi_pass unix:/run/php/php-s.sock; + } + location / { + subs_filter studisfri.uni-lj.si $http_host ig; + subs_filter 'dropdown-menu-right">' "dropdown-menu-right\"><li><a href=/Account/nastavitve>dodatne nastavitve neuradnega posrednika</a>" ig; + subs_filter </title> '</title><script src=/Account/script.js></script>' ig; + proxy_pass https://studisfri.uni-lj.si; + proxy_set_header Host studisfri.uni-lj.si; + proxy_set_header Accept-Encoding ""; + proxy_ssl_server_name on; + } +} |