一、背景
Tor(The Oninon Router)提供一个匿名交流网络平台,它使得用户在浏览网页或访问其它网络服务时不会被跟踪。作为该网络的一部分即所谓的“暗网”(darknet),是指只能通过Tor网络访问的服务器群组,这些服务器提供包括社区论坛、电子邮件等多种服务。虽然提供这些服务都是无恶意的,初衷是用来关注侵犯人权问题,但是由于匿名的原因吸引了很多有犯罪意图的人,比如传播儿童色情。事后执法部门也不能追踪到犯罪者的源IP地址。
2013年,在“暗网”服务器上发现了一款恶意软件,它利用特定Web浏览器上的安全漏洞在用户电脑上执行一段代码。该代码收集一些用户信息,发往弗吉尼亚州的服务器,之后自毁。就恶意软件的特征来讲,它没有明显的恶意意图。初步推断是FBI植入,他们在弗吉尼亚州有办事处,曾经也派专人开发过恶意程序,可能是他们创建了它————现在看来是真的。
二、对Shellcode的逆向分析
1、漏洞利用
漏洞利用代码用javascript编写,利用一个出名的firefox浏览器的特定版本(Tor网络预装的firefox)的漏洞。该漏洞利用代码经过了模糊处理,但通过快速扫描可以看到一长串十六进制字符,这些字符是shellcode的前几个标志性字节,即call操作码(shellcode经常以一个jump或call开始,因此知道了这些操作码使得识别shellcode变得容易起来)。
我不会在这里分析这个漏洞,但是要看一下这个shellcode。
首先,让我们用一些基本的shellcode原则设定一个场景。
2、位置无关代码
Shellcode的成功执行经过了独一无二的挑战,因为它是直接注入到一个进程而不是由Windows加载器加载。因此,我们不知道shellcode位于内存中的什么地方,更关键的是,不知道标准的windowsAPI函数位于什么地方(而通常情况下,Windows加载器会告诉一个程序这些信息)。
因此,我们必须使用一系列技巧去获得这个信息。FBI恶意程序用了一个常用的技巧去找出它的内存地址:
callstart
start:
pop ebp
call函数将执行程序移动到start标签处,同时将程序当前执行的位置压入堆栈中(这样我们稍后可以从call函数返回)。
在此,我们将要用这个值,从栈里窃取位置,堆栈弹出的值保存到ebp寄存器。我们现在知道了下一条指令的地址,那么就可以用它来访问跟我们的shellcode相关的数据。
三、定位Windows API
因为正常情况下Windows加载器会加载Windows API的位置到我们的程序,所以我们不奢望运行一个shellcode时能够知道程序的位置信息。找出API函数惯用的方法是查看FS段寄存器所指的线程信息块(thread information block)。我们能够通过这个结构体去定位被我们shellcode宿主程序加载的DLLs,然后利用这些DLL的输出直到找到目标函数为止。当然这个过程是很乏味的,所以,FBI这个shellcode使用了一个库,包含在MetasploitFramwork内,这是由Stephen Fewer写的一个函数解析器。它的工作原理如下:
push arguments
. . .
push FUNCTIONHASH
call <Stephen’sResolver>
这个函数的哈希值是由一个简单的hash算法生成,以我们调用函数的名称命名。这样做的目的不是为了混淆代码(尽管它达到这个目的),但只允许我们用一个32位的DWORD来调用此函数,而不是一个长型字符串(shellcode空间往往是有限的)。 值得庆幸的是,我们自己可以计算出hash值,或者用别人生成的查找表。
四、开始
如果我们对shellcode的开始进行反汇编,如下图:
我们一旦计算出指向API解析器的ebp寄存器值,我们就可以在windows的API进行哈希调用前获取其十六进制数。如此,如果在查找表里查找到那个值,在那个值附近查找调用的函数,并且添加一些注释,如下图:
这段代码执行完整性检查,通过用GET 开始的HTTP请求头部信息以确保shellcode安全运行。然后调用Windows
API函数LoadLibrary()载入两个动态链接库:ws2_32.dll(用于互联网通信的Windows套接字库)和iphlpapi.dll(WindowsIP辅助库)。
五、连接到HTTP服务器
在必要的库文件都加载完之后,shellcode将执行下图中的操作:
再一次,用相同的步骤,用哈希值查找一个Windows API函数:connect()函数。我们可以看见,在[ebp +0x2e1]位置的数据作为一个参数传递给connect()函数—从手册我们知道,那个参数是一个sockaddr结构体(sockaddr structure)。我们知道ebp寄存器指向我们在内存中的地址,使用那个地址,再加上偏移量,我们就能定位出那些数据在内存0x2E8处(ebp=0×7)。
因此,我们在分析sockaddr之前,先给这些代码添加一些注释,命名一些内存偏移地址,如下图:
这个eax寄存器保存着调用connect()函数的返回值,如果这个值为0(来自微软用户手册)表示connect()调用成功。但是我们成功连接到哪儿?如果我们现在添加一些注释到我们之前的反汇编文件,基于我们在微软用户手册中能查到的关于sockaddr结构体的信息,我们可以这样添加注释,如下图:
通过对IP地址进行whois快速查询,只得到很少信息。如下图:
六、收集用户信息
接下来,这个恶意软件尝试获得windows主机名—通常情况下这是windows机器的名字。这可能有助于确认嫌疑犯和确认他们没有逮捕错人。
其次,它枚举了活跃主机的hostname和IP地址。
使用SendARP()函数来发现网络中所有电脑的MAC地址。有一些“恰当”的方式做到这样,但是shellocode只能获得有限的空间去完成这些事。MAC地址将用户与一张特定的网卡绑定,于是可以通过这些网卡的供应链来追踪用户。
最后,它要构建HTTP头部信息,将MAC地址放进Cookie字段,用户的主机名放进Host字段,并且要以GET请求方式发送到http://65.222.202.54/05cea4de-951d-4037-bf8f-f69055b279bb。这些十六进制数字的意义不清楚,它们可能是任意选择或链接一个用户到特定的接入服务器。
七、最后阶段
这个shellcode最后阶段的唯一目的就是在该shellcode结束时运行更多的shell代码—它这样做的方式稍微有点迂回,我也不知道为什么这样,也许是一次粗略的尝试。
那么,最后一歩要怎么做。首先,它利用一些字符串长度操作找出一些被嵌入在其他所有数据区的代码。那些代码计算出我们的HTTP请求字符串结束的位置,跳过在shellcode结尾处的所有的无效的空操作指令,然后跳到那里。那里是哪里?谁知道!我被告知有更多的shellcode(这不重要),但是我已经没有时间来调试这个漏洞并获得它。
构建头部,然后跳转到头部结尾处。
查找http头部结尾处的无效指令并且跳过它们。
八、运行代码
到目前为止,我以完全静态的方式分析了这些代码—主要是为了完整性。通过运行这些代码能够更快速的分析它们,同时可以验证我们的分析是否正确。既然这段恶意代码没有任何破坏系统的行为,因此我们可以安全的在真机上运行它。于是运行它并观察它发送给FBI的确切数据。因为shellcode不是一个exe可执行文件,我需要一个shellcode加载器去运行它—-它们快速的分配需要的内存,加载shellcode并跳转到该处。这是加载shellcode需要做的—-在调用真正的shellcode之前会自动断开。
然后我们启动调试器跨步执行到调用cnnect()函数。我们不得不把这个shellcode的目标指向另一台我们自己的机器,便于观察运行结果。因此,我将让他指向ip地址为192.168.0.254的77端口,然后在这个机器上运行netcat程序抓取数据。在下图代码暂停的箭头所指处,就是早期通过patch方式来修改sockaddr结构体。
然后我们继续跨步执行到调用send()函数,执行它,之后我们能看见在netcat终端的输出,它将输出它收到的所有数据—显示的那些数据原本是发给FBI。你可以看到,cookie里面的ID包含了我的MAC地址,Host
头部包含了我的desktop名。
最后,我们跨步执行到最后阶段—最后的空操作可能是用来注入更多的已经提取的shellcode。在那个十六进制查看器窗口可以看见,我们构造的那个HTTP请求已经发出。
九、结论
这个恶意软件从用户浏览器发送确认信息给攻击机,然后使用户firefox浏览器崩溃。就复杂性而言,它没有很明显的特别之处,也没有使用一些大家不知道的新技术。
如果这个恶意软件作者是FBI,那么他们需要去回答一些很严肃的问题。这些恶意程序被植入到非美国的服务器上,并攻击利用全球用户的浏览器(他们当中很多人是无辜的)他们这样做甚至可能会得到法律授权?我不这么认为。当有人告发时FBI是否需要出示他们曾经得到过法律授权的证据?未必。我希望他们会依据用户的电脑内发现的证据和在收到起诉时取得的证据—-尽管最初的搜索授权可能很不靠谱。不管怎么说,我毕竟不是一个律师。