Damit unser Programm G05 AES Padding Oracle möglichst realitätsnah arbeitet habe ich einen Mini-Webserver erstellt, welcher auf eine Mindestfunktionalität geschrumpft ist. Hier wird ein Login-Vorgang für einen Internetservice simuliert, bei dem der Benutzer das Passwort in eine Feld eingibt und dieses dann an den Webserver weiterreicht. Der Webserver muss natürlich den Schlüssel zur Verschlüsselung kennen und über eine Datenbank (in der z.B. die Hashes der Passwörter hinterlegt sind) eine Freigabe des Services ermöglichen.
Dieser Webserver gibt nur 2 Statusmeldungen zurück – entweder ein „HTTP/1.1 200 OK“ bei einem korrekten Padding oder ein „HTTP/1.1 500 Invalid Padding“ bei einem falschen Padding. Obwohl wir nun umständliche Aufrufe über das HTTP-Protokoll vornehmen, wird das verschlüsselte Passwort innerhalb weniger Sekunden ermittelt.
Selbstverständlich schmücke ich mich hier nicht mit fremden Federn, die Basis des Webservers entstammt dem Internet-Artikel „Ein Webserver in 150 Zeilen“ von Anders Moeller und der Artikel ist unter diesem Link zu finden: http://cs.au.dk/~amoeller/WWW/javaweb/server.html.
Beim Start des Servers werdet Ihr vermutlich von Eurer (Windows-) Firewall einen Warnhinweis erhalten – bitte bejaht die Frage nach einer erlaubten Kommunikation, sonst klappt es nicht mit der Entschlüsselung :-).
Hier ist nun der Sourcecode:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
package net.bplaced.javacrypto.unsecure; /* * Herkunft/Origin: http://javacrypto.bplaced.net/ * Programmierer/Programmer: Michael Fehr * Copyright/Copyright: frei verwendbares Programm (Public Domain) * Copyright: This is free and unencumbered software released into the public domain. * Lizenztext/Licence: <http://unlicense.org> * getestet mit/tested with: Java Runtime Environment 8 Update 191 x64 * getestet mit/tested with: Java Runtime Environment 11.0.1 x64 * Datum/Date (dd.mm.jjjj): 09.10.2019 * Projekt/Project: G05 AES Padding Orakel / G05 AES Padding Oracle * Funktion: zeigt die Unsicherheit durch das Padding Orakel * Function: shows the unsecurement through the padding oracle * * Sicherheitshinweis/Security notice * Die Programmroutinen dienen nur der Darstellung und haben keinen Anspruch auf eine korrekte Funktion, * insbesondere mit Blick auf die Sicherheit ! * Prüfen Sie die Sicherheit bevor das Programm in der echten Welt eingesetzt wird. * The program routines just show the function but please be aware of the security part - * check yourself before using in the real world ! * * Das Projekt basiert auf dem nachfolgenden Artikel: * The project is based on this article: * https://blog.skullsecurity.org/2013/a-padding-oracle-example * * Der Server basiert auf dem "Webserver in 150 Zeilen" von Anders Moeller * The Webserver is based on the "Webserver in 150 lines" from Anders Moeller * source: http://cs.au.dk/~amoeller/WWW/javaweb/server.html * * Aufruf im Browser / usage in a browser: * http://localhost:8079/|Base64 dekodierter String> * falsches Padding / wrong padding: * http://localhost:8079/AAAAAAAAAAAAAAAAAAAAAAOhCkLI3iLM5pYmWhdB_FM= * richtiges Padding / correct padding: * http://localhost:8079/JNB_IdTCPtD6ijpGC13gT5fl1wp9lAeafsGQO4AP0cE= */ import java.net.*; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.io.*; import java.util.*; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class G05_AesPaddingOracleWebserver { public static void main(String[] args) { System.out.println("G05 AES CBC Padding Oracle Webserver gestartet"); int portInt = 8079; // gegebenenfalls individuell ändern boolean quietBoolean = true; // auf false ändern gibt statusmeldungen auf die konsole aus // webserver starten ServerSocket socket = null; try { socket = new ServerSocket(portInt); } catch (IOException e) { System.err.println("Kann den Server nicht starten: " + e); System.exit(-1); } // benutzungshinweise des servers System.out.println("Der Server antwortet auf Verbindungen an Port: " + portInt); System.out.println("Aufruf des Servers: http://localhost:" + portInt + "/ <Base64 dekodiertes Cookie"); System.out.println("Beispielaufruf : http://localhost:8079/AAAAAAAAAAAAAAAAAAAAANgYNOrTB3i6R-yyVv9f8XU="); System.out.println("Antworten des Servers abhängig vom Ergebnis des AES Padding Oracles:"); System.out.println("Ungültiges Padding gefunden: HTTP/1.1 500 Invalid Padding"); System.out.println("Gültiges Padding : HTTP/1.1 200 OK"); // schleife für anfragen an den server while (true) { Socket connection = null; try { // warten auf die anfrage connection = socket.accept(); BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); OutputStream out = new BufferedOutputStream(connection.getOutputStream()); PrintStream pout = new PrintStream(out); // nur die erste zeile der anfrage lesen - den rest ignorieren String request = in.readLine(); if (quietBoolean == false) { System.out.println("request:" + request); } if (request == null) continue; if (quietBoolean == false) { log(connection, request); } while (true) { String misc = in.readLine(); if (misc == null || misc.length() == 0) break; } // die eingabezeile wird analysiert if (!request.startsWith("GET") || request.length() < 14 || !(request.endsWith("HTTP/1.0") || request.endsWith("HTTP/1.1"))) { if (quietBoolean == false) { System.out.println("Bad request"); } } else { String req = request.substring(4, request.length() - 9).trim(); if (req.indexOf("..") != -1 || req.indexOf("/.ht") != -1 || req.endsWith("~")) { if (quietBoolean == false) { System.out.println("Bad request"); } } else { String inputString = req.substring(1); if (inputString.length() > 12) { // die anfrage wird an prepareInput übergeben // die rückgabewerte sind 0 [Padding in Ordnung] // bzw. 1 [falsches Padding] int paddingStatus = prepareInput(inputString); if (paddingStatus == 0) { if (quietBoolean == false) { System.out.println("Der paddingStatus ist 0"); } pout.print("HTTP/1.1 200 OK\r\n" + paddingStatus); if (quietBoolean == false) { log(connection, "200 OK"); } } if (paddingStatus == 1) { if (quietBoolean == false) { System.out.println("Der paddingStatus ist 1"); } pout.print("HTTP/1.1 500 Invalid Padding\r\n" + paddingStatus); if (quietBoolean == false) { log(connection, "500 Invalid Padding"); } } } } } out.flush(); } catch (IOException e) { System.err.println(e); } try { if (connection != null) connection.close(); } catch (IOException e) { System.err.println(e); } } } private static void log(Socket connection, String msg) { System.err.println(new Date() + " [" + connection.getInetAddress().getHostAddress() + ":" + connection.getPort() + "] " + msg); } private static int prepareInput(String inputString) { // zuerueckverwandlung in ein byte array Base64.Decoder decURL = Base64.getUrlDecoder(); byte[] output = decURL.decode(inputString.getBytes()); // aufteilung in iv = 16 byte und ct = restliche bytes 16..32..48.. byte[] iv = null; byte[] ct = null; iv = Arrays.copyOfRange(output, 0, 16); ct = Arrays.copyOfRange(output, 16, output.length); int AesCbcPadDecryptStatusBIKint = AesCbcPadDecryptStatusBIK(iv, ct); return AesCbcPadDecryptStatusBIKint; } public static int AesCbcPadDecryptStatusBIK(byte[] initvectorByte, byte[] ciphertextByte) { // der server kennt den schlüssel zur entschlüsselung // diese routine entschlüsselt nicht, sondern gibt nur eine // information zum Padding-Status aus byte[] key = "MyAesKey12345678".getBytes(); int status = 0; // 0=decryption, 1=bad padding, 2=general error Cipher AesCipher; try { AesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); IvParameterSpec ivParameterSpec = new IvParameterSpec(initvectorByte); AesCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); byte[] decryptByte = AesCipher.doFinal(ciphertextByte); // decryptByte wird nicht weiter benutzt } catch (IllegalBlockSizeException e) { e.printStackTrace(); status = 2; } catch (BadPaddingException e) { // e.printStackTrace(); status = 1; } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { e.printStackTrace(); status = 2; } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { e.printStackTrace(); status = 2; } return status; } } |
Die Ausgabe auf der Konsole ist „servertypisch“ kurz und knapp – solltet Ihr mehr Informationen in der Konsole wünschen ändert einfach in Zeile 56 den Schalter von „true“ in „false“:
1 2 3 4 5 6 7 |
G05 AES CBC Padding Oracle Webserver gestartet Der Server antwortet auf Verbindungen an Port: 8079 Aufruf des Servers: http://localhost:8079/ <Base64 dekodiertes Cookie Beispielaufruf : http://localhost:8079/AAAAAAAAAAAAAAAAAAAAANgYNOrTB3i6R-yyVv9f8XU= Antworten des Servers abhängig vom Ergebnis des AES Padding Oracles: Ungültiges Padding gefunden: HTTP/1.1 500 Invalid Padding Gültiges Padding : HTTP/1.1 200 OK |
Alle Quellcodes zu den Unsicherheiten findet Ihr zum Download in meinem Github-Repository, welches Ihr über diesen Link erreicht: https://github.com/java-crypto/G-Unsicherheit. Alle Programme sind sowohl unter Java 8 als auch unter Java 11 lauffähig.
Die Lizenz zum obigen Beispiel findet Ihr auf der eigenen Lizenz-Seite.
Letzte Aktualisierung: 09.10.2019