[翻译]The Blue Frost安全挑战题解
附件为题目以及作者的解题过程。
在平时博客里发的软件动态之外,我这次要聊一聊Blue Frost Security的娱乐挑战题的解题思路。在8月2号,Blue Frost Secucrity发布了一篇关于提供位于阿根廷的Ekoparty安全会议门票的博文。为了获得门票(还有免费的威士忌),你需要完成一系列的exploit挑战题并发送解题思路给他们。他们提供了一个64位,有着简单堆栈缓冲区溢出的二进制PE文件,目标是在Windows7,Windows8.1或Windows10中成功执行calc.exe文件。同时还有额外挑战——在三个系统中都能成功利用函数执行,或是在利用后进一步令服务处于可用状态。
通过我会错过这些消息,不过我的同事Abdul-Aziz Hariri私下激起了我解题的欲望。最终我是第四个成功解题的人,尽管我没有进一步分析,但下面是我解题中的一些思路。
通常做这些东西对我来说是十分有趣的事儿,我之前玩过战争游戏并参加过CTF,但那是一段时间前的事了,因此做的时候能够回忆起不少美好的记忆。当你看到二进制文件时,会感到这是个明显的缓冲溢出题,但需要仔细查看整个代码才能成功的解出它。
在接收到新的连接后,就会读取最大4096字节的数据储存到栈的缓存区中。

尽管调用的 recv函数没有返回错误,但它仍然调用了一个用于接受数字字节和对象缓冲区的函数。

这个函数内置了Hello字符串的比较,然而这里有一个小问题。调用的strncmp长度是Hello加上空白终结符。我们输入的字节长度并没有真正的进行检查,也就是说只要我们在最初调用的recv中发送Hello以及空白符后,还可以在栈中最多存放4090任意字节的数据。

一旦我们通过字符串对比的检查,程序流就会在进入下一个新函数前返回到主函数中。

这个函数先读取客户端发来地四个字节,如果这四个字节有错误或少于四个字节,那么就会给出一个错误路径并提前退出函数。

当我们执行到这里的时候,剩下的两个字节会以无符号的16位整数进行处理,这些整数会分配到一个尽可能读取字节数的缓冲区中。memcpy函数将整个缓冲区储存在栈中,再清空堆缓冲区。

如果我们观察写入的栈缓存区,我们可以发现它的缓存是256字节。

尽管我们可以对栈进行写的操作,但在溢出之前还有一个cookie栈保护,因此我们需要进一步研究如何绕过这一保护机制。
让我们来看看下一部分,这里有一些奇怪地东西。它在函数开头读取接收到的四个字节中的前两个和0xff,再作为索引连接上一个单独的字母A,在保存这一值后成功被调用。

现在我们成功地调用了0x4141414141414141,现在看起来并没有什么用的异常处理,因此我们需要进一步展开分析。
如果我们仔细观察字符串,可以发现两点值得注意的地方:首先你看到的部分基于你使用的反编译器,我使用的是IDA。其次我们需要做些东西才能看见这部分。这里有一个指向有着2048字节缓冲区,IDA标为system函数的指针。然而这是一个忽悠人的地方。0xff的索引只能提供2040字节让我们输入到缓冲区中,所以我们仍被困在调用0x4141414141414141函数的地方。
为了帮助阅读,IDA无意间隐藏了一部分我们需要的内容,在第一部分的字符串816字节位置的“A”字母后还有一个NULL终结符。如果我们从字符串中移除这个NULL,将其作为包含周围字节的QWORD,那么就能看见这是一个指向函数的指针。


在缓冲区的0x330字节处,可以看到这个隐藏的函数。通过0x66索引的调用该函数能够令我们作为一个单独对象溢出栈缓冲区。
函数中并没有多少能用到的东西,它从输入数据中通过调用 IsBadReadPtr函数读取QWORD,并返回结果。这正是我们想要的,IsBadReadPtr函数用于处理非法指针,绕过它便可以执行我们传递的任意值。

我们成功地调用了函数并几乎执行到了函数地尾部,但我们还需要找到泄露返回数据的方法。好在有一个send函数能够将终点栈的缓冲区内容发送给我们,它能够发送比接收到的数字多一位的字节,也就是说,我们可以获取栈中的单独一位字节。

表面上看这可能没什么用,但它其实是个单线程的网络服务。如果我们能够发送多个请求,那么它们就能在同一个栈地址产生多个相同的cookie值。在我们的恶意客户端中,可以发送256字节的数据。通过读取第一个栈的cookie值并加载在缓存器中,再发送257字节来读取第二个栈的cookie,我们可以反复重复这个操作来读取我们想要的字节数。
由于我们能够读取栈中的数值,因此我们可以获取栈中的指针并用它来计算二进制镜像和栈指针的值。之后我们可以通过堆溢出在不修改栈cookie的情况下修改返回指针从而调用我们想要的函数。
在执行计算器时我们有好几个方案,可以通过镜像文件的基地址来计算系统函数的偏移量,也可以通过之前获取的4096字节来帮助完成这一目标。在我提交的exploit中,我使用了缓冲区溢出来提取添加了0x88到RSP的ROP,并返回了4096字节的缓冲区,从这里我找到了漏洞函数,使用动态指针来调用系统中的“calc.exe\x00”的对象。
我的老板Brian对我只实现了代码执行而表示遗憾,因此我还进行了进一步的研究。通过使用4090字节的缓冲区来储存我想要的栈层内容,并通过缓冲区溢出,我成功地使用了一些ROP gadgets来调用memcpy函数。通过这一方法,实现了将我想要的栈层复制到底层地址中,从而使用POP RSP gadget来返回数据。到这一部,执行进程可以正常运行,唯一需要我具体描述的内容是主函数中我废弃的通信介绍协议,我通过信息收集阶段读取的一些QWORDs成功将它写入到了调用memcpy的函数中。
如果你对如何利用的脚本感兴趣,你可以在我发布的这里下载下来进行分析,竞赛的目标程序也可以在这里下载下来。不管最后的奖励如何,我都感到这个题目十分有趣。它还展示了如何利用一个基础进程脚本来执行一个文件。不管BFS是否提供了承诺的威士忌,看到弹出的计算器都十分地令我感到满足。
你可以在@WanderingGlitch上关注我的推特以及我的团队,从而获取最新的安全技术和安全补丁内容。
