Aktualisiert am: 2023-12-06

SQL Injection

Enumeration

Challenge

  • Im Quellcode finden wir einen Kommentar, der uns auf die Beschaffenheit der Tabelle users hinweist. Dabei werden auch die Feldlängen definiert

     /*
     CREATE TABLE `users` (
       `username` varchar(64) DEFAULT NULL,
       `password` varchar(64) DEFAULT NULL
     );
     */
    
  • Jegliche SQL-Abfragen gehen durch mysql_real_escape_string, um SQL Injection zu verhindern

  • Jeglicher Output wird durch htmlentities geschickt, um XSS zu verhindern. Das ist aber ohnehin nicht unser Ziel

  • Wenn der User existiert, werden bei korrektem Kennwort die Zugangsdaten ausgegeben. Wenn der User nicht existiert, wird er erstellt

     if(validUser($link,$_REQUEST["username"])) {
         //user exists, check creds
         if(checkCredentials($link,$_REQUEST["username"],$_REQUEST["password"])){
             echo "Welcome " . htmlentities($_REQUEST["username"]) . "!<br>";
             echo "Here is your data:<br>";
             $data=dumpData($link,$_REQUEST["username"]);
             print htmlentities($data);
         }
         else{
             echo "Wrong password for user: " . htmlentities($_REQUEST["username"]) . "<br>";
         }
     }
     else {
         //user doesn't exist
         if(createUser($link,$_REQUEST["username"],$_REQUEST["password"])){
             echo "User " . htmlentities($_REQUEST["username"]) . " was created!";
         }
     }
    

Exploitation

  • Die Funktion mysql_real_escape_string war (ist?) unzuverlässig bei fehlerhafter Konfiguration des Encodings. Dieser Artikel von 2006 geht auf das Ausnutzen der Schwachstelle mittels Multibyte Encoding ein. Es wird dort auch darauf hingeweisen, dass man besser Prepared Statements verwendet, was der OWASP-Empfehlung entspricht.
  • Wie ich gelernt habe, definiert SQL im Standard ein unerwartetes Verhalten bei der Überprüfung auf Gleichheit (=) von Strings

    • Trailing Spaces werden bei einem SELECT Statement abgeschnitten
    • Trailing Spaces werden bei einem INSERT Statement berücksichtigt,
      • soweit dies die Feldlänge zulässt
  • SQL bietet uns hier einen Angriffsvektor, der sich mit einem besonders langen Benutzernamen ausnutzen lässt. Beachtet den Punkt am Ende – das kann auch ein anderes Zeichen wie ein Buchstabe oder eine Ziffer sein

     kali@kali:~$ python3 -c "print('natas28' + ' ' * 64 + '.')"
     natas28                                                                .
    
    1. Der Punkt (.) am Ende sorgt dafür, dass in validUser die SELECT-Abfrage fehlschlägt, gleichwohl der Benutzer natas28 bereits existiert. Entsprechend wird createUser aufgerufen
    2. Die 64 Spaces ( ) nach dem Benutzernamen sprengen die Feldlänge beim INSERT. Der Punkt am Ende wird abgeschnitten
    3. Ab nun geben SELECT-Abfragen auf natas28 mehrere Ergebnisse zurück
  • Mit dem überlangen Benutzernamen können wir uns einen Benutzer mit beliebigem Kennwort erstellen und uns damit als natas28 Anmelden, um die (bzw. alle) Zugangsdaten zu erhalten

     JWwR438wkgTsNKBbcJoowyysdM82YjeF
    

Best Practices

  • Definiere identifizierende Felder mit einem UNIQUE Constraint, um dadurch keine weiteren INSERT Statements zuzulassen, die sich bloss in der Anzahl an Trailing Spaces unterscheiden

Links