从2013年开始举办的Flare-On逆向挑战赛今年已经是第6届。今年的比赛共有12道题目,涉及Windows,.NET,Linux和Android上x86的各种架构。此外,这也是Flare-On历史上的第一次比赛中出现NES ROM的题目。该比赛是仅有的以Windows为中心的CTF竞赛之一。完成Flare-On逆向挑战也是笔者一直以来的目标之一,今年终于有时间去实现这个目标了。
help题目是今年flare-on challenge的最后一道压轴题目,因此相较于传统的题目而言没有更加明确的导向,开放性较高,需要研究者自己根据经验去分析推断并最终解决问题。下载解压缩得到一个dump文件和一个pcap文件,windbg加载一下这个dump文件并执行!analyze -v指令可以得到最后的崩溃信息。
分析时需要注意的是,这里的程序和库都是64位的,所以传参数的时候大多数的参数都是通过寄存器传的,而不是通过栈传递的参数。并且,Windows下面和Linux下面还不太一样,传递时没有通过RDI、RSI这两个寄存器。第一个参数保存在RCX当中,第二个参数保存在RDX当中,随后是R8、R9寄存器,再之后才是栈。
首先看下这个pcap文件,内容比较的多。简单查看以下http传输的内容,是对flare-on.com官网的访问数据流,并没有特别引人注意的地方。
所以需要通过分析dump文件看看这个特别大的数据包到底应该怎么去使用,合理的过滤留下有用的数据。Windbg加载完成了之后发现程序崩溃在man.sys这个加载的内核驱动当中,并且崩溃的位置在man + 0x1ce7的位置。因此,现在要做的就是想办法dump下来这个sys文件然后看看当中到底是什么导致了崩溃。dump文件有两种方法,一种是直接在windbg当中用writemem命令转储0xf000长度的内存。
.writemem E:\test 0xfffff880033bc000 L0xf000
另外一种是使用之前在隐写术当中经常出现的volatility工具(https://github.com/volatilityfoundation/volatility),这个工具是专门用于分析windows的dump文件的。首先,通过它的modules方法查看系统加载了哪些模块
./volatility_2.6_lin64_standalone -f ~/Desktop/help.dmp --profile=Win7SP1x64 modules
会发现其中存在一个奇怪的文件,也就是我们之前在windbg当中看到的引起崩溃的地方。同时也能看到这个文件所在的路径,以及系统登录使用的用户名(FLARE ON 2019)。有一点需要注意的是,使用volatility时必须设置的一个参数是profile,它需要知道你的memorydump文件来自哪个版本的操作系统,不同的操作系统版本有不同的memorydump存储结构,如果选择错了profile会导致netscan等命令无法使用。这里正确的profile应该是Win7SP1x64。
通过memdump转储sys文件。转储完成之后会发现这并不是一个正常的sys文件,因为这个文件缺少了正常window下文件的头,它的头部全都是\x00。当时分析到这里的时候一下就断了思路,不知道下一步应该做什么了。后来第二天重新再看的时候发现这里转储出来的文件中间存在另一个PE的头,将PE之后的部分提取出来会发现是一个dll文件。
尝试调试分析这个dll文件,看看能不能给我们提供一些有用的线索。关于dll文件的动态调试,可以参考文章 https://disassemble.blog/2018/07/13/debugging-a-dll-in-x64dbg-and-sync-with-ida/主要看看这个导出函数c当中完成了一些什么,函数解密栈上的数据获得想要调用的WindowsAPI名称,解密函数是sub_180001150。这个函数是一个典型的R解密函数,R加解密的过程相同,因此可以解密保存的函数名。执行的流程主要是通过listen监听4444端口发送的数据,之后调用CreateThread创建新的线程去执行sub_18002bd0函数。
这个函数当中调用recv接收数据,VirtualAlloc新建了0x3000字节大小的堆块。recv的过程首先接受了4个字节的数据包长度,然后接受相应长度的数据,根据获得的数据内容去调用DeviceIoControl。分析到这里,我们知道了程序通过4444端口和外部进行通信。但是打开pcap包发现通信的内容似乎和dll当中描述的不太一样,似乎通信数据进行了一定的加密。再次陷入僵局。
在volatility查看程序的链接情况时,发现除了4444端口之外,在6666,7777,8888端口也有已经建立的链接。并且建立的链接的程序都是pid位876的svchost.exe程序。
我们可以通过vaddump命令dump出pid为876程序加载的所有使用的内存页,会dump出一系列的内容,内容比较的多。
./volatility_2.6_lin64_standalone -f ~/Desktop/help.dmp --profile=Win7SP1x64 vaddump -D out -p 876
在dump出来的所有文件中用grep查找包含dropbox字符串的内存页,会找到7个对应的文件,修复文件头就能够得到这样的几个文件,2个sys文件以及5个dll文件。对这样的几个文件进行分析,发现其中stmedit.sys是一个在内核当中对网络流量进行HOOK并修改的驱动,主要做的是对流量进行亦或加密。
其他的几个dll的功能如下:
c.dll 在流量发送前进行加密 n.dll 负责网络链接,连接192.168.1.243主机传输数据 k.dll keylogger进行键盘记录 s.dll screenshot抓取系统截屏 f.dll 传输文件
所以这里的dll传输的不同内容,对应的端口也应该不一样。keylogger、screenshot、file等,对应的应该是6666,7777,8888三个端口当中的一个。解密数据包的内容应该就能看到程序到底从被害者的机器当中偷走了什么东西。在wireshark当中应用如下的过滤规则可以看到每个端口传输的数据内容,4444是在受害者主机上开启的端口,接收别人传输的内容,6666,7777,8888是连接的攻击者机器上的端口。
tcp.dstport == 4444 and tcp.len > 0
关于4444端口和8888端口,之前说过这两个端口的内容都是亦或加密过的,但并不清楚亦或使用的key的内容。这里比较有意思的一点是可以通过数据包猜到这个使用的key。
比如4444段口的流量当中重复出现了5df34a484848dd23这个字节流,把这8个字节作为亦或使用的key去解密4444的数据包,发现4444端口当中传输了一个PE文件。同理用4A1F4B1CB0D825C7作为key去解密7777端口传输的数据,能够得到一个bmp文件,这个文件是传输的受害者主机上的截图文件。这个截图文件比较的关键,可以看到master password的长度是18位。
6666端口和8888端口数据亦或的key的内容更加容易得到,不知道为什么传输的时候既传送了一个亦或加密之后的数据包,又传输了一个没有亦或加密的数据包。因此直接将这两个数据包传输的data内容直接亦或就能够得到亦或使用的key。
但是不同的是这里两个端口传输的数据内容是经过c.dll当中函数加密过的,获得真正传输内容还需要解密一下。加密的过程在c.dll当中的e函数里,可以看到加密是调用了windows的RtlCompressBuffer的LZNT1压缩函数之后又调用了R**加密函数加密了一次,加密使用的key是用户的用户名,并且因为GetUsername的特性用户名之后还多加了一个\0,为’FLARE ON 2019\0’。
因此解密的过程如下XORkey→R**→Decompress。对8888端口的解密,解密出一些有意思的东西,如下图所示。这个keylogger记录的是当前使用程序的标题和键盘的输入,看起来输入的内容是th1sisth33nd111,当前正在使用的程序是KeePass。但是这个输入的内容和之前解密出来图片中密码长度不符,这里只有15个字节,而之前的图片中显示密码的长度应该是18个字节。
所以这里还有一些问题,问题出现在keylogger的实现当中,题目也说了这个木马文件的实现是有问题的。当我们查看keylogger实现时可以发现其逻辑存在问题。当按下shift + 1的时候,也就是我们输入的是一个!的时候,会被记录成1,输入为#的时候会被记录成3。输入 或者.的时候不会被记录下来。因此猜测缺少的3个字符应该是等特殊字符。
我们将6666端口得到的数据流量解密,得到一个keepass的数据库文件,可以用keepass2john将其转换成为john the ripper的hash格式,然后下载john并用john的MASK功能生成wordlist进行爆破,爆破出剩下字符的位置和内容。
[user]~$ git clone https://github.com/piyushcse29/john-the-ripper.git
[user]~$ cd john-the-ripper/src
[user]~$ make
[user]~$ make clean generic
[user]~$ cd ../run
./john --mask="[tT][hH][\!1][sS]_[iI][sS]_[tT][hH][3#]_[3#][nN][dD][\!1][\!1][\!1]" -stdout >> wordlist
./john --format:KeePass --wordlist=/home/lowkey/Desktop/JohnTheRipper-bleeding-jumbo/run/wordlist ~/Desktop/kp
得到passwd Th!s_iS_th3_3Nd!!!。打开keepass的数据库文件就能够得到flag了。
*本文原创作者:Kriston,本文属于FreeBuf原创奖励计划,未经许可禁止转载