BugkuCTF-PWN题pwn3-read_note超详细讲解
知識(shí)點(diǎn)
puts()的特性 , puts()會(huì)一直輸出某地址的數(shù)據(jù),直到遇到 \x00
Canary最低位為\x00(截?cái)喾?
\x 和 0x 的區(qū)別:
區(qū)別不大,都是把數(shù)按16進(jìn)制輸出。
1、0x 表示整型數(shù)值 (十六進(jìn)制)
char c = 0x42; 表示的是一個(gè)數(shù)值(字母B的ASCII碼66),可以認(rèn)為等價(jià)于: int c = 0x42;
2、\x42用于字符表達(dá),或者字符串表達(dá)
char c = ‘\x42’; 亦等價(jià)于: char c = 0x42;
char* s = “\x41\x42”; //表示字符串:AB
程序編譯的時(shí)候入口并不是main函數(shù),而是start代碼段。事實(shí)上,start代碼段還會(huì)調(diào)用__libc_start_main來(lái)做一些初始化工作,最后調(diào)用main函數(shù)并在main函數(shù)結(jié)束后做一些處理。
解題流程
先運(yùn)行一下看看
一開(kāi)始會(huì)讓你輸入一個(gè)路徑,不存在的話就會(huì)報(bào)錯(cuò)退出,然后打印出文件內(nèi)容,然后分別輸入note的長(zhǎng)度和內(nèi)容,輸入內(nèi)容的長(zhǎng)度取決于之前的note長(zhǎng)度;如果實(shí)際輸入的長(zhǎng)度不是624,則再輸入一遍
查看保護(hù)機(jī)制
發(fā)現(xiàn)除了RELRO,其他保護(hù)機(jī)制全開(kāi)
此題難點(diǎn):
第一要讀懂題目找出漏洞
第二是要繞過(guò)各類保護(hù)機(jī)制
第三是exp的編寫(xiě)調(diào)試。
第一要讀懂題目找出漏洞
首先分析程序,重要部分如下所示。一開(kāi)始會(huì)讓你輸入一個(gè)路徑,不存在的話就會(huì)報(bào)錯(cuò)退出,然后打印出文件內(nèi)容;以上均可以忽略,沒(méi)有任何用處。然后分別輸入note的長(zhǎng)度和內(nèi)容,輸入內(nèi)容的長(zhǎng)度取決于之前的note長(zhǎng)度;如果實(shí)際輸入的長(zhǎng)度不是624,則再輸入一遍,此時(shí)輸入內(nèi)容的長(zhǎng)度變?yōu)?x270(624)。可以發(fā)現(xiàn),v4的長(zhǎng)度為0x258(600),而輸入的長(zhǎng)度可以任意控制,因此存在棧溢出。
第二是要繞過(guò)各類保護(hù)機(jī)制
確定了漏洞所在,下一個(gè)問(wèn)題就是如何繞過(guò)NX、Canary、PIE和ASLR等保護(hù)機(jī)制了,下面一個(gè)個(gè)來(lái)說(shuō)。
1.NX很簡(jiǎn)單,ROP即可。
2.Canary會(huì)很大程度上妨礙棧溢出,但結(jié)合本題的環(huán)境,輸入一次后緊接著一個(gè)puts函數(shù)將輸入內(nèi)容打印出來(lái),然后還有一次輸入的機(jī)會(huì),因此可以在第一次輸入時(shí),通過(guò)覆蓋Canary最低位的\x00為其他值(Canary最低位肯定為\x00,而puts()會(huì)一直輸出直到碰見(jiàn)\x00位置),讓puts()泄露出Canary的內(nèi)容,第二次輸入時(shí)再將正確的Canary寫(xiě)回去,就可以繞過(guò)Canary的保護(hù)了。
3.PIE會(huì)讓程序加載的基地址隨機(jī)化,但是隨機(jī)化并不完全,最低三位是不會(huì)改變的,可以利用這個(gè)特性,通過(guò)覆蓋最低的兩位來(lái)有限的修改程序控制流,然后再泄露出程序加載地址。
4.至于ASLR,利用ret2libc的方法,泄露出libc的版本,就可以算出system等函數(shù)的地址然后get shell了。
第三是exp的編寫(xiě)調(diào)試
通過(guò)以上的分析,由于需要泄露三個(gè)內(nèi)容,因此main函數(shù)需要執(zhí)行四次,每次執(zhí)行都會(huì)有兩次輸入,每次執(zhí)行的工作分別如下:
第一次執(zhí)行
需要在填滿v4的長(zhǎng)度600后,再溢出兩個(gè)十六進(jìn)制位(64位計(jì)算機(jī)一個(gè)地址是8個(gè)字節(jié)),覆蓋Canary的最低位,然后將Canary打印出來(lái);再次輸入時(shí),將正確的Canary放在原來(lái)的位置,然后溢出棧上返回地址的低兩位為\x20。為什么是\x20,是因?yàn)閺膇da里可以發(fā)現(xiàn),vul函數(shù)最后retn的地址為0xd1f,main函數(shù)的起始地址是0xd20,前面的偏移都是相同的,因此可以通過(guò)這類方法繞過(guò)PIE再跳回main。
如圖D2E為main的返回地址
第二次執(zhí)行
在填滿600的基礎(chǔ)上,需要再多溢出兩個(gè)地址位數(shù)(64位計(jì)算機(jī)一個(gè)地址是8個(gè)字節(jié)),也就是616。從棧分布可以看出,v4之后是Canary(0x7fffffffde88處),然后再填充一個(gè)地址位,就可以輸出main+14的真實(shí)地址,也就可以得到程序加載的地址。之后使用跟之前同樣的方法,再次回到main函數(shù)的起始位置。
第三次執(zhí)行
要溢出的就是__libc_start_main的真實(shí)地址了,作為main函數(shù)的返回地址,從上圖可以看出,可以從棧上泄露__libc_start_main+240的地址,依次利用LibcSearcher算出libc版本,然后得到system地址和/bin/sh字符串的地址。此時(shí)已經(jīng)具備了get shell的條件,但由于第二次輸入的長(zhǎng)度所限,因此還要再跳回main函數(shù),再次執(zhí)行程序。
第四次執(zhí)行
將payload拼接好,然后發(fā)給程序了。由于是64位,傳參需要rdi。通過(guò)ROPgadget搜索程序二進(jìn)制,發(fā)現(xiàn)存在pop rdi;ret;的gadget,將其偏移再加上第二步得到的程序加載基地址,就可以得到gadget的真實(shí)地址,至此,payload拼接完成,可以拿到shell了。
exp:
運(yùn)行結(jié)果
由于本題有bug,建立連接后直接輸入flag,出現(xiàn)flag
總結(jié)
以上是生活随笔為你收集整理的BugkuCTF-PWN题pwn3-read_note超详细讲解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 教你做前端表单文本框必填
- 下一篇: C语言 文件读写 EOF - C语言零基