Skip to content

Instantly share code, notes, and snippets.

@AshishMahto
Last active April 7, 2021 21:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AshishMahto/24569356b9f4d83bac6fb10b7fb9f607 to your computer and use it in GitHub Desktop.
Save AshishMahto/24569356b9f4d83bac6fb10b7fb9f607 to your computer and use it in GitHub Desktop.
Tenable CTF 2021: Netrunner Encryption (Crypto)

http://167.71.246.232:8080/netrun.txt

Scroll down for netrun.php, the source code given for the challenge.


The front end is simply a little form where you get to enter some plaintext and recieve ciphertext after a 128-bit AES-ECB encode.

The catch: the flag, along with some bytes of additional garbage, are added onto your input plaintext as (right) padding.


You gotta know your cryptography jargon to get a good foothold for this one. Googling "chosen plaintext attack aes ecb" got me to
https://crypto.stackexchange.com/questions/42891/chosen-plaintext-attack-on-aes-in-ecb-mode
and eventually
https://cedricvanrompay.gitlab.io/cryptopals/challenges/09-to-13.html#Challenge-12---Byte-at-a-time-ECB-decryption-(Simple)
which was a really nice beginner's tutorial on doing exactly the attack needed here. (Credits to Cédric Van Rompay)

The idea behind the attack: Send 31 bytes of something simple: in my case, this was 31 As. Call this the target cyphertext.

The 32nd byte will be the first byte of the right padding (the CTF's format was flag{...}, so in this case, we already know it's 'f').

Because AES-ECB is a (16-byte) block cypher, 32 bytes of plaintext correspond exactly to 32 bytes of cyphertext.
The rest of the padding past the f won't affect those first two blocks of cyphertext.

Then, run through all printable characters i, and check if 31 As + i (so all 32 bytes) produces are cyphertext
that shares a 32 byte prefix with the target cyphertext above.

When we get to i = 'f', the current cyphertext and the target cyphertext will share that prefix. That's how we know we found the first byte of the padding.


Finally, repeat this by shifting the As buffer down: use a target plaintext of 30 As following by an f.

Once we find that the cyphertext from 30 As + 'f' + i shares a common prefix, we've found the next byte.

Continue until either our buffer of As run out, or we get the entire flag: flag{b4d_bl0cks_for_g0nks}.


Our pitfalls:

  1. The first time around, I chose a buffer of 16 As. This was too short, and we had to figure out how to generalize the approach.
  2. The length of the cyphertext / common prefix was different from what we expected. This was because we needed to b64decode the response.
  3. We started this a couple hours before the end of the competition. We should've attempted this sooner, and got more sleep instead.
from requests import post
from string import printable
from base64 import b64decode
def enc(p):
r = post('http://167.71.246.232:8080/crypto.php', data={'text_to_encrypt': p, 'do_encrypt': 'Encrypt'})
bgn, end = r.content.index(b'<b>'), r.content.index(b'</b>')
return b64decode(r.content[bgn+3:end].decode('utf-8'))
magic_len = 16 + 15
flag = ''
def tryAll(inp, target):
global flag, magic_len
actual_target = target[16:32]
for i in printable:
get = enc(inp + i)[16:32]
if actual_target == get:
print(f"SUCCESS: {i}")
flag = flag + i
magic_len = magic_len - 1
if i == '}': magic_len = 0
return
raise AssertionError(f'could not find char, flag so far: {flag}')
while magic_len: tryAll("A" * magic_len + flag, enc("A" * magic_len))
print(flag)
<html>
<body>
<h1>Netrunner Encryption Tool</h1>
<a href="netrun.txt">Source Code</a>
<form method=post action="crypto.php">
<input type=text name="text_to_encrypt">
<input type="submit" name="do_encrypt" value="Encrypt">
</form>
<?php
function pad_data($data) {
$flag = "flag{wouldnt_y0u_lik3_to_know}";
$pad_len = (16 - (strlen($data.$flag) % 16));
return $data . $flag . str_repeat(chr($pad_len), $pad_len);
}
if(isset($_POST["do_encrypt"])) {
$cipher = "aes-128-ecb";
$iv = hex2bin('00000000000000000000000000000000');
$key = hex2bin('74657374696E676B6579313233343536');
echo "</br><br><h2>Encrypted Data:</h2>";
$ciphertext = openssl_encrypt(pad_data($_POST['text_to_encrypt']), $cipher, $key, 0, $iv);
echo "<br/>";
echo "<b>$ciphertext</b>";
}
?>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment