今天看啥  ›  专栏  ›  一路开花●-●

UAF—metasequoia_2020_summon

一路开花●-●  · CSDN  ·  · 2020-01-01 00:00

距离上次发帖已经一个多月了。每当我准备撸起袖子好好学堆的时候,就会有其他乱七八糟的事插进来。。。
在这里插入图片描述
话不多说,今天练习的内容是UAF系列的summon。

在这里插入图片描述
给了你六个选项:
1.show:展示你的生物及其等级
2.summon [name]:召唤一个名字为name的生物
3. level-up [level]:提升等级,最高只能四级
4. strike:和黑暗之王开打
5. release:释放你的生物
6. quit:退出
这道题需要你和怪物打架,需要修炼到5级才能打赢。但是在这个游戏中,你只能修到4级。

查看权限

在这里插入图片描述
都开了。

F5分析二进制文件

在这里插入图片描述
只要修炼到5级,就能get shell。
在这里插入图片描述

解题思路

在这里插入图片描述
为v7申请了0x10大小空间。
**v7是2维指针。*v7是**v7的取值,也是首元素。使用strdup函数令其指向nptr所指向的空间。
在这里插入图片描述
关于strdup的说明:
在这里插入图片描述
strdup是为*v7 malloc一块空间的。*v7指向了创建的生物名字。 在这里插入图片描述

*(v7+2)指向的是生物的等级。也就是说,一个summon会malloc 两个块。
我们先创建一个生物,观察内存布局:

在这里插入图片描述
在这里插入图片描述
从图中可以看见,一共有两个块,v7的地址是1020,1020处存放的是*v7,对应着*v7=strdup(npstr)=1040,nptr是名字的地址1040。

1028处存放的是等级。在1040存放的是我们刚才输入的名字“aaaa”的ASCII码。假如这个名字足够长,能溢出到1048的位置,这时1048的位置值可以看作是等级的值。

确认v7的结构体是:

struct v7{
char *name;
int level;
}
你可能会问:*v7是name,*v7+2是level。按照上图level的位置,应该是*v7+1吧?后来我也注意到这点,在源码中找到了答案:
在这里插入图片描述
还是太粗心了,人家早就把v7转为了4字节。而1020位置只有8字节,所以*v7+2的位置是1028。
我们实验一下上面假设:(因为地址随机化,地址和上面的有出入,但最后两位都是一样的,地址内的值是对的,不影响分析。)

在这里插入图片描述
可以看到,由于名字过长,f048的位置已经成功溢出。在源码中,只free了名字,没有free等级。

在这里插入图片描述

选择release试试:

在这里插入图片描述
可以看到,free *v7之后,名字aaaaaaaa被清理,但是对应的等级5依然存在。
看到这里,有点思路了吧?把这个块free掉,再使用summon调用malloc填充名字,就能get shell了!
在这里插入图片描述

这道题的flag我是在本机的根目录下建的。

from pwn import *

#context.log_level='debug'

p=process("./summoner")
def dbg():
	gdb.attach(p)
	pause()


p.sendlineafter('>',str('summon aaaaaaaa\x05'))
p.sendlineafter('>',str('release'))
#dbg()
p.sendlineafter('>',str('summon aaaa'))
#dbg()
p.sendlineafter('>',str('strike'))
#dbg()
p.interactive()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

大功告成

在这里插入图片描述
在这里插入图片描述




原文地址:访问原文地址
快照地址: 访问文章快照