UAF—metasequoia_2020_summon

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

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

查看权限

UAF—metasequoia_2020_summon_第3张图片
都开了。

F5分析二进制文件


只要修炼到5级,就能get shell。
UAF—metasequoia_2020_summon_第4张图片

解题思路

UAF—metasequoia_2020_summon_第5张图片
为v7申请了0x10大小空间。
**v7是2维指针。*v7是**v7的取值,也是首元素。使用strdup函数令其指向nptr所指向的空间。
UAF—metasequoia_2020_summon_第6张图片
关于strdup的说明:
UAF—metasequoia_2020_summon_第7张图片
strdup是为*v7 malloc一块空间的。*v7指向了创建的生物名字。UAF—metasequoia_2020_summon_第8张图片

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

UAF—metasequoia_2020_summon_第9张图片
在这里插入图片描述
从图中可以看见,一共有两个块,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。
我们实验一下上面假设:(因为地址随机化,地址和上面的有出入,但最后两位都是一样的,地址内的值是对的,不影响分析。)

UAF—metasequoia_2020_summon_第10张图片
可以看到,由于名字过长,f048的位置已经成功溢出。在源码中,只free了名字,没有free等级。

UAF—metasequoia_2020_summon_第11张图片

选择release试试:

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

这道题的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()

大功告成

在这里插入图片描述
UAF—metasequoia_2020_summon_第14张图片

你可能感兴趣的:(堆学习,pwn,python,ubuntu)