I am trying to solve a CTF challenge based on DES. I attached the code of the challenge to the question. So far I have noticed that the otp used for the encryption is the same for the entire session, and my idea would be the following:
- Get the OTP by using choice 1
- Use the obtained OTP to decrypt the ciphertext given by choice 2 (since I pass the key)
I tried to implement this process and haven't managed to get the flag. Given that the hint of the challenge is the following:
Not all keys are strong alike.
Do you think that I am on the right track or do you have any proposal on how I could approach the problem? Thank you in advice.
Code of the challenge:
#!/usr/bin/env python3
import signal
from Crypto.Cipher import DES
from Crypto.Util.Padding import pad
from Crypto.Util.number import bytes_to_long
import os
TIMEOUT = 300
assert("FLAG" in os.environ)
flag = os.environ["FLAG"]
assert(flag.startswith("CCIT{"))
assert(flag.endswith("}"))
otp = os.urandom(8)
def xor(a, b):
return bytes([a[i % len(a)] ^ b[i % len(b)] for i in range(max(len(a), len(b)))])
def encrypt_des(text, key):
try:
key = bytes.fromhex(key)
text = xor(bytes.fromhex(text), otp)
cipher = DES.new(key, DES.MODE_ECB)
ct = xor(cipher.encrypt(pad(text, 8)), otp)
return ct.hex()
except Exception as e:
return f"Something went wrong: {e}"
def handle():
while True:
print("1. Encrypt text")
print("2. Encrypt flag")
print("0. Exit")
choice = int(input("> "))
if choice == 1:
text = input("What do you want to encrypt (in hex)? ").strip()
key = input("With what key (in hex)? ").strip()
print(encrypt_des(text, key))
elif choice == 2:
key = input("What key do you want to use (in hex)? ").strip()
print(encrypt_des(flag.encode().hex(), key))
else:
break
if __name__ == "__main__":
signal.alarm(TIMEOUT)
handle()
My current approach:
from pwn import *
from Crypto.Cipher import DES
from Crypto.Util.Padding import pad, unpad
import warnings
warnings.filterwarnings("ignore")
r = remote('desoracle.challs.cyberchallenge.it', 9035)
r.recvuntil('>')
# First we want to get the OTP
r.sendline('1')
myText = b'This is my cool text'.hex()
r.recvuntil('(in hex)?')
r.sendline(myText)
myKey = "FEFEFEFEFEFEFEFE" #this is one of the four weak keys
print(myKey)
r.recvuntil('(in hex)?')
r.sendline(myKey)
output = r.recvline().strip().decode()
outputInBytes = bytes.fromhex(output)
otp = xor(bytes.fromhex(myText), outputInBytes)
print(otp)
r.recvuntil('>')
r.sendline('2')
myKey = "FEFEFEFEFEFEFEFE" #this is one of the four weak keys
r.recvuntil('(in hex)?')
r.sendline(myKey)
output = r.recvline().strip().decode()
paddedFlagAfterXor = bytes.fromhex(output)
flag = xor(paddedFlagAfterXor, otp)
print(flag.decode())
r.interactive()
```
assert(foo)
. Please don't do that. Better to simplyassert foo
. Then a maintenance engineer won't be tricked into "refactoring" asassert(foo, "diagnostic msg")
, which means something very very different fromassert foo, "diagnostic msg"
. $\endgroup$