0xd13a

A rookie in a world of pwns

HackIT CTF 2017 Writeup: rev150

Broken Packer

rev150

Description: Looks like this packer can not unpack what has been packed :( There are 2 mistakes in unpacking procedure. It leads to the error. Try to fix unpacker and figure out what is inside.

Webpage: broken_packer.zip

We are given an archive with 2 files - a packer and a packed executable. As it later turns out the packer is not even needed to solve it, so let’s concentrate on the packed application, packed.

Running the application from command line yields a segfault:

1
2
$ ./packed
Segmentation fault

We better fix those errors the description is talking about. Let’s open it in IDA:

img1.png

The unpacking routine is very simple and the errors are easy to spot. The loop index is in rax so xor [rdx], dl should really be xor [rax], dl. Also, we go byte-by-byte, so the index increment should be add rax, 1, not add rax,2. Let’s patch the binary to correct these.

Patching the application gets us further:

1
2
3
$ ./packed
The hardest part is overcome.
Segmentation fault

Let’s debug the application and look at the disassembly to see what function outputs that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
.text:0000000000400BA9 sub_400BA9      proc near               ; DATA XREF: sub_400990+1D
.text:0000000000400BA9
.text:0000000000400BA9 var_10          = qword ptr -10h
.text:0000000000400BA9 var_4           = dword ptr -4
.text:0000000000400BA9
.text:0000000000400BA9                 push    rbp
.text:0000000000400BAA                 mov     rbp, rsp
.text:0000000000400BAD                 sub     rsp, 10h
.text:0000000000400BB1                 mov     [rbp+var_4], edi
.text:0000000000400BB4                 mov     [rbp+var_10], rsi
.text:0000000000400BB8                 lea     rdi, aTheHardestPart ; "The hardest part is overcome."
.text:0000000000400BBF                 call    sub_407FC0
.text:0000000000400BC4                 mov     rax, [rbp+var_10]
.text:0000000000400BC8                 add     rax, 8
.text:0000000000400BCC                 mov     rax, [rax]
.text:0000000000400BCF                 lea     rsi, aCryp      ; "cryp"
.text:0000000000400BD6                 mov     rdi, rax
.text:0000000000400BD9                 call    sub_400370
.text:0000000000400BDE                 test    eax, eax
.text:0000000000400BE0                 jnz     short loc_400BF5
.text:0000000000400BE2                 mov     rax, [rbp+var_10]
.text:0000000000400BE6                 add     rax, 10h
.text:0000000000400BEA                 mov     rax, [rax]
.text:0000000000400BED                 mov     rdi, rax
.text:0000000000400BF0                 call    sub_400AAE
.text:0000000000400BF5
.text:0000000000400BF5 loc_400BF5:                             ; CODE XREF: sub_400BA9+37
.text:0000000000400BF5                 mov     rax, [rbp+var_10]
.text:0000000000400BF9                 add     rax, 8
.text:0000000000400BFD                 mov     rax, [rax]
.text:0000000000400C00                 lea     rsi, aDecr      ; "decr"
.text:0000000000400C07                 mov     rdi, rax
.text:0000000000400C0A                 call    sub_400370
.text:0000000000400C0F                 test    eax, eax
.text:0000000000400C11                 jnz     short loc_400C18
.text:0000000000400C13                 call    sub_400B39
.text:0000000000400C18
.text:0000000000400C18 loc_400C18:                             ; CODE XREF: sub_400BA9+68
.text:0000000000400C18                 mov     eax, 0
.text:0000000000400C1D                 leave
.text:0000000000400C1E                 retn
.text:0000000000400C1E sub_400BA9      endp

Looks like there is a cryp and a decr command line parameters. Assuming we need to decrypt the flag, we first need to find where the encrypted flag is. Running with cryp parameter gives us a hint at what it looks like:

1
2
3
$ ./packed cryp h4ck1t{test}
The hardest part is overcome.
0x95, 0xc0, 0x90, 0x8e, 0xc5, 0x8f, 0x74, 0x31, 0x6b, 0x63, 0x34, 0x68,

The encrypted bytes look like this:

1
95 C0 90 8E C5 8F 74 31 6B 63 34 68   ......t1kc4h

So the ending is just the flag prefix, reversed. Luckily, the encrypted flag is embedded in the binary:

img2.png

Now let’s analyze the decryption algorithm so that we could reproduce it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
.text:0000000000400B39 sub_400B39      proc near               ; CODE XREF: sub_400BA9+6A
.text:0000000000400B39
.text:0000000000400B39 var_9           = byte ptr -9
.text:0000000000400B39 var_8           = dword ptr -8
.text:0000000000400B39 var_4           = dword ptr -4
.text:0000000000400B39
.text:0000000000400B39                 push    rbp
.text:0000000000400B3A                 mov     rbp, rsp
.text:0000000000400B3D                 sub     rsp, 10h
.text:0000000000400B41                 lea     rdi, byte_6B20A0
.text:0000000000400B48                 call    sub_417BB0
.text:0000000000400B4D                 mov     [rbp+var_8], eax
.text:0000000000400B50                 mov     eax, [rbp+var_8]
.text:0000000000400B53                 mov     [rbp+var_4], eax
.text:0000000000400B56
.text:0000000000400B56 loc_400B56:                             ; CODE XREF: sub_400B39+6B
.text:0000000000400B56                 cmp     [rbp+var_4], 0
.text:0000000000400B5A                 js      short loc_400BA6
.text:0000000000400B5C                 mov     eax, [rbp+var_4]
.text:0000000000400B5F                 movsxd  rdx, eax
.text:0000000000400B62                 lea     rax, byte_6B20A0
.text:0000000000400B69                 movzx   ecx, byte ptr [rdx+rax]
.text:0000000000400B6D                 mov     eax, [rbp+var_8]
.text:0000000000400B70                 sub     eax, 1
.text:0000000000400B73                 sub     eax, [rbp+var_4]
.text:0000000000400B76                 movsxd  rdx, eax
.text:0000000000400B79                 lea     rax, byte_6B20A0
.text:0000000000400B80                 movzx   eax, byte ptr [rdx+rax]
.text:0000000000400B84                 xor     eax, ecx
.text:0000000000400B86                 xor     eax, 0FFFFFF80h
.text:0000000000400B89                 mov     [rbp+var_9], al
.text:0000000000400B8C                 mov     eax, [rbp+var_4]
.text:0000000000400B8F                 movsxd  rdx, eax
.text:0000000000400B92                 lea     rax, byte_6B20A0
.text:0000000000400B99                 movzx   ecx, [rbp+var_9]
.text:0000000000400B9D                 mov     [rdx+rax], cl
.text:0000000000400BA0                 sub     [rbp+var_4], 1
.text:0000000000400BA4                 jmp     short loc_400B56
.text:0000000000400BA6 ; ---------------------------------------------------------------------------
.text:0000000000400BA6
.text:0000000000400BA6 loc_400BA6:                             ; CODE XREF: sub_400B39+21
.text:0000000000400BA6                 nop
.text:0000000000400BA7                 leave
.text:0000000000400BA8                 retn
.text:0000000000400BA8 sub_400B39      endp

Essentially the algorithm reverses the flag, and then encodes the first part of it by XORing it with characters in the second part and with 0x80.

To decode it let’s write a short script:

1
2
3
4
5
6
7
8
9
data = bytearray(open("packed","rb").read())

off = 0xb20a0
out = ""

for x in range(0x30/2):
	out += chr(data[off + x] ^ data[off + 0x30 - x - 1] ^ 0x80)
	
print (out + str(data[off + 0x30/2: off + 0x30]))[::-1]

The flag is h4ck1t{mor0ns_rel1es_on_p4ck3r5_vari0rs_d03sn0t}:

1
2
$ python solve.py 
h4ck1t{mor0ns_rel1es_on_p4ck3r5_vari0rs_d03sn0t}