Agents [crypto]

Agents

Our competitors are recruiting delivery guys. I heard they were using a very weird task to test the applicants. See what you can do about it.

nc crypto-agents.ctfz.one 9543

Recon

Does AES in OFB mode, so it's pretty much a stream cipher where there's a keystream xor'd with the plaintext.

$ nc crypto-agents.ctfz.one 9543

Choose action:
1) Go to HQ
2) Go to Agent
<any other key>) exit
1
You are messenger. Your mission is to deliver message to our agent. Use name "geniya560766" for communication.

Message is securily encrypted with AES-OFB (key and IV already delivered with quantum channel). Just deliver this message: 

9h3dAesMfnAFT7Y4cqCaD+NtneWVVdPqLFAZQz8vLL3avq4FeWKzSwwQftL5hYiNsXd59OCqd3WNntwZX/soYAMVrbzAh+ZuK2H/7Ho4sknDP9G6wNaFbVnIPKpzZYVRhMOvHaz2bX1bgafSyk2XS+ppOLWvXyJ9uiHPZaGgS9yG6vTVsZqk0TnABppV+ISqAyeV6om7joR9j+qEadCdnsjALDLgFUi9tLwNVn90GuSgGr5tcLRzBtay/MmR4CqX6KTs+RbgpocARYztTXDgsbPaqD9hboKj06nW00S3BTfXqDo98D/oky2/ULdXeC+HfionaHwzgc4fNlLRCYyLYmYWOEq1Kp403Olm9gNQe9kWkPHDFAn1kN14HwA0sQloNAK3Jh+VpyQROq0QaY0mhRIh/W9NUGXlCQFyyA4Jqj14Zk26uk1TaQICw0aMXNKhOg==

Choose action:
1) Go to HQ
2) Go to Agent
<any other key>) exit
2
Hello, delivery boy! Give me your name

geniya560766
Ok, give me message from HQ

9h3dAesMfnAFT7Y4cqCaD+NtneWVVdPqLFAZQz8vLL3avq4FeWKzSwwQftL5hYiNsXd59OCqd3WNntwZX/soYAMVrbzAh+ZuK2H/7Ho4sknDP9G6wNaFbVnIPKpzZYVRhMOvHaz2bX1bgafSyk2XS+ppOLWvXyJ9uiHPZaGgS9yG6vTVsZqk0TnABppV+ISqAyeV6om7joR9j+qEadCdnsjALDLgFUi9tLwNVn90GuSgGr5tcLRzBtay/MmR4CqX6KTs+RbgpocARYztTXDgsbPaqD9hboKj06nW00S3BTfXqDo98D/oky2/ULdXeC+HfionaHwzgc4fNlLRCYyLYmYWOEq1Kp403Olm9gNQe9kWkPHDFAn1kN14HwA0sQloNAK3Jh+VpyQROq0QaY0mhRIh/W9NUGXlCQFyyA4Jqj14Zk26uk1TaQICw0aMXNKhOg==
Thank you. HQ posted here that you are untrusted, so i cant give you secret info.

Bye!


Choose action:
1) Go to HQ
2) Go to Agent
<any other key>) exit

By modifying the message, a bit we get a "JSON corrupted." message. So assuming there's some "trusted": 1 entry, we'll just xor every position with 1 until we find it. Eventually at position 11 we get a message:

Ok, give me message from HQ

9h3dAesMfnAFT7Y4cqCaD+NtneWVVdPqLFAZQz8vLL3avq4FeWKzSwwQftL5hYiNsXd59OCqd3WNntwZX/soYAMVrbzAh+ZuK2H/7Ho4sknDP9G6wNaFbVnIPKpzZYVRhMOvHaz2bX1bgafSyk2XS+ppOLWvXyJ9uiHPZaGgS9yG6vTVsZqk0TnABppV+ISqAyeV6om7joR9j+qEadCdnsjALDLgFUi9tLwNVn90GuSgGr5tcLRzBtay/MmR4CqX6KTs+RbgpocARYztTXDgsbPaqD9hboKj06nW00S3BTfXqDo98D/oky2/ULdXeC+HfionaHwzgc4fNlLRCYyLYmYWOEq1Kp403Olm9gNQe9kWkPHDFAn1kN14HwA0sQloNAK3Jh+VpyQROq0QaY0mhRIh/W9NUGXlCQFyyA4Jqj14Zk26uk1TaQICw0aMXNKhOg==

Thank you. HQ said that I can rely on you. Here is top secret message.
It is encrypted with public RSA key contained in message that you gave me.
Please deliver it to HQ as soon as posible.

8fe5fa1ce66f6df9e119ee0b15340d88a5075cbc5f962775b17fbc54b21bbada40796b830854c23422a306e461770e03914d41a2a46c33db4b0eb5c2530b9ceb261b5b7f3e395cdc642e1b209d92e2a5ab2fddf53fe5fe63a33b1b80e16214c0a69f48cc89b691f3d41463517886cd14fa9870b974668ef9fac58185d411e15e

Choose action:
1) Go to HQ
2) Go to Agent
<any other key>) exit

Cool, the pubkey is in the encrypted message. We can use the "JSON corrupted" error messages and a bit of scanning to figure out where n and e are and how they're formatted. Eventually, we find out that e is the last field and it's formatted as an int. Something like ...FIELDS...,"e":12345, where the int representation starts at position 331. Knowing that a safe choice for e is 65537, we can just xor the encrypted e with 65537 xor \x20\x20\x20\x201, so it should decrypt to <sp><sp><sp><sp>1 where <sp> is the space character - 0x20. Then we should just get \(m^1 \mod n \equiv m\)

Code

from pwn import *
from base64 import b64encode, b64decode
from Crypto.Util.number import long_to_bytes
from Crypto.Util.strxor import strxor
import re
import string
hexa = re.compile(r'^([0-9a-fA-F]+)$', re.MULTILINE)

c = remote("crypto-agents.ctfz.one", 9543)

print(c.readuntil("exit\r\n"))

pos_e = 331
c.send("1\r\n")
print(c.readuntil("name \""))
name = c.readuntil("\"").replace('"', '')
print("GOT NAME: " + name)
print(c.readuntil("\n"))
print(c.readuntil("message: \r\n\r\n"))
ct = list(b64decode(c.readuntil("\n\n").strip()))
ct[11] = chr(ord(ct[11]) ^ 1)
se = strxor("65537", "\x20" * 4 + "1")
print("E XORD: %r" % se)
pxor = strxor(se, ''.join(ct[pos_e:pos_e+5]))
for i in range(pos_e, pos_e+5):
    ct[i] = pxor[i - pos_e]
ct = b64encode(''.join(ct))
print("GOT CT: " + ct)
print("CHANGED POSITION: " + str(pos_e))
print("EXIT< " + c.readuntil("exit\r\n"))
c.send("2\r\n")
print("NAME< " + c.readuntil("name\r\n\r\n"))
c.send(name + "\r\n")
print("HQ< " + c.readuntil("HQ\r\n\r\n"))
c.send(ct + "\r\n")
exity = c.readuntil("exit\r\n")
print("EXIT< " + exity)

if "posible." in exity:
    msg = hexa.search(exity).groups(1)[0]
    print("GOT MESSAGE: " + msg)
    x = int(msg, 16)
    solved = long_to_bytes(x)
    if all(map(lambda s: s in string.printable, solved)):
        print("ALL PRINTABLE MESSAGE: " + solved)
GOT MESSAGE: 6374667a6f6e657b3046425f4d3064335f43346e375f243456335f4133245f4b33597d
ALL PRINTABLE MESSAGE: ctfzone{0FB_M0d3_C4n7_$4V3_A3$_K3Y}

Flag

ctfzone{0FB_M0d3_C4n7_$4V3_A3$_K3Y}