SecurityFest 2017 - Ranshomware
We found an sh ransomware on a server. Can you help us recover the server’s data?
This challenge provided an archive with a ranshomware.sh
bash script, a flag.txt
encrypted file in the flags
folder and a debian-40r9-amd64-businesscard.iso
encrypted file, along with some encrypted.txt
file with an hash inside.
Reading the bash script we discovered those files were encrypted with AES-256-CTR mode. The key was derived directly from /dev/urandom
.
But we noticed a bug, for every file the IV was incremented by 1, starting from 0.
Ideally the IV (or nonce) in AES-CTR mode should be random and not predictable, then for every block an internal couter will be concatenated with the IV.
This is working as a OneTimePad where the pad key is the AES encrypted IV.
You should never re-use a pad key. EVER.
This is a simple single-block scheme
Since the script is passing a full 256 bit IV to the openssl
command, the counter is not concatenated, but it’s added to the IV instead.
The IV for the flag.txt
file will be 00000000000000000000000000000005
The IV for the debian.iso
will be 00000000000000000000000000000003
The third block in the debian.iso
will have IV equals to 00000000000000000000000000000005
, the same as the flag.
Now, if you encrypt the same thing, with the same key and the same algorithm, you will get the same ciphertext.
The third block of ciphertext for the debian.iso
will be equals to C = X ⊕ P
, where C
is the ciphertext, P
is the plain debian.iso
and X
is the AES encryption of the above IV with the script key.
The first block of ciphertext for the flag.txt
file will share the same X
.
Without the key we can’t calculate X
but fortunately we have the plain debian.iso
, so X = C ⊕ P
using the iso plaintext and ciphertext, then we do P = C ⊕ X
for the flag ciphertext.
This will give us the decrypted flag.txt
content.
SCTF{MISSHANDLED_IVS_ARE_AWFUL_FOR_HEALTH_0H_4lM057_11k3_1337!}
Here our python script:
with open("debian_plain.iso") as f:
iso_p = f.read()
with open("debian_enc.iso") as f:
iso_e = f.read()
iso_p = iso_p[32:1024]
iso_e = iso_e[32:1024]
key = ""
for i in xrange(len(iso_e)):
key += chr(ord(iso_p[i]) ^ ord(iso_e[i]))
with open("flag.txt") as f:
flag = f.read()
result = ""
for i in xrange(len(flag)):
result += chr(ord(key[i]) ^ ord(flag[i]))
print result