0x04 [2020 DawgCTF] Tik Tok – 500pt
题目类型:Pwn
checksec结果:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
题目分析
题目除了给定了一个二进制文件,同时给定了需要使用的libc以及一个songs的压缩包,解压后内部是若干文本文件。
我们为了方便读取这些文件,我们给与这些文件最高权限
程序逻辑
由于程序保留了一定的符号,这方便我们分析功能,程序有以下功能:
- 程序中的数据结构为:
struct Songs{ char path[0x18]; _DWORD *fd; _DWORD *padding; _QWORD *dir; _QWORD *name; _QWORD *play_content; }
Import_song
:首先执行ls -R
,遍历当前目录下的文件,然后用户输入一个<dir>/<file_name>.<ex_type>
的路径保存在songs[song_count].path
里,尝试打开那个路径所代表的文件,将文件句柄放在songs[song_count].fd
里。接着若以下条件均满足则继续运行,否则程序退出:songs[song_count].fd
不为-1
(文件打开成功)songs[song_count].path[0]
为可读字符(一定程度防止读非法文件)songs[song_count].path
中不存在flag
或../
(防止读flag
文件以及防止跨目录读文件)
将
<dir>
保存在songs[song_count].dir
里,将<file_name>
保存在songs[song_count].file_name
里,song_count
增加1
。-
Show_playlist
:遍历整个songs
数组,若songs[i].dir
不为空,则打印i+1
、songs[song_count].dir
、songs[song_count].file_name
。 -
Play_song
:读取一个index
,检查index - 1
是否小于song_count
,index - 1
是否大于0
,songs[index-1].dir
是否为空。接着,若songs[index-1].play_content
为空,从songs[song_count].fd
读取四个字节,转换成long
型数值作为size
,然后分配一个size + 1
大小的chunk
放在songs[index-1].play_content
中,接着从songs[song_count].fd
读取size
个字节存入chunk
中,打印存入的内容,返回。 -
Remove_song
:读取一个index
,检查index - 1
是否小于song_count
,index - 1
是否大于0
,songs[index-1].dir
是否为空。接着清空songs[index-1].dir
、songs[index-1].file_name
,释放songs[index-1].play_content
,指针置零,清空songs[song_count].path
,关闭songs[song_count].fd
并将该位置置空。
漏洞分析
- 首先,我们遇到的最大困难就是向
chunk
的写入看似不可控,因此我们要想办法让某个song
的文件描述符为0
,这样我们就可以通过stdin
向其中写入数据了。 - 在
play_song
功能中,程序没有对size
进行任何检查且其为无符号变量,这将会导致堆溢出,若我们能控制size
为-1
,将会调用malloc(0)
,且将允许向返回的Chunk
写入极大量的数据!
漏洞利用
创建可控Chunk
利用open
函数的漏洞,对于open
函数,我们可以输入一个目录,它也将会成功的打开而不会返回失败,且这个目录允许添加多个/
。
此处存在:
path
成员和fd
成员直接相邻,且在向path
成员写值时存在逻辑,当且仅当path
成员的末尾是\n
时,才会将其替换成\x00
否则不作任何操作!- 程序限定
song_count
的上限是0x31
,而fd
处存放的是文件描述符,而这个文件描述符将会由3
开始递增,当其递增到0x2E
时,若path
中没有.
,将会把这个文件描述符替换成NULL。
于是我们的利用脚本如下:
for i in range(3,0x2F):
import_song(sh,'Animal/'.ljust(0x18,'/'))
⚠️:此处还有一点需要注意!正如我们在程序逻辑中说明的那样,一旦我们对这个歌曲执行了释放操作,程序将关闭fd指针处的文件流,而在此处,这意味着stdin
将会被关闭,因此我们必须防止其被释放!
0 条评论