diff --git a/dvwa/css/main.css b/dvwa/css/main.css index 541c87892..77b095524 100644 --- a/dvwa/css/main.css +++ b/dvwa/css/main.css @@ -86,6 +86,22 @@ ul + ul, ul + ul.menuBlocks, ul + h1, ul + h2, ul + p { font-size: 13px; } +div.nearly { + border: 2px solid #0000ff; + padding: 10px 20px 10px 20px; + margin-top: 15px; + margin-bottom: 15px; +} + +div.success { + border: 2px solid #00ff00; + padding: 10px 20px 10px 20px; + text-align: center; + font-weight: bold; + margin-top: 15px; + margin-bottom: 15px; +} + div.warning { border: 2px solid #ff0000; padding: 10px 20px 10px 20px; diff --git a/dvwa/includes/dvwaPage.inc.php b/dvwa/includes/dvwaPage.inc.php index adc9b0d67..4f266abc5 100644 --- a/dvwa/includes/dvwaPage.inc.php +++ b/dvwa/includes/dvwaPage.inc.php @@ -305,6 +305,7 @@ function dvwaHtmlEcho( $pPage ) { $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'authbypass', 'name' => 'Authorisation Bypass', 'url' => 'vulnerabilities/authbypass/' ); } $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'open_redirect', 'name' => 'Open HTTP Redirect', 'url' => 'vulnerabilities/open_redirect/' ); + $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'encryption', 'name' => 'Cryptography', 'url' => 'vulnerabilities/cryptography/' ); } $menuBlocks[ 'meta' ] = array(); @@ -512,7 +513,7 @@ function dvwaSourceHtmlEcho( $pPage ) { // To be used on all external links -- function dvwaExternalLinkUrlGet( $pLink,$text=null ) { - if(is_null( $text )) { + if(is_null( $text ) || $text == "") { return '' . $pLink . ''; } else { diff --git a/vulnerabilities/cryptography/help/help.php b/vulnerabilities/cryptography/help/help.php new file mode 100644 index 000000000..f07846105 --- /dev/null +++ b/vulnerabilities/cryptography/help/help.php @@ -0,0 +1,193 @@ + + + +
+

Help - Cryptographic Problems

+ +
+ + + + +
+

About

+

+ Cryptography is key area of security and is used to keep secrets secret. When implemented badly these secrets can be leaked or the crypto manipulated to bypass protections. +

+

+ This module will look at three weaknesses, using encoding instead of encryption, using algorithms with known weaknesses, and padding oracle attacks. +

+ +


+ +

Objective

+

Each level has its own objective but the general idea is to exploit weak cryptographic implementations.

+ +


+ +

Low Level

+

The thing to notice is the mention of encoding rather than encryption, that should give you a hint about the vulnerability here.

+

+ +

+
+

Start by encoding a few messages and looking at the output, if you have spent any time around encoding standards you should be able to tell that it is in Base64. Could it be that simple? Try Base64 decoding some test strings to find out:

+
encode (hello) -> HwQPBBs=
+base64decode (HwQPBBs=) -> 0x1f 0x04 0x0f 0x04 0x1b
+
encode (a secret) -> FkEQDRcFChs=
+base64decode (FkEQDRcFChs=) -> 0x16 0x41 0x10 0x0d 0x17 0x05 0x0a 0x1b
+

+That failed, but what you might notice is that the number of output characters matches the number of input characters. Another common encoding method that is sometimes mistaken for encryption is XOR, this takes the clear text input and XORs each character with a key which is repeated or truncated to be the same length as the input.

+

+XOR is associative, this means that if you XOR the clear text with the key you get the cipher text and if you XOR the cipher text with the key you get the clear text, what it also means is if you XOR the clear text with the cipher text, you get the key. Let's try this with our examples: +

+
encode (hello) -> HwQPBBs=
+xor (HwQPBBs=, hello) -> wacht
+

+This looks promising, let's try the second example: +

+
encode (a secret) -> FkEQDRcFChs=
+xor (FkEQDRcFChs=, a secret) -> wachtwoo
+ +

+There is no repetition in the key yet so let's try with a longer string. +

+ +
encode (thisisaverylongstringtofindthepassword) -> AwkKGx0EDhkXFg4NDAYTBBsdGwoQFQwOHRkLGxoBBwAQGwMYHQs=
+xor (thisisaverylongstringtofindthepassword, base64decode (AwkKGx0EDhkXFg4NDAYTBBsdGwoQFQwOHRkLGxoBBwAQGwMYHQs=)) -> wachtwoordwachtwoordwachtwoordwachtwoo
+ +

+It looks like we have found our key "wachtwoord". Let's give it a try on our challenge string: +

+ +
xor (base64decode(Lg4WGlQZChhSFBYSEB8bBQtPGxdNQSwEHREOAQY=), wachtwoord) -> Your new password is: Olifant
+ + +

+And there we have it, the message we are looking for and the password we need to login. +

+ +

Another lesson here, do not assume that the messages or the underlying system you are working with is in English. The key "wachtwoord" is Dutch for password.

+
+ +

Medium Level

+

The tokens are encrypted using an Electronic Code Book based algorithm (aes-128-ecb). In this mode, the clear text is broken down into fixed sized blocks and each block is encrypted independently of the rest. This results in a cipher text that is made up from a number of individual blocks with no way to tie them together. Worse than this, any two blocks, from any two clear text inputs, are interchangeable as long as they have been encrypted with the same key. In our example, this means you can take blocks from the three different tokens to make your own token.

+

+ +

+
+

+ How do you know the block size? This is given in the algorithm name. aes-128-ebc is a 128 bit block cipher. 128 bits is 16 bytes, but to make things human readable, the bytes are represented as hex characters meaning each byte is two characters. This gives you a block size of 32 characters. Sooty's token is 192 characters long, 192 / 32 = 6 and so Sooty's token has six code blocks. +

+ +

+Let's start by breaking the tokens down into blocks.

+

Sooty:

+
e287af752ed3f9601befd45726785bd9
+b85bb230876912bf3c66e50758b222d0
+837d1e6b16bfae07b776feb7afe57630
+5aec34b41499579d3fb6acc8dc92fd5f
+cea8743c3b2904de83944d6b19733cdb
+48dd16048ed89967c250ab7f00629dba
+

+ +

Sweep:

+
3061837c4f9debaf19d4539bfa0074c1
+b85bb230876912bf3c66e50758b222d0
+83f2d277d9e5fb9a951e74bee57c77a3
+caeb574f10f349ed839fbfd223903368
+873580b2e3e494ace1e9e8035f0e7e07
+ +

Soo:

+
5fec0b1c993f46c8bad8a5c8d9bb9698
+174d4b2659239bbc50646e14a70becef
+83f2d277d9e5fb9a951e74bee57c77a3
+c9acb1f268c06c5e760a9d728e081fab
+65e83b9f97e65cb7c7c4b8427bd44abc
+16daa00fd8cd0105c97449185be77ef5
+ +

+ Each token has broken down nicely into blocks so we are on the right track. +

+

+ If you look carefully at the blocks you will see that there are some that repeat over the different tokens, this means that the same clear text has been encrypted to create the block. If we look at the description we can try to map these to the JSON object. +

+

+ Taking Sooty as an example: +

+

Sooty:

+
e287af752ed3f9601befd45726785bd9 <- Username
+b85bb230876912bf3c66e50758b222d0 <- Expiry
+837d1e6b16bfae07b776feb7afe57630 <- Level
+5aec34b41499579d3fb6acc8dc92fd5f <- Bio
+cea8743c3b2904de83944d6b19733cdb
+48dd16048ed89967c250ab7f00629dba
+

+

+ Assuming we are right with our mappings, if you compare the blocks that match you can see that Sooty and Sweep both have the same expiry block (b85bb230876912bf3c66e50758b222d0) and both Sweep and Soo have the same level block (83f2d277d9e5fb9a951e74bee57c77a3). This matches with what we know about the tokens as both Sooty and Sweep have expired tokens and both Sweep and Soo are users, not admins. +

+

+ Knowing all this, we can now create our forged session token. We need to take the username block from Sweep, the expiry block from Soo and the level block from Sooty. We can then finish the token off with the remaining blocks from any of the tokens. This gives us: +

+
3061837c4f9debaf19d4539bfa0074c1 <- Sweep as username
+174d4b2659239bbc50646e14a70becef <- Soo's expiry time
+837d1e6b16bfae07b776feb7afe57630 <- Sooty's admin privileges
+caeb574f10f349ed839fbfd223903368 <- Finish off with Sweep's bio
+873580b2e3e494ace1e9e8035f0e7e07
+

+ Which gives us... +

+

+ +

+

+ This is a very contrived setup with the tokens tweaked to force blocks to map to the JSON object so manipulation is easier to do, in the real world it is unlikely to be this easy however as data is often formed from fixed sized blocks overlaps can happen in a way that mixing blocks up results in valid data. Sometimes just being able to pass invalid data is enough so all that is needed is to swap blocks in a way that they can be decrypted and then passed on to the rest of the system where they will cause errors. +

+

+ If you want to play with this some more, there is a script called ecb_attack.php in the sources directory which shows how the tokens were generated and lets you combine them in different ways to create custom tokens. +

+
+ +

High Level

+

The system is using AES-128-CBC which means it is vulnerable to a padding oracle attack.

+ +

+ +

+ +
+

Rather than try to explain this here, go read this excelent write up on the attack by Eli Sohl.

+

Cryptopals: Exploiting CBC Padding Oracles

+

+ If you want to play with this some more, there is a script called oracle_attack.php in the sources directory which runs through the full attack with debug. You can run this either against the DVWA site or it will run locally against its own pretend web server. +

+
+ +

Impossible Level

+

You can never say impossible in crypto as something that would take years today could take minutes in the future when a new attack is found or when processing power takes a giant leam forward.

+

+ The current recommended alternative to AES-CBC is AES-GCM and so the system uses that here. 256 bit blocks rather than 128 bit blocks are used, and a unique IV used for every message. This may be secure today but who knows what tomorrow brings? +

+
+ +
+ +
diff --git a/vulnerabilities/cryptography/index.php b/vulnerabilities/cryptography/index.php new file mode 100644 index 000000000..97c7587b4 --- /dev/null +++ b/vulnerabilities/cryptography/index.php @@ -0,0 +1,64 @@ + +

Vulnerability: Cryptography Problems

+ +
+"; + +$page[ 'body' ] .= " + {$html} +
+ +

More Information

+ +\n"; + +dvwaHtmlEcho( $page ); + +?> + diff --git a/vulnerabilities/cryptography/source/check_token_high.php b/vulnerabilities/cryptography/source/check_token_high.php new file mode 100644 index 000000000..1c2ce95aa --- /dev/null +++ b/vulnerabilities/cryptography/source/check_token_high.php @@ -0,0 +1,25 @@ + 527, + "message" => "Content type must be application/json" + )); + } else { + $token = $jsonData = file_get_contents('php://input'); + $ret = check_token ($token); + } +} else { + $ret = json_encode (array ( + "status" => 405, + "message" => "Method not supported" + )); +} + +print $ret; +exit; diff --git a/vulnerabilities/cryptography/source/check_token_impossible.php b/vulnerabilities/cryptography/source/check_token_impossible.php new file mode 100644 index 000000000..cba0ef246 --- /dev/null +++ b/vulnerabilities/cryptography/source/check_token_impossible.php @@ -0,0 +1,25 @@ + 527, + "message" => "Content type must be application/json" + )); + } else { + $token = $jsonData = file_get_contents('php://input'); + $ret = check_token ($token); + } +} else { + $ret = json_encode (array ( + "status" => 405, + "message" => "Method not supported" + )); +} + +print $ret; +exit; diff --git a/vulnerabilities/cryptography/source/download_ecb_attack.php b/vulnerabilities/cryptography/source/download_ecb_attack.php new file mode 100644 index 000000000..5d28b5313 --- /dev/null +++ b/vulnerabilities/cryptography/source/download_ecb_attack.php @@ -0,0 +1,16 @@ + diff --git a/vulnerabilities/cryptography/source/download_oracle_attack.php b/vulnerabilities/cryptography/source/download_oracle_attack.php new file mode 100644 index 000000000..25740da1a --- /dev/null +++ b/vulnerabilities/cryptography/source/download_oracle_attack.php @@ -0,0 +1,16 @@ + diff --git a/vulnerabilities/cryptography/source/ecb_attack.php b/vulnerabilities/cryptography/source/ecb_attack.php new file mode 100644 index 000000000..4a245b0fb --- /dev/null +++ b/vulnerabilities/cryptography/source/ecb_attack.php @@ -0,0 +1,98 @@ +user == "sweep" && $user->ex > time() && $user->level == "admin") { + print "Welcome administrator Sweep\n"; +} else { + print "Failed\n"; +} + +?> diff --git a/vulnerabilities/cryptography/source/high.php b/vulnerabilities/cryptography/source/high.php new file mode 100644 index 000000000..f1cbe341d --- /dev/null +++ b/vulnerabilities/cryptography/source/high.php @@ -0,0 +1,70 @@ + + function send_token() { + + const url = 'source/check_token_high.php'; + const data = document.getElementById ('token').value; + + console.log (data); + + fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: data + }) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + console.log(data); + message_line = document.getElementById ('message'); + if (data.status == 200) { + message_line.innerText = 'Welcome back ' + data.user + ' (' + data.level + ')'; + message_line.setAttribute('class', 'success'); + } else { + message_line.innerText = 'Error: ' + data.message; + message_line.setAttribute('class', 'warning'); + } + }) + .catch(error => { + console.error('There was a problem with your fetch operation:', error); + }); + + } + +

+ You have managed to steal the following token from a user of the Prognostication application. +

+

+ +

+

+ You can use the form below to provide the token to access the system. You have two challenges, first, decrypt the token to find out the secret it contains, and then create a new token to access the system as a other users. See if you can make yourself an administrator. +

+
+
+
+

+

+

+ +

+
+"; + +?> diff --git a/vulnerabilities/cryptography/source/impossible.php b/vulnerabilities/cryptography/source/impossible.php new file mode 100644 index 000000000..6e5342d15 --- /dev/null +++ b/vulnerabilities/cryptography/source/impossible.php @@ -0,0 +1,70 @@ + + function send_token() { + + const url = 'source/check_token_impossible.php'; + const data = document.getElementById ('token').value; + + console.log (data); + + fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: data + }) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + console.log(data); + message_line = document.getElementById ('message'); + if (data.status == 200) { + message_line.innerText = 'Welcome back ' + data.user + ' (' + data.level + ')'; + message_line.setAttribute('class', 'success'); + } else { + message_line.innerText = 'Error: ' + data.message; + message_line.setAttribute('class', 'warning'); + } + }) + .catch(error => { + console.error('There was a problem with your fetch operation:', error); + }); + + } + +

+ You have managed to steal the following token from a user of the Impervious application. +

+

+ +

+

+ This being the impossible level, you should not be able to mess with the token in any useful way but feel free to try below. +

+
+
+
+

+

+

+ +

+
+"; + +?> diff --git a/vulnerabilities/cryptography/source/low.php b/vulnerabilities/cryptography/source/low.php new file mode 100644 index 000000000..235ed19d3 --- /dev/null +++ b/vulnerabilities/cryptography/source/low.php @@ -0,0 +1,112 @@ +getMessage(); + } +} + +$html = " +

+ This super secure system will allow you to exchange messages with your friends without anyone else being able to read them. Use the box below to encode and decode messages. +

+
+

+

+

+ or + +

+

+ +

+
+"; + +if (!is_null ($encoded)) { + $html .= " +

+

"; +} + +$html .= " +
+

+ You have intercepted the following message, decode it and log in below. +

+

+ +

+"; + +if ($errors != "") { + $html .= '
' . $errors . '
'; +} + +if ($messages != "") { + $html .= '
' . $messages . '
'; +} + +if ($success != "") { + $html .= '
' . $success . '
'; +} + +$html .= " +
+

+

+

+ +

+
+"; +?> diff --git a/vulnerabilities/cryptography/source/medium.php b/vulnerabilities/cryptography/source/medium.php new file mode 100644 index 000000000..b07efcc39 --- /dev/null +++ b/vulnerabilities/cryptography/source/medium.php @@ -0,0 +1,110 @@ +user == "sweep" && $user->ex > time() && $user->level == "admin") { + $success = "Welcome administrator Sweep"; + } else { + $messages = "Login successful but not as the right user."; + } + } + } + } catch(Exception $e) { + $errors = $e->getMessage(); + } +} + +$html = " +

+ You have managed to get hold of three session tokens for an application you think is using poor cryptography to protect its secrets: +

+

+ Sooty (admin), session expired +

+

+ +

+

+ Sweep (user), session expired +

+

+ +

+

+ Soo (user), session valid +

+

+ +

+

+ Based on the documentation, you know the format of the token is: +

+
{
+    \"user\": \"example\",
+    \"ex\": 1723620372,
+    \"level\": \"user\",
+    \"bio\": \"blah\"
+}
+

+You also spot this comment in the docs: +

+
+To ensure your security, we use aes-128-ecb throughout our application. +
+ +
+

+ Manipulate the session tokens you have captured to log in as Sweep with admin privileges. +"; + +if ($errors != "") { + $html .= '

' . $errors . '
'; +} + +if ($messages != "") { + $html .= '
' . $messages . '
'; +} + +if ($success != "") { + $html .= '
' . $success . '
'; +} + +$html .= " +
+

+

+

+ +

+
+"; +?> diff --git a/vulnerabilities/cryptography/source/oracle_attack.php b/vulnerabilities/cryptography/source/oracle_attack.php new file mode 100644 index 000000000..3ecb454b3 --- /dev/null +++ b/vulnerabilities/cryptography/source/oracle_attack.php @@ -0,0 +1,318 @@ + $token, + "iv" => $iv_string_b64 + ); + + if (is_null ($url)) { + $body = check_token (json_encode ($data)); + } else { + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json', 'Accept:application/json')); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode ($data)); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HEADER, true); + + $response = curl_exec($ch); + + $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $header = substr($response, 0, $header_size); + $body = substr($response, $header_size); + + // May return false or something that evaluates to false + // so can't do strict type check + if ($response == false) { + print "Could not access remote server, is the URL correct? +${url} +"; + exit; + } + if (strpos ($header, "200 OK") === false) { + print "Check token script not found, have you got the right URL? +${url} +"; + exit; + } + + curl_close($ch); + } + + return json_decode ($body, true); +} + +function do_attack ($iv_string_b64, $token, $url) { + $iv_string = base64_decode ($iv_string_b64); + $temp_init_iv = unpack('C*', $iv_string); + + # The unpack creates an array starting a 1, the + # rest of this code assumes an array starting at 0 + # so calling array_values changes the array 0 based + + $init_iv = array_values ($temp_init_iv); + + print "Trying to decrypt\n"; + print "\n"; + + $iv = zero_array(16); + $zeroing = zero_array(16); + + + for ($padding = 1; $padding <= 16; $padding++) { + $offset = 16 - $padding; + print ("Looking at offset $offset for padding $padding\n"); + for ($i = 0; $i <= 0xff; $i++) { + $iv[$offset] = $i; + for ($k = $offset + 1; $k < 16; $k++) { + $iv[$k] = $zeroing[$k] ^ $padding; + } + try { + $obj = make_call ($token, $iv, $url); + + # 526 is decryption failed + if ($obj['status'] != 526) { + print "Got hit for: " . $i . "\n"; + + # Only get here if the decrypt works correctly + + /* + + Check for edge case on offset 15 (right most byte). + The decrypted data could look like this: + + 0x44 ... 0x02 0x02 + ^^^^ Real last byte of value 2 byte padding + + In this situation, if we happen to land on a value + that sets the last byte to 0x02 then that will + look like valid padding as it will make the data end + in 0x02 0x02 as it already does: + + 0x44 ... 0x02 0x02 + ^^^^ Fluke, we want 0x01 for 1 byte padding + + This is what we want: + + 0x44 ... 0x02 0x01 + ^^^^ Valid 1 byte padding + + To do this, change the IV value for offset 14 which will + change the second to last byte and make the call again. + If we were in the edge case we would now have: + + 0x44 ... 0xf3 0x02 + ^^^^ No longer valid padding + + This is no longer valid padding so it will fail and we can + continue looking till we find the value that gives us + valid 1 byte padding. + + */ + + // Used by the edge case check + + $ignore = false; + if ($offset == 15) { + print "Got a valid decrypt for offset 15, checking edge case\n"; + $temp_iv = $iv; + $temp_iv[14] = 0xff; + $temp_d_obj = make_call ($token, $temp_iv, $url); + if ($temp_d_obj['status'] != 526) { + print "Not edge case, can continue\n"; + } else { + print "Edge case, do not continue\n"; + $ignore = true; + } + } + + if (!$ignore) { + print "There was a match\n"; + $zeroing[$offset] = $i ^ $padding; + # print "IV: " . byte_array_to_string ($iv) . "\n"; + # print "Zero: " . byte_array_to_string ($zeroing) . "\n"; + break; + } + } + } catch(Exception $exp) { + # print "Fail\n"; + # var_dump ($e); + } + } + } + + print "\n"; + print "Finished looping\n"; + print "\n"; + + print "Derived IV is: " . byte_array_to_string ($iv) . "\n"; + + # If you want to check this, it should be all 16 to show it is all padding + # $x = xor_byte_array ($iv, $zeroing); + # print "Derived IV XOR and zeroing string: " . byte_array_to_string ($x) . "\n"; + + print "Real IV is: " . byte_array_to_string ($init_iv) . "\n"; + print "Zeroing array is: " . byte_array_to_string ($zeroing) . "\n"; + print "\n"; + + $x = xor_byte_array ($init_iv, $zeroing); + print "Decrypted string with padding: " . byte_array_to_string ($x) . "\n"; + $number_of_padding_bytes = $x[15]; + $without_padding = array_slice ($x, 0, 16 - $number_of_padding_bytes); + print "Decrypted string without padding: " . byte_array_to_string ($without_padding) . "\n"; + + $str = ''; + for ($i = 0; $i < count ($without_padding); $i++) { + $c = $without_padding[$i]; + if ($c > 0x19 && $c < 0x7f) { + $str .= chr($c); + } else { + $str .= "0x" . sprintf ("%02x", $c) . " "; + } + } + + print "Decrypted string as text: " . $str . "\n"; + + /* + Trying to modify decrypted data by playing with the zeroing array. + */ + + print "\n"; + print "Trying to modify string\n"; + print "\n"; + + $new_clear = "userid:1"; + print "New clear text: " . $new_clear . "\n"; + + for ($i = 0; $i < strlen($new_clear); $i++) { + $zeroing[$i] = $zeroing[$i] ^ ord($new_clear[$i]); + } + $padding = 16 - strlen($new_clear); + $offset = 16 - $padding; + for ($i = $offset; $i < 16; $i++) { + $zeroing[$i] = $zeroing[$i] ^ $padding; + } + + print "New IV is: " . byte_array_to_string ($zeroing) . "\n"; + print "\n"; + + print "Sending new data to server...\n"; + print "\n"; + + try { + $ret_obj = make_call ($token, $zeroing, $url); + + print "Response from server:\n"; + var_dump ($ret_obj); + + if ($ret_obj['status'] == 200 && $ret_obj['level'] == "admin") { + print "\n"; + print "Hack success!\n\n"; + print "The new token is:\n"; + + # This maps the IV byte array down to a string + $iv_string = implode(array_map("chr", $zeroing)); + + # Now base64 encode it so it is safe to send + $iv_string_b64 = base64_encode ($iv_string); + + $new_token = array ( + "token" => $token, + "iv" => $iv_string_b64 + ); + print json_encode ($new_token); + print "\n\n"; + } else { + print "Hack failed\n"; + } + } catch (Exception $exp) { + print "Hack failed, system could not decrypt message\n"; + var_dump ($exp); + } +} + +$shortopts = ""; +$shortopts .= "h"; // Help + +$longopts = array( +"url:", // Required value +"iv:", // Required value +"token:", // Required value +"local", // No value +"help", // No value +); +$options = getopt($shortopts, $longopts); + +if (array_key_exists ("h", $options) || array_key_exists ("help", $options)) { + print "This script can either test against a local decryptor or a remote.\n +To test locally, pass --local, otherwise pass the IV, token and URL for the remote system. + +--local - Test locally +--iv - IV from remote system +--token - Token from remote system +--url - URL for the check function +-h, --help - help + +"; + exit; +} elseif (array_key_exists ("l", $options) || array_key_exists ("local", $options)) { + print "Creating the token locally\n\n"; + + $token_data = json_decode (create_token(true), true); + + $token = $token_data['token']; + $iv = $token_data['iv']; + $url = null; +} elseif (array_key_exists ("iv", $options) && + array_key_exists ("token", $options) && + array_key_exists ("url", $options)) { + print "Attacking remote server using parameters provided\n\n"; + + $token = $options['token']; + $iv = $options['iv']; + $url = $options['url']; +} else { + print "Either specify --local or provide the IV, token and URL\n\n"; + exit; +} + +do_attack ($iv, $token, $url); diff --git a/vulnerabilities/cryptography/source/token_library_high.php b/vulnerabilities/cryptography/source/token_library_high.php new file mode 100644 index 000000000..29c93559c --- /dev/null +++ b/vulnerabilities/cryptography/source/token_library_high.php @@ -0,0 +1,132 @@ + base64_encode ($e), + "iv" => base64_encode (IV) + ); + return json_encode($data); +} + +function check_token ($data) { + $users = array (); + $users[1] = array ("name" => "Geoffery", "level" => "admin"); + $users[2] = array ("name" => "Bungle", "level" => "user"); + $users[3] = array ("name" => "Zippy", "level" => "user"); + $users[4] = array ("name" => "George", "level" => "user"); + + $data_array = false; + try { + $data_array = json_decode ($data, true); + } catch (TypeError $exp) { + $ret = array ( + "status" => 521, + "message" => "Data not in JSON format", + "extra" => $exp->getMessage() + ); + } + + if (is_null ($data_array)) { + $ret = array ( + "status" => 522, + "message" => "Data in wrong format" + ); + } else { + if (!array_key_exists ("token", $data_array)) { + $ret = array ( + "status" => 523, + "message" => "Missing token" + ); + return json_encode ($ret); + } + if (!array_key_exists ("iv", $data_array)) { + $ret = array ( + "status" => 524, + "message" => "Missing IV" + ); + return json_encode ($ret); + } + + $ciphertext = base64_decode ($data_array['token']); + $iv = base64_decode ($data_array['iv']); + + # Asssume failure + $ret = array ( + "status" => 500, + "message" => "Unknown error" + ); + try { + $d = decrypt ($ciphertext, $iv); + if (preg_match ("/^userid:(\d+)$/", $d, $matches)) { + $id = $matches[1]; + if (array_key_exists ($id, $users)) { + $user = $users[$id]; + $ret = array ( + "status" => 200, + "user" => $user["name"], + "level" => $user['level'] + ); + } else { + $ret = array ( + "status" => 525, + "message" => "User not found" + ); + } + } else { + $ret = array ( + "status" => 527, + "message" => "No user specified" + ); + } + } catch (Exception $exp) { + $ret = array ( + "status" => 526, + "message" => "Unable to decrypt token", + "extra" => $exp->getMessage() + ); + } + } + return json_encode ($ret); +} diff --git a/vulnerabilities/cryptography/source/token_library_impossible.php b/vulnerabilities/cryptography/source/token_library_impossible.php new file mode 100644 index 000000000..4f31a6a00 --- /dev/null +++ b/vulnerabilities/cryptography/source/token_library_impossible.php @@ -0,0 +1,130 @@ + base64_encode ($e), + "iv" => base64_encode ($iv), + ); + return json_encode($data); +} + +function check_token ($data) { + $users = array (); + $users[1] = array ("name" => "Geoffery", "level" => "admin"); + $users[2] = array ("name" => "Bungle", "level" => "user"); + $users[3] = array ("name" => "Zippy", "level" => "user"); + $users[4] = array ("name" => "George", "level" => "user"); + + $data_array = false; + try { + $data_array = json_decode ($data, true); + } catch (TypeError $exp) { + $ret = array ( + "status" => 521, + "message" => "Data not in JSON format", + "extra" => $exp->getMessage() + ); + } + + if (is_null ($data_array)) { + $ret = array ( + "status" => 522, + "message" => "Data in wrong format" + ); + } else { + if (!array_key_exists ("token", $data_array)) { + $ret = array ( + "status" => 523, + "message" => "Missing token" + ); + return json_encode ($ret); + } + if (!array_key_exists ("iv", $data_array)) { + $ret = array ( + "status" => 524, + "message" => "Missing IV" + ); + return json_encode ($ret); + } + + $ciphertext = base64_decode ($data_array['token']); + $iv = base64_decode ($data_array['iv']); + + # Asssume failure + $ret = array ( + "status" => 500, + "message" => "Unknown error" + ); + try { + $d = decrypt ($ciphertext, $iv); + if (preg_match ("/^userid:(\d+)$/", $d, $matches)) { + $id = $matches[1]; + if (array_key_exists ($id, $users)) { + $user = $users[$id]; + $ret = array ( + "status" => 200, + "user" => $user["name"], + "level" => $user['level'] + ); + } else { + $ret = array ( + "status" => 525, + "message" => "User not found" + ); + } + } else { + $ret = array ( + "status" => 527, + "message" => "No user specified" + ); + } + } catch (Exception $exp) { + $ret = array ( + "status" => 526, + "message" => "Unable to decrypt token", + "extra" => $exp->getMessage() + ); + } + } + return json_encode ($ret); +} diff --git a/vulnerabilities/cryptography/source/xor_theory.php b/vulnerabilities/cryptography/source/xor_theory.php new file mode 100644 index 000000000..ad04c2ad4 --- /dev/null +++ b/vulnerabilities/cryptography/source/xor_theory.php @@ -0,0 +1,36 @@ +'; // For debugging + } + } + return $outText; +} + +$clear = "hello world, what a great day"; +$key = "wachtwoord"; + +print "Clear text\n" . $clear . "\n"; +print "\n"; + +$encoded = (xor_this($clear, $key)); +$b64_encoded = base64_encode ($encoded); +print "Encoded text\n"; +var_dump ($b64_encoded); +print "\n"; + +$b64_decoded = base64_decode ($b64_encoded); +$decoded = xor_this($b64_decoded, $key); +print "Decoded text\n"; +var_dump ($decoded); +print "\n"; + +?>