ふるつき

v(*'='*)v かに

CTFZone 2019 Quals Writeup

[Crypto] Agents

  • AES-OFB encrypted json which may formed : {"trusted": 0, "N": ..., "e": 65537} (@ptr-yudai guessed this)
  • when we turned trused into 1, the server returned RSA-encrypted secrets using N and e in json.
  • Since encryption mode was OFB, once we could guess the json format then it is easy to manipulate plaintext by XOR.
from ptrlib import Socket
from base64 import b64decode, b64encode
from logging import getLogger, WARN

getLogger("ptrlib.pwn.sock").setLevel(WARN + 1)


def getMsg():
    sock.recvuntil("exit")
    sock.sendline("1")
    sock.recvuntil('Use name "')
    name = sock.recvline().decode().split('"')[0]
    sock.recvuntil("message:")
    sock.recvline()
    sock.recvline()
    cipher = sock.recvline()
    return name, b64decode(cipher)


def send(name, msg):
    sock.recvuntil("exit")
    sock.sendline("2")
    sock.recvuntil("your name")
    sock.sendline(name)
    name_check = sock.recvline().decode()
    if name_check.startswith("error"):
        return False, None
    sock.recvuntil("HQ")
    sock.sendline(b64encode(msg))
    sock.recvline()
    sock.recvline()
    result = sock.recv()
    if result.startswith(b"Thank you. HQ said "):
        result += sock.recv()
    return True, result


sock = Socket("crypto-agents.ctfz.one", 9543)
n, c = getMsg()
c = bytearray(c)
c[11] = c[11] ^ 1
c[-2] = c[-2] ^ ord("7") ^ ord("1")
c[-3] = c[-3] ^ ord("3") ^ ord(" ")
c[-4] = c[-4] ^ ord("5") ^ ord(" ")
c[-5] = c[-5] ^ ord("5") ^ ord(" ")
c[-6] = c[-6] ^ ord("6") ^ ord(" ")
print("{}".format(11), send(n, bytes(c)))

ctfzone{0FB_M0d3_C4n7_$4V3_A3$_K3Y}

The concept is very easy, but guessing part is hard and useless.