Over the weekend I participated in dCTF by DragonSec SI along with some friends. There were some really interesting and unique challenges in this CTF.

Summary

This was a time-restricted python crypto challenge served over a socket. The first part was to solve a simple maths equation within two seconds. The second part was cracking a triple DES cipher that was initialized using time as the key, also within a two-second time limit.

Challenge Source

#!/usr/bin python3

from flag import flag
from Crypto.Cipher import DES3
from time import time
from random import randint
from secrets import token_hex
from pytimedinput import timedInput

guess = 3
TIMEOUT = 2

a = randint(1000000000000000, 9999999999999999)
b = randint(1000000000000000, 9999999999999999)

print("Show me you are worthy and solve for x! You have one second.")
print("{} * {} = ".format(a, b))

answ, _ = timedInput("> ", timeOut = 2, forcedTimeout = True)

try:
    assert(a*b == int(answ))
except:
    print("You are not worthy!")
    exit(1)

key = str(int(time()).zfill(16).encode("utf-8")
secret = token_hex(16)
cipher = DES3.new(key, DES3.MODE_CFB, b"00000000")
encrypted = cipher.encrypt(secret.encode("utf-8"))
print("You have proven yourself to be capable of taking on the final task. Decrypt this and the flag shall be yours!")
print(encrypted.hex())


start_time = time()
while(time() - start_time < TIMEOUT and guess > 0):
    delta = time() - start_time
    answ, _ = timedInput("> ", timeOut = TIMEOUT + 1 - delta, forcedTimeout = True)

    try:
        assert(secret == answ)
        break
    except:
        if answ != "":
            guess -= 1
            if (guess != 1):
                print("You are wrong. {} guesses remain.".format(guess))
            else:
                print("You are wrong. {} guess remains.".format(guess))

if (secret != answ):
    print("You have been unsuccessful in your quest for the flag.")
else:
    print("Congratulations! Here is your flag.")
    print(flag)
The challenge source code

Solution

The following solution uses pwntools to handle the connection to the server. The crux of the solution comes down to getting the same value for int(time()) as the server. Initially, I thought this could be problematic; however, I found it was correct when running the solution on the first attempt.

from pwn import *
from Crypto.Cipher import DES3
from time import time

def solve_maths(equ):
    a,b = equ.split('=')[0].replace(' ','').split('*')
    return int(a)*int(b)

def decrypt(t1, ct):
    key = str(t1).zfill(16).encode("utf-8")
    cipher = DES3.new(key, DES3.MODE_CFB, b"00000000")
    return cipher.decrypt(bytes.fromhex(ct))

c = remote('dctf-chall-just-take-your-time.westeurope.azurecontainer.io', 9999)
c.recvline()
equ = c.recvline()
print('Equ: {}'.format(equ))
solution = solve_maths(equ.decode("utf-8"))
print('Sol: {}'.format(solution))

t1 = int(time())
print('Time: {}'.format(t1))

c.sendline(str(solution))


if 'capable' not in c.recvline().decode("utf-8"):
    print('Failed...')
    exit()

ct = c.recvline().strip().decode("utf-8")

print('CT: {}'.format(ct))

for t in range(t1, t1+3):

    secret = decrypt(t, ct).decode("utf-8")

    print('Attempt: {} - {} - {}'.format(t, ct, secret))

    c.sendline(secret)

    if 'wrong' in c.recvline().decode("utf-8"):
        print('Wrong...')
    else:
        break

c.interactive()
The source code of one possible solution

Running this gives the following:

The solution in action

So now we have the flag, woo!

dctf{1t_0n1y_t0Ok_2_d4y5...}