解題思路 | 從一道Pwn題說起

0×00 序

好久沒有寫文章了钾菊,最近在學(xué)習(xí)pwn,這次就分享pwnable.tw上的一道pwn的解題思路嗦篱。這篇文章主要目的并不是以做這道題為目的填物,而是以這個題為主線纹腌,我主要想講的是通過這道題霎终,我們能聯(lián)想或者學(xué)會一些其他的東西滞磺,所以叫做從一道pwn題說起。如果有什么不對的地方莱褒,歡迎大家指出击困。

0×01 題目分析

題目本身難度并不大,正好適合剛接觸堆的選手練習(xí)广凸,快速掌握堆利用的知識和技巧阅茶,下面我們開始分析,首先運行程序谅海,如下圖所示脸哀,這是一道菜單題目,可以添加扭吁,刪除和打印節(jié)點撞蜂,這應(yīng)該是一個堆上的pwn題盲镶。

然后可以使用IDA分析程序流程,首先看一下主函數(shù)蝌诡,我對其中的一些函數(shù)做了重名命溉贿,這樣可以對后面的分析更簡單,更方便浦旱,讓你更清楚程序的流程宇色。

主函數(shù)分析

主函數(shù)的主要流程很清晰,讀入你輸入的選項颁湖,然后執(zhí)行對應(yīng)的函數(shù)宣蠕,我們需要主要分析一下各個功能選項(如add,delete等)的詳細流程爷狈。

Add函數(shù)分析

在分析Add函數(shù)時植影,這里我分析出來一個結(jié)構(gòu)體,創(chuàng)建一些結(jié)構(gòu)體對程序的數(shù)據(jù)結(jié)構(gòu)進行描述涎永,對你分析程序有很大的幫助思币。

00000000 st struc ; (sizeof=0x8, mappedto_5) 00000000 func dd ? 00000004 msg_ptr dd ? 00000008 st ends

Add函數(shù)首先會判斷num的值已經(jīng)申請的結(jié)構(gòu)體數(shù)量,如果小于等于5就遍歷存儲結(jié)構(gòu)體指針的數(shù)組羡微,找到一個為空的項谷饿,然后申請一個8字節(jié)大小的堆塊,然后將sub_804862B賦值給func也就是結(jié)構(gòu)體的第一個成員妈倔,然后在讀入一個size申請一個size大小的堆塊博投,將其賦值給msg_ptr指針,然后讀入一個字符串到msg_ptr (新申請的堆塊),最后將結(jié)構(gòu)體數(shù)量加1

Delete函數(shù)分析

相對于Add盯蝴,delete函數(shù)的流程就簡單的多了毅哗,讀入要刪除index,然后判斷index是否合法捧挺,然后判斷這個index對應(yīng)的結(jié)構(gòu)體指針數(shù)組的是否存在虑绵,如果存在就釋放對應(yīng)的結(jié)構(gòu)體的msg_ptr和結(jié)構(gòu)體指針

print函數(shù)分析

print函數(shù)也很簡單和delete的類似,不同的是最后調(diào)用了func指針指向的函數(shù)闽烙,根據(jù)Add函數(shù)中的賦值翅睛,這個函數(shù)應(yīng)該是sub_804862B,下面我們看一下這個函數(shù)

這個函數(shù)也很簡單黑竞,就是輸入msg_ptr指向堆塊的內(nèi)容捕发。

0×02 漏洞分析

整個程序的流程上面已經(jīng)分析清楚,主要的漏洞點很魂,就在delete函數(shù)中扎酷,在釋放指針之后沒有將其賦值為空,這樣會引起UAF和double free 漏洞遏匆》òぃ可能對新手來說骤铃,對堆上的漏洞很陌生,我這里簡單的介紹一下坷剧,如果想要詳細了解可以閱讀文章最后的參考資料惰爬,要搞懂這些首先需要對堆的結(jié)構(gòu)有一定了解,這些網(wǎng)上有很多文章惫企。

UAF(use after free)釋放重用漏洞撕瞧,漏洞原理,釋放后的指針沒有賦值為空狞尔,在其他地方再次申請到這塊內(nèi)存并改變其的內(nèi)容丛版,而再次使用到之前釋放后的指針,就會造成程序的結(jié)果變得不正確偏序。如果這個釋放的指針中有函數(shù)指針等重要數(shù)據(jù)页畦,同時在其他的地方修改成精心構(gòu)造的數(shù)據(jù),就可能泄露數(shù)據(jù)研儒,甚至劫持控制流豫缨。

double free 雙重釋放漏洞,漏洞原理端朵,對釋放的堆塊再次進行釋放好芭,當(dāng)然連續(xù)釋放一塊堆塊,libc中有檢查冲呢,這個是報double free的錯舍败,但是中間釋放一個其他堆塊,程序不會報錯崩潰敬拓,這樣就將double free轉(zhuǎn)化成uaf邻薯,因為第一塊和第三塊指向同一個地址公用一塊內(nèi)存,同樣可以構(gòu)造特殊的數(shù)據(jù)完成利用乘凸。

下面我們首先明確一下自己的目標厕诡,通過漏洞拿到shell,完成這個目標我們需要什么條件呢

system地址 劫持控制流/bin/sh等字符串 堆結(jié)構(gòu)

在講我們怎么獲取這些信息之前翰意,我先來將一些堆結(jié)構(gòu)基礎(chǔ)知識木人,如果您已經(jīng)掌握信柿,請?zhí)^此處冀偶。在linux 的內(nèi)存管理中,主要是通過bins數(shù)組和鏈表來管理各個堆塊的渔嚷,首先他們分為fast bin ,small bin ,large bin, unsorted bin进鸠,這里我主要講一下fast bin和small bin,因為篇幅有限形病,這里沒一個部分都可以拿出來單獨將一篇客年,其中的細節(jié)也很多霞幅,如果自己有能力或者有興趣,強烈建議大家去閱讀glibc的源代碼量瓜,這會對你堆利用或者發(fā)現(xiàn)新的利用姿勢有很大幫助司恳,下面先介紹一下堆塊的結(jié)構(gòu)(x86平臺)

其中fd和bk只有釋放的堆塊才有效,同時NMP是三個標志位绍傲,P代表這前一個堆塊是否被釋放扔傅,pre_size也是前一個堆塊的大小(這里的前一塊只得是連續(xù)的前一塊),堆塊頭的大小也就是8字節(jié)烫饼。

Fast bin

在glibc內(nèi)存管理中猎塞,fastbinsY 這是一個鏈表數(shù)組, 這數(shù)組的大小是10杠纵,數(shù)組的每一項都是一個單項鏈表(只使用其中的fd)荠耽,每次堆塊都是從尾部添加和摘除(后進先出),每一個鏈表里的堆塊大小一樣比藻,相鄰兩個鏈表相差8字節(jié)铝量,堆塊大小從16到80,用戶申請大小小于等于64字節(jié)银亲,這都會分配到fastbin款违,fastbin的堆塊不會發(fā)生堆塊合并,它的P位一直是1群凶。

Small bin

smallbin 屬于bins中的一部分插爹,一共有62個,和fastbin一樣是個鏈表數(shù)組请梢,數(shù)組中的每一條鏈表都是雙向鏈表赠尾,堆塊的大小小于512字節(jié),連續(xù)free的堆塊會發(fā)生堆塊合并毅弧。

本題堆分析

根據(jù)上面的介紹气嫁,我們對堆的結(jié)構(gòu)有個大概的了解,下面我們對這個題目的堆進行一個簡單的分析和調(diào)試够坐,根據(jù)上面靜態(tài)分析寸宵,我們了解到程序首先會申請一個8字節(jié)大小的堆塊來存儲函數(shù)指針和字符串指針,根據(jù)上面的知識我們知道這個堆塊是屬于fastbin的元咙,同時程序還會分配一個我們自定義大小的堆塊梯影。首先我們使用pwntools寫一個腳本,我們要add一個大小為0×50的note庶香,(pwntools是一個很好的工具甲棍,可以幫助我們快速寫出exp,堆上的操作很多都是重復(fù)的建議寫成函數(shù)赶掖,我們的gdb也可以裝peda感猛,pwndbg七扰,gef等插件來幫助我們來調(diào)試,我這里裝了pwndbg)陪白,運行腳本颈走,這里加了gdb.attach(p),我們可以很方便的程序咱士,我們附加程序后疫鹊,在malloc函數(shù)的下一個指令下斷點,這時eax寄存器里的值就是返回的分配堆地址司致,具體如下圖拆吆。

程序斷在0x0804869f,這是我們下的斷點上脂矫,我們使用x/100wx $eax-8 可以查看程序當(dāng)前分配后的堆情況包括堆塊頭的信息(8是堆塊頭的大小枣耀,系統(tǒng)分配給程序的地址是從堆塊頭之后的),具體如圖庭再。

然后我在0×08048731,下斷點捞奕,然后繼續(xù)調(diào)試,依舊使用x/100wx $eax-8 查看堆信息拄轻,這里除了大小不一樣和上面的差不多颅围,我就不具體分析,我們記錄一下當(dāng)前的堆的起始地址0x898f000恨搓,然后再add函數(shù)返回的地方0x080487D3下斷點院促,然后使用x/100wx 0x898f000 查看堆的狀態(tài),具體如下圖斧抱。

下面我們修改腳本申請和釋放幾個堆塊分析一個堆塊釋放過程常拓,熟悉了堆的結(jié)構(gòu)我們可以使用pwndbg插件的一些特殊調(diào)試命令加快我們的調(diào)試速度,比如pwndbg給我們提供heap命令可以方便的查看堆塊的分配情況辉浦,bins命令可以快速查看bins的狀態(tài)弄抬,當(dāng)然還有一些其他方便的功能。

腳本主要代碼 add(0x50,"A"*10) add(0x50,"B"*10) add(0x50,"C"*10) gdb.attach(p) delete(0) delete(1) gdb.attach(p)

運行腳本宪郊,使用heap命令可以看到當(dāng)前申請的堆塊掂恕,具體如下圖所示。

然后繼續(xù)運行弛槐,程序會斷在釋放前兩個note(note之程序中的結(jié)構(gòu))之后懊亡,這時候我們可以使用bins命令查看一個堆塊的釋放情況,具體如下圖丐黄。

結(jié)合著兩個圖斋配,我們可看到孔飒,首先我們釋放的是note0(0x9fd1000)灌闺,然后釋放的是note1(0x9fd1068),我們可以觀察到fastbins的情況艰争,這兩個都是申請8字節(jié)大小,整個堆塊的大小是16=0×10桂对,所以這兩塊會連在一起甩卓,根據(jù)釋放的順序,是從鏈表的尾部插入(這里的頭和尾是相反的)的蕉斜,而其他兩個堆塊是先放到unsortbin里暫存逾柿,以提高分配速度。這里思考一些如果我們再 add(0×8,”X”)宅此,這時程序會分配到哪里的堆塊机错。

0×03漏洞利用 system地址獲取

通過上面的分析和調(diào)試,我相信大家對堆也有了一定的了解父腕,下面我們回到之前的幾個問題弱匪,首先是泄露system地址,這個題目給了libc文件璧亮,所以我們知道leak出libc地址或者是leak一個一直函數(shù)的真實地址(比如free,malloc等等都可以)萧诫,這里我講兩種方式類獲取這個地址。

方法一:通過leak函數(shù)got表的地址獲取libc基址

首先說這個方法枝嘶,因為ELF的動態(tài)鏈接帘饶,在got.plt段會存儲真正的函數(shù)地址(在這個函數(shù)被調(diào)用之后,程序的加載過程同樣很復(fù)雜群扶,這里我們可以去閱讀《程序員的自我修養(yǎng):鏈接及刻、裝載與庫》,相信會對你有很大的幫助)竞阐,還記得上面說到的問題嗎?如果我們再 add(0×8,”X”)的時候提茁,這里叫note2吧,具體我們可以先看下面這張圖馁菜。

note2分配的地址就是(note1的頭結(jié)構(gòu)體地址)0x9fd1068茴扁,而他的字符串也是8大小,它分配的地址就是(note0的頭結(jié)構(gòu)體的地址)0x9fd1000,也就是我們可以通過輸入來控制汪疮,note0頭結(jié)構(gòu)體的值峭火,讓他完成我的leak功能,最上面的函數(shù)分析智嚷,我們知道print函數(shù)的功能就是打印字符串的值卖丸,現(xiàn)在我們可以控制字符串的值了,相當(dāng)于我們可以控制他打印的值了盏道,那么我們可以這樣如下操作稍浆。

add(0x8,p32(0x804862B)+p32(0x0804A018)) 0x0804A018這是free函數(shù)真實函數(shù)的地址,在IDA中g(shù)ot.plt段可以找到

根據(jù)堆的信息我們可以發(fā)現(xiàn)我們成功的修改了note0的字符串指針,把他修改成了之前free函數(shù)的真實地址的位置衅枫,我們在調(diào)用print(0)嫁艇,就可以將free的函數(shù)地址獲取,然后通過下面的公式就可以獲取到system函數(shù)的地址(兩個函數(shù)的相對偏移是固定的)弦撩。

libc_base = free_addr - libc.symbols['free'] system_addr = libc_base + libc.symbols['system'] 方法二:使用main_arena獲取libc基址

上面我們已經(jīng)介紹了一種leak地址的方式步咪,下面我們介紹另一種方式獲取地址的方式,那就是通過main_arena來獲取益楼,在fastbin為空時猾漫,unsortbin的fd和bk指向自身main_arena,而main_arena存儲在libc.so.6文件的.data段感凤,通過這個偏移我們就可以獲取libc的基址悯周,這里我講一下怎么找到main_arena的地址,首先使用IDA打開libc文件陪竿,然后搜索函數(shù)malloc_trim()队橙,具體如下圖所示。

為什么是這個呢萨惑,我們可以對照一下malloc.c的源代碼捐康,源代碼如下圖。

我們可以如下構(gòu)造腳本

add(0x50,"A"*10) //申請一個不是fastbin的內(nèi)存 add(0x50,"B"*10) //防止發(fā)生堆塊合并 delete(0) add(0x50,"") //note0的頭已經(jīng)被破壞了它的(func位置)也就是fd位置會為0庸蔼,所以我們要再申請同樣大小的解总,才能正確的調(diào)用print gdb.attach(p)

根據(jù)上面的調(diào)試的結(jié)果,我們可以計算libc_base和system_addr姐仅,具體如下

libc_base = leak_addr - (main_arena+48) system_addr = libc_base + libc.symbols['system'] 劫持控制流和/bin/sh字符串

至此我們獲取了system的地址了花枫,我們看一下怎么劫持控制流,這個還是比較明顯的掏膏,上面獲取地址的時候劳翰,我們已經(jīng)可以修改func指針了,我們可以把它寫成我們獲取的system地址馒疹。

((void (__cdecl *)(st *))ptr[v1]->func)(ptr[v1]);

我們再來仔細分析一下print這個函數(shù)佳簸,主要就是上面這一行代碼,調(diào)用這個函數(shù)同時颖变,將這個頭結(jié)構(gòu)體的地址當(dāng)作參數(shù)傳遞給函數(shù)生均,但是我們這個結(jié)構(gòu)體開頭的是這個函數(shù)地址,system執(zhí)行到這里會報錯腥刹,找不這個指令马胧,我們跳過這個4個字節(jié)的函數(shù)指針,我們加一個 “;” 就可以寫下一條指令了衔峰,但是這里會有一個問題佩脊,那就是我們空間有限蛙粘,除了前四個字節(jié)之外還有四個字節(jié),在去除”;”就剩三個字節(jié)威彰,想寫入”/bin/sh”這是不可能的出牧,這里有兩個技巧可以解決這個問題,具體如下:

system("$0"); system("sh");

這兩種方法都可以啟動shell抱冷,最后exp構(gòu)造如下:

add(0x8,p32(system_addr)+p32(";$0\x00")) 或 add(0x8,p32(system_addr)+p32(";sh\x00"))

0×04 總結(jié)

經(jīng)過這道題目崔列,我們對堆更加的了解梢褐,一道簡單的題目也可以讓我們學(xué)到很多旺遮,同時體會到調(diào)試的重要性,親自動手去調(diào)試和只想不動手學(xué)到的知識和深度是不同的盈咳,只有親自動手才能加深自己的印象和理解的也會更深耿眉,強烈建議大家跟著我的流程調(diào)試一下,只看這個文章鱼响,不動手是不夠的鸣剪。

來自freebuf

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市丈积,隨后出現(xiàn)的幾起案子筐骇,更是在濱河造成了極大的恐慌,老刑警劉巖江滨,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铛纬,死亡現(xiàn)場離奇詭異,居然都是意外死亡唬滑,警方通過查閱死者的電腦和手機告唆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晶密,“玉大人擒悬,你說我怎么就攤上這事〉炯瑁” “怎么了懂牧?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長尊勿。 經(jīng)常有香客問我归苍,道長,這世上最難降的妖魔是什么运怖? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任拼弃,我火速辦了婚禮,結(jié)果婚禮上摇展,老公的妹妹穿的比我還像新娘吻氧。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布盯孙。 她就那樣靜靜地躺著鲁森,像睡著了一般。 火紅的嫁衣襯著肌膚如雪振惰。 梳的紋絲不亂的頭發(fā)上歌溉,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天,我揣著相機與錄音骑晶,去河邊找鬼痛垛。 笑死,一個胖子當(dāng)著我的面吹牛桶蛔,可吹牛的內(nèi)容都是我干的匙头。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼仔雷,長吁一口氣:“原來是場噩夢啊……” “哼蹂析!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起碟婆,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤电抚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后竖共,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝙叛,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年肘迎,在試婚紗的時候發(fā)現(xiàn)自己被綠了甥温。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡妓布,死狀恐怖姻蚓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情匣沼,我是刑警寧澤狰挡,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站释涛,受9級特大地震影響加叁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜唇撬,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一它匕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧窖认,春花似錦豫柬、人聲如沸告希。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽燕偶。三九已至,卻和暖如春础嫡,著一層夾襖步出監(jiān)牢的瞬間指么,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工榴鼎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留伯诬,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓檬贰,卻偏偏與公主長得像姑廉,于是被迫代替她去往敵國和親缺亮。 傳聞我的和親對象是個殘疾皇子翁涤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 0x00漏洞簡介 uaf漏洞產(chǎn)生的主要原因是釋放了一個堆塊后,并沒有將該指針置為NULL萌踱,這樣導(dǎo)致該指針處于懸空的...
    BJChangAn閱讀 1,181評論 0 0
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,111評論 1 32
  • 一葵礼、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡單分配策略的問題地址空間不隔離內(nèi)存使用效率低程序運行的地址不確定 關(guān)于...
    SeanCST閱讀 7,819評論 0 27
  • 0x00 前言 Pwn弱雞,比賽劃水并鸵,只好跟著大佬的博客刷刷一些題目才能維持尊嚴鸳粉,在刷題目的時候又發(fā)現(xiàn)了一些新姿勢...
    Fish_o0O閱讀 1,541評論 0 6
  • 最全的iOS面試題及答案 iOS面試小貼士 ———————————————回答好下面的足夠了-----------...
    zweic閱讀 2,704評論 0 73