Pay2Load

Aperi'CTF 2019 - Forensic (175 pts).

Aperi’CTF 2019 - Pay2Load

Challenge details

Event Challenge Category Points Solves
Aperi’CTF 2019 Pay2Load Forensic 175 3

We’re given a chall data.bin file.

Task description:

Our company was recently targeted by a ransomware campaign.

Despite our deep packet analysis probes, the attacker managed to infiltrate and compromise one of our servers by exploiting a 0day vulnerability.

Fortunately, the forensic team was able to recover a suspicious payload that might have been used during the attack.

Analyze this payload!

Note: the targeted server is running Linux.

TL;DR

The challenge consists in analyzing a file containing an alphanumeric payload. After some analysis, we finally discover that this payload is a shellcode for Linux i386 architecture that prints a flag.

File analysis

At first glance, the file doesn’t seem to be a known file format, but let’s check it anyway:

$ file files/data.bin
files/data.bin: ASCII text, with very long lines, with no line terminators
$ binwalk files/data.bin

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------

Data analysis

Let’s analyze the data using Python to see if we find any interesting information about its content:

data = 'PYj0X40PPPPPQaJP50000RX4DjzY0DOPVX500005O000PTY01VX5000B5000pPTYAAA01VX50000500t0PTYAA01VX500005Ow10PTY01A01VX5000B5000pPTYAAA01VX58H7E5NVrXPTY01A01A01VX501015qabzPVX510B05absXPVX5B1015vSXJPVX5004B5cXZuPVX504E05XXvoPVX50B445XqXXPVX5B0005rTUXPVX502005M8XSPVX500BB5aXccPVX500BB500stPTYAAA01VX5000050t10PTYA01VX500005w400PTY01Tx'

occurrences = {}
for char in set(data):
    occurrences[char] = data.count(char)

print('Max char value: 0x{0:02x}'.format(max(map(ord, set(data)))))
print('Min char value: 0x{0:02x}'.format(min(map(ord, set(data)))))
print('Number of different chars: {:d}'.format(len(set(data))))
print('Character occurrences:')

for char in sorted(occurrences, key=occurrences.get, reverse=True):
    print('{:s}: {:d} ({:f}%)'.format(char, occurrences[char], (occurrences[char] / len(data) * 100)))

Output:

Max char value: 0x7a
Min char value: 0x30
Number of different chars: 41
Character occurrences:
0: 84 (25.846154%)
5: 37 (11.384615%)
X: 32 (9.846154%)
P: 26 (8.000000%)
1: 19 (5.846154%)
V: 19 (5.846154%)
A: 15 (4.615385%)
B: 11 (3.384615%)
...

As shown above, the payload seems to use only printable characters:

Decimal Hex Char
48 0x30 0
122 0x7a z

This charset could be used for base64 encoding, but we’re not able to decode the data as is:

>>> binascii.a2b_base64(data)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
binascii.Error: Invalid base64-encoded string: number of data characters (325) cannot be 1 more than a multiple of 4

According to the top 10 character occurrences, the string seems to contain many alphanumeric characters, but let’s analyze the data using binvis.io!

entropybyteclass

On the left is the entropy visualization - black is the zero entropy, with colors ranging from blue to hot pink for maximum entropy.

On the right is the Hilbert curve visualization that allows us to visualize the usage of common byte classes:

Color Byte class
0x00
Low
Ascii
High
0xFF

The data is only composed of ASCII characters…

Reading the task description again, we’re informed that this string has been used to compromise a server which is running Linux.

If we take some time to think about what type of string can be used to compromise an operating system, we can establish this short list:

  • Basic injection payload (e.g., SQLi, XSS, command, etc.)
  • Shellcode
  • File

As described above, the string doesn’t seem to use a known format, and therefore shouldn’t have been used for basic injection such as SQL or command injection.

Let’s check if it’s a valid shellcode!

Shellcode analysis

The well-known Online x86 / x64 Assembler and Disassembler developped by Taylor Hornby can be used to disassemble a shellcode, let’s try it!

First, we need to convert the string to its hexadecimal representation:

print(''.join(['\\x{:02x}'.format(ord(c)) for c in data]))

Output:

\x50\x59\x6a\x30\x58\x34\x30\x50\x50\x50\x50\x50\x51\x61\x4a\x50\x35\x30\x30\x30\x30\x52\x58\x34\x44\x6a\x7a\x59\x30\x44\x4f\x50\x56\x58\x35\x30\x30\x30\x30\x35\x4f\x30\x30\x30\x50\x54\x59\x30\x31\x56\x58\x35\x30\x30\x30\x42\x35\x30\x30\x30\x70\x50\x54\x59\x41\x41\x41\x30\x31\x56\x58\x35\x30\x30\x30\x30\x35\x30\x30\x74\x30\x50\x54\x59\x41\x41\x30\x31\x56\x58\x35\x30\x30\x30\x30\x35\x4f\x77\x31\x30\x50\x54\x59\x30\x31\x41\x30\x31\x56\x58\x35\x30\x30\x30\x42\x35\x30\x30\x30\x70\x50\x54\x59\x41\x41\x41\x30\x31\x56\x58\x35\x38\x48\x37\x45\x35\x4e\x56\x72\x58\x50\x54\x59\x30\x31\x41\x30\x31\x41\x30\x31\x56\x58\x35\x30\x31\x30\x31\x35\x71\x61\x62\x7a\x50\x56\x58\x35\x31\x30\x42\x30\x35\x61\x62\x73\x58\x50\x56\x58\x35\x42\x31\x30\x31\x35\x76\x53\x58\x4a\x50\x56\x58\x35\x30\x30\x34\x42\x35\x63\x58\x5a\x75\x50\x56\x58\x35\x30\x34\x45\x30\x35\x58\x58\x76\x6f\x50\x56\x58\x35\x30\x42\x34\x34\x35\x58\x71\x58\x58\x50\x56\x58\x35\x42\x30\x30\x30\x35\x72\x54\x55\x58\x50\x56\x58\x35\x30\x32\x30\x30\x35\x4d\x38\x58\x53\x50\x56\x58\x35\x30\x30\x42\x42\x35\x61\x58\x63\x63\x50\x56\x58\x35\x30\x30\x42\x42\x35\x30\x30\x73\x74\x50\x54\x59\x41\x41\x41\x30\x31\x56\x58\x35\x30\x30\x30\x30\x35\x30\x74\x31\x30\x50\x54\x59\x41\x30\x31\x56\x58\x35\x30\x30\x30\x30\x35\x77\x34\x30\x30\x50\x54\x59\x30\x31\x54\x78

Let’s submit this shellcode to the disassembler with x86 architecture:

0:     50               push   eax
1:     59               pop    ecx
2:     6a 30            push   0x30
4:     58               pop    eax
5:     34 30            xor    al,0x30
7:     50               push   eax
8:     50               push   eax
9:     50               push   eax
a:     50               push   eax
b:     50               push   eax
c:     51               push   ecx
d:     61               popa
e:     4a               dec    edx
f:     50               push   eax
10:    35 30 30 30 30   xor    eax,0x30303030
15:    52               push   edx
16:    58               pop    eax
17:    34 44            xor    al,0x44
19:    6a 7a            push   0x7a
1b:    59               pop    ecx
1c:    30 44 4f 50      xor    BYTE PTR [edi+ecx*2+0x50],al
20:    56               push   esi
21:    58               pop    eax
22:    35 30 30 30 30   xor    eax,0x30303030
27:    35 4f 30 30 30   xor    eax,0x3030304f
2c:    50               push   eax
2d:    54               push   esp
2e:    59               pop    ecx
2f:    30 31            xor    BYTE PTR [ecx],dh
31:    56               push   esi
32:    58               pop    eax
33:    35 30 30 30 42   xor    eax,0x42303030
38:    35 30 30 30 70   xor    eax,0x70303030
3d:    50               push   eax
3e:    54               push   esp
3f:    59               pop    ecx
40:    41               inc    ecx
41:    41               inc    ecx
42:    41               inc    ecx
43:    30 31            xor    BYTE PTR [ecx],dh
45:    56               push   esi
46:    58               pop    eax
47:    35 30 30 30 30   xor    eax,0x30303030
4c:    35 30 30 74 30   xor    eax,0x30743030
51:    50               push   eax
52:    54               push   esp
53:    59               pop    ecx
54:    41               inc    ecx
55:    41               inc    ecx
56:    30 31            xor    BYTE PTR [ecx],dh
58:    56               push   esi
59:    58               pop    eax
5a:    35 30 30 30 30   xor    eax,0x30303030
5f:    35 4f 77 31 30   xor    eax,0x3031774f
64:    50               push   eax
65:    54               push   esp
66:    59               pop    ecx
67:    30 31            xor    BYTE PTR [ecx],dh
69:    41               inc    ecx
6a:    30 31            xor    BYTE PTR [ecx],dh
6c:    56               push   esi
6d:    58               pop    eax
6e:    35 30 30 30 42   xor    eax,0x42303030
73:    35 30 30 30 70   xor    eax,0x70303030
78:    50               push   eax
79:    54               push   esp
7a:    59               pop    ecx
7b:    41               inc    ecx
7c:    41               inc    ecx
7d:    41               inc    ecx
7e:    30 31            xor    BYTE PTR [ecx],dh
80:    56               push   esi
81:    58               pop    eax
82:    35 38 48 37 45   xor    eax,0x45374838
87:    35 4e 56 72 58   xor    eax,0x5872564e
8c:    50               push   eax
8d:    54               push   esp
8e:    59               pop    ecx
8f:    30 31            xor    BYTE PTR [ecx],dh
91:    41               inc    ecx
92:    30 31            xor    BYTE PTR [ecx],dh
94:    41               inc    ecx
95:    30 31            xor    BYTE PTR [ecx],dh
97:    56               push   esi
98:    58               pop    eax
99:    35 30 31 30 31   xor    eax,0x31303130
9e:    35 71 61 62 7a   xor    eax,0x7a626171
a3:    50               push   eax
a4:    56               push   esi
a5:    58               pop    eax
a6:    35 31 30 42 30   xor    eax,0x30423031
ab:    35 61 62 73 58   xor    eax,0x58736261
b0:    50               push   eax
b1:    56               push   esi
b2:    58               pop    eax
b3:    35 42 31 30 31   xor    eax,0x31303142
b8:    35 76 53 58 4a   xor    eax,0x4a585376
bd:    50               push   eax
be:    56               push   esi
bf:    58               pop    eax
c0:    35 30 30 34 42   xor    eax,0x42343030
c5:    35 63 58 5a 75   xor    eax,0x755a5863
ca:    50               push   eax
cb:    56               push   esi
cc:    58               pop    eax
cd:    35 30 34 45 30   xor    eax,0x30453430
d2:    35 58 58 76 6f   xor    eax,0x6f765858
d7:    50               push   eax
d8:    56               push   esi
d9:    58               pop    eax
da:    35 30 42 34 34   xor    eax,0x34344230
df:    35 58 71 58 58   xor    eax,0x58587158
e4:    50               push   eax
e5:    56               push   esi
e6:    58               pop    eax
e7:    35 42 30 30 30   xor    eax,0x30303042
ec:    35 72 54 55 58   xor    eax,0x58555472
f1:    50               push   eax
f2:    56               push   esi
f3:    58               pop    eax
f4:    35 30 32 30 30   xor    eax,0x30303230
f9:    35 4d 38 58 53   xor    eax,0x5358384d
fe:    50               push   eax
ff:    56               push   esi
100:   58               pop    eax
101:   35 30 30 42 42   xor    eax,0x42423030
106:   35 61 58 63 63   xor    eax,0x63635861
10b:   50               push   eax
10c:   56               push   esi
10d:   58               pop    eax
10e:   35 30 30 42 42   xor    eax,0x42423030
113:   35 30 30 73 74   xor    eax,0x74733030
118:   50               push   eax
119:   54               push   esp
11a:   59               pop    ecx
11b:   41               inc    ecx
11c:   41               inc    ecx
11d:   41               inc    ecx
11e:   30 31            xor    BYTE PTR [ecx],dh
120:   56               push   esi
121:   58               pop    eax
122:   35 30 30 30 30   xor    eax,0x30303030
127:   35 30 74 31 30   xor    eax,0x30317430
12c:   50               push   eax
12d:   54               push   esp
12e:   59               pop    ecx
12f:   41               inc    ecx
130:   30 31            xor    BYTE PTR [ecx],dh
132:   56               push   esi
133:   58               pop    eax
134:   35 30 30 30 30   xor    eax,0x30303030
139:   35 77 34 30 30   xor    eax,0x30303477
13e:   50               push   eax
13f:   54               push   esp
140:   59               pop    ecx
141:   30 31            xor    BYTE PTR [ecx],dh
143:   54               push   esp
144:   78               .byte 0x78

Except the last byte, this seems to be a valid shellcode consisting essentially of pushing xored data onto the stack.

If we analyze it a little more deeply, we notice that the following instruction set operates on the last byte of the shellcode:

17:    34 44            xor    al,0x44
19:    6a 7a            push   0x7a
1b:    59               pop    ecx
1c:    30 44 4f 50      xor    BYTE PTR [edi+ecx*2+0x50],al

Basically, it’s used to replace the last byte of the shellcode:

al = 0xFF
al ^= 0x44
byte = 0x78
hex(byte ^ al)

Output:

0xc3

Which, according to the Intel instruction set reference corresponds to the ret instruction opcode:

Opcode Instruction Description
C3 RET Near return to calling procedure

If we pay attention to what is pushed on the stack, we actually get another shellcode:

\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\x31\xc9\x51\x68\x21\x21\x7d\x0a\x68\x63\x30\x64\x65\x68\x68\x33\x6c\x6c\x68\x6c\x33\x5f\x53\x68\x6e\x37\x34\x62\x68\x7b\x50\x52\x31\x68\x41\x50\x52\x4b\x89\xe1\xba\x1d\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80

Which can be dissassembled to:

0:    b8 04 00 00 00   mov    eax,0x4      ; write syscall
5:    bb 01 00 00 00   mov    ebx,0x1      ; STDOUT
a:    31 c9            xor    ecx,ecx      ; ecx = 0
c:    51               push   ecx          ; '\x00' (null terminated string)
d:    68 21 21 7d 0a   push   0xa7d2121    ; '!!}\n'
12:   68 63 30 64 65   push   0x65643063   ; 'c0de'
17:   68 68 33 6c 6c   push   0x6c6c3368   ; 'h3ll'
1c:   68 6c 33 5f 53   push   0x535f336c   ; 'l3_S'
21:   68 6e 37 34 62   push   0x6234376e   ; 'n74b'
26:   68 7b 50 52 31   push   0x3152507b   ; '{PR1'
2b:   68 41 50 52 4b   push   0x4b525041   ; 'APRK'
30:   89 e1            mov    ecx,esp      ; ecx = flag
32:   ba 1d 00 00 00   mov    edx,0x1d     ; edx = len(flag)
37:   cd 80            int    0x80         ; write(STDOUT, flag, len(flag))
39:   b8 01 00 00 00   mov    eax,0x1      ; exit syscall
3e:   bb 00 00 00 00   mov    ebx,0x0      ; SUCCESS
43:   cd 80            int    0x80         ; exit(0)

We can also get the flag without disassembling the shellcode:

cat <<-'EOF' >flag.c
int main(void) {
    char shellcode[] = "PYj0X40PPPPPQaJP50000RX4DjzY0DOPVX500005O000PTY01VX5000B5000pPTYAAA01VX50000500t0PTYAA01VX500005Ow10PTY01A01VX5000B5000pPTYAAA01VX58H7E5NVrXPTY01A01A01VX501015qabzPVX510B05absXPVX5B1015vSXJPVX5004B5cXZuPVX504E05XXvoPVX50B445XqXXPVX5B0005rTUXPVX502005M8XSPVX500BB5aXccPVX500BB500stPTYAAA01VX5000050t10PTYA01VX500005w400PTY01Tx";

    (*(void (*)())shellcode)();
}
EOF
gcc -m32 -z execstack -o flag flag.c  # we're returning to the shellcode which is pushed on the stack.
./flag

The final flag is APRK{PR1n74bl3_Sh3llc0de!!}

Happy Hacking!

Creased