Aynakeya's Blog

Kill My Emotion

Malformed Query - How to analyze a transmission protocol [utctf 2022]

0x0 Description

The problem give us a wireshark data packets files. Our goal is to analyze the packet, find the protocol used in the interaction and get the flag.

0x1 Analyze the protocol

in the wireshark, there is a very suspicious udp stream (3.93.213.98:9855) that gives us a command result of ls -al.

the connection start with a packet that contains publickey.

Then server reply with a packet with a RSA publickey.

1
2
00000000  00 0c 01 00 00 01 00 00  00 00 00 00 09 70 75 62   ........ .....pub
00000010 6c 69 63 6b 65 79 00 00 10 00 01 lickey.. ...

If we copy the first packet and send to the sever, we got a new response with a different publickey!

This clearly shows that the server is using a special protocol to interact with client.

Now, our goal is to figure out how is the protocol looks like.

1
2
3
4
from pwn import *
io = connect("3.93.213.98",9855,typ="udp")
io.send(bytes.fromhex("000c01000001000000000000097075626c69636b65790000100001"))
print(io.recv())
1
2
3
$ python xxx.py 
[+] Opening connection to 3.93.213.98 on port 9855: Done
b'\x00\x0c\x81\x80\x00\x01\x00\x02\x00\x00\x00\x00\tpublickey\x00\x00\x10\x00\x01\xc0\x0c\x00\x10\x00\x01\x00\x00\x137\x00\xff\xfe-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyljjH5MViK9eDX3TYlO8\nCei+rVufA+lrsw36gv/Ntv34PBXebZBC8BSwy/t0jMHnn7+9fY0zum9sMwV7A7R9\n3RWt5WppeqPyhuFNlM8DoGN5RLjTVLLKvSG2df5c8IktfDpjdrgUYDOiMMN7ANVE\nyIK+Nt+RBoGK2fkKk3NljlmmXKKP\xc0\x0c\x00\x10\x00\x01\x00\x00\x137\x00\xce\xcdU2yQZX6uHgMPXk1QSvXRsPcdWG255dBhVXK/\nrB2vAMOsD2QDMiUEa5KFgDxoBT3CH1H2nPCcXGux2j+gCpxyzzSdWrdxw64xmcGm\nrYWyC/lEygNDYc82JQJatHJSeDmz1TeA6LoY29QnKzSfrOZNvRxaB9NbbY7s9zRS\nJwIDAQAB\n-----END RSA PUBLIC KEY-----\n'

Further analyze the packet, we found all the packet follows a similar structure.

1
2
header  + sequence num + next sequence num + magic data + [length +  data  + ending] * n
4 bytes + 2 bytes + 2 bytes + 4 bytes + [1 byte + n byte + 5 byte] * n

Note that the max length for a single message is 0xfe(254), if our data is more than 254 bytes, we need seperate data into 254 bytes chunks.

Here is the packet (9 bytes) client send to the server

1
2
3
4
00000000  00 0c 01 00 00 01 00 00  00 00 00 00 09     70 75 62   ........ .....pub
header seq n seq magic data length data
00000010 6c 69 63 6b 65 79 00 00 10 00 01 lickey.. ...
ending bytes

here is another example packet (more than 254 bytes) received from the server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    00000000  00 0c 81 80 00 01 00 02    00 00 00 00 09  70 75 62   ........ .....pub
header seq next seq magic data len data
00000010 6c 69 63 6b 65 79 00 00 10 00 01 c0 0c 00 10 00 lickey.. ........
ending bytes header seq
00000020 01 00 00 13 37 00 ff fe 2d 2d 2d 2d 2d 42 45 47 ....7... -----BEG
next seq magic data len data
00000030 49 4e 20 52 53 41 20 50 55 42 4c 49 43 20 4b 45 IN RSA P UBLIC KE
00000040 59 2d 2d 2d 2d 2d 0a 4d 49 49 42 49 6a 41 4e 42 Y-----.M IIBIjANB
......
00000120 58 39 43 30 6c 33 c0 0c 00 10 00 01 00 00 13 37 X9C0l3.. .......7
ending bytes
00000130 00 ce cd 31 58 72 62 36 75 33 70 74 5a 78 4f 49 ...1Xrb6 u3ptZxOI
......
000001E0 41 42 0a 2d 2d 2d 2d 2d 45 4e 44 20 52 53 41 20 AB.----- END RSA
000001F0 50 55 42 4c 49 43 20 4b 45 59 2d 2d 2d 2d 2d 0a PUBLIC K EY-----.

After we receive the public key, we can encrypt command with public key, send packet to the server and get the flag.

(The encryption using OAEP scheme with sha512)

0x2 Final script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Hash import SHA512
import base64
io = connect("3.93.213.98",9855,typ="udp")
io.send(bytes.fromhex("000c01000001000000000000097075626c69636b65790000100001"))
print(io.recvuntil(b"-----BEGIN RSA PUBLIC KEY-----\n").hex())
public_key = io.recvuntil(b"\n-----END RSA PUBLIC KEY-----\n",drop=True).replace(b"\xc0\x0c\x00\x10\x00\x01\x00\x00\x137\x00\xce\xcd",b"")
public_key = b"-----BEGIN RSA PUBLIC KEY-----\n"+public_key+b"\n-----END RSA PUBLIC KEY-----"
print(public_key.decode())
keyPub = RSA.importKey(public_key)
cipher = PKCS1_OAEP.new(keyPub,SHA512)
cipher_text = cipher.encrypt(b"ls")
print(len(cipher_text),cipher_text.hex())
payload = bytes.fromhex("000c01000002000000000000")+bytes.fromhex("fe")+cipher_text[:254:]+bytes.fromhex("0000100001")+bytes.fromhex("02")+cipher_text[254:256:]+bytes.fromhex("0000100001")
print(payload.hex())
io.send(payload)
print(io.recv())
0%