柿子还是要捡软的捏
在这篇文章中,我将会对一段非常优雅的恶意代码进行分析。这段代码堪称“人间尤物”,在它的帮助下,恶意软件的作者可以直接利用目标主机中的浏览器来检测主机中是否安装有某些特定的应用程序,并根据检测信息来对“不明真相的吃瓜群众”进行攻击。简而言之,攻击者可以检测目标用户计算机中是否安装有某些特殊的分析软件(例如Fiddler),如果目标主机中没有安装这类工具的话,攻击者便会对这名用户实施攻击。实际上,这样做的目的就是为了避免他们的恶意软件被发现,以实现长期感染。俗话说得好,柿子还是要捡软的捏。
先表个态
今天,我打算带领大家找出一个类似CVE-2016-3351(该漏洞允许远程攻击者通过浏览器来获取敏感信息)的漏洞。为什么是“类似”呢?因为微软公司在上周二已经将该漏洞修复了,但是我感觉这个问题并没有得到彻底的解决。在我们开始之前,请先允许我向几位大神致敬。如果没有MalwareBytes公司的Jérôme Segura 所提供的帮助,我根本就无从下手。而且当我在测试过程中遇到困难时,Kafeine和Brooks Li给我提供了很多有价值的信息(他们关于AdGholas恶意广告活动的研究报告)。最后,我还要感谢Eric Lawrence和David Ross,没有他们两位的帮助和支持,我压根写不出这篇文章。
话不多说,进入正题
在上周之前,我对恶意软件所使用的这些技术压根一窍不通。我只知道AdGholas恶意广告活动可以躲避安全检测,但是我并不知道它是如何实现的,而且更要命的是,留给我的时间已经不多了。话虽如此,但是出于职业本能,我还是对此非常好奇的。但是Brooks Li告诉我,在微软公司正式发布修复补丁之前,他不能公布针对该漏洞的PoC(概念验证实例)。那么,我只好“let it go”了。直到上周二,我突然发现我的Windows开始更新了,这竟然是一个mimeType漏洞!然后,我便迫不及待地联系Brooks Li,他说他已经将该漏洞的分析报告和解决方案公布出来了[传送门]。这篇文章写得非常棒,但是吸引我眼球的是下面这张图片,重点部分我已经用黄色圈圈标注出来了。
这段代码写得实在是太赞了!如此简洁,如此优雅!好吧,是我太激动了。先将它转换成一个大家都能看懂的形式:
anchor = document.createElement("A");
anchor.href = ".saz";
(anchor.mimeType);
// returns Fiddler Session Archive
我已经无法抑制激动的心情了。这段代码将会返回“Fiddler Session Archive”,攻击者可以通过它来判断用户主机中是否安装有Fiddler。当然了,通过其他的系统扩展也是可以实现这个功能的,打开注册表,然后定位到ComputerHKEY_CLASSES_ROOT,剩下的就交给你了。
虽然这是一段恶意代码,但我不得不再表达一下我对它的“仰慕之情”,这段代码写得真的很简洁。为了方便进行测试,我基于这段代码编写了一个简单的功能函数。当然了,当我们的Windows安装了更新补丁之后,这段“人间尤物”般的代码也就失效了,但是你敢否认它的简洁和优雅吗?明显不能!五分钟之后,我的Windows已更新完毕。果不其然,当我们尝试从一个锚点获取mimeType属性时,它返回的是“undefined”。
接下来,让我们来看一看修复前和修复后的代码。如果这是一个减肥产品的广告,它绝对会让人印象深刻,因为代码量减少了很多!我们暂时不用对这些代码进行分析,我只是想让各位看一看代码被删掉了多少。相比之下,我还是喜欢那位恶意软件作者的优雅,尽管它是恶意软件。毫无疑问,攻击者永远都不会被那些所谓的“漏洞补丁”吓傻,他们肯定会尝试找出其他的方法来进行攻击。
漏洞已被“修复”,同志们仍需努力
漏洞既然已经被修复了,那么攻击者就必须要想其他的办法来进行攻击。等等,我也可以站在攻击者的角度来考虑这些问题呀!虽然需要花一些时间,但为何不尝试一下呢?那么,我们首先要获取到修复前的代码(mshtml.dll),然后找出我们在访问锚点mimeType数据时触发的是哪一个函数。接下来,我们就要尝试使用不同的路径来调用这个相同的函数。
下图显示的是我们在对mshtml.dll文件进行反汇编之前的猜想。我们可以肯定的是,黄色路径肯定是存在的,但是我们想知道红色的路径是否可行。那么我们该怎么做呢?首先,我们要准确找到获取mimeType数据的二进制代码,然后找出所有指向它的引用。红色的路径只是一种猜测,也许可行也许不可行,但至少我们应该尝试一下。
在实验过程中我们需要用到IDA Pro,但是你也可以选择使用其他的工具。实际上,当我们熟悉了IE/Edge浏览器中的各种函数名称之后,我们就可以使用类似WinDBG和Hiew这样的工具来加快分析速度了。但是在这篇文章中,我假设IE浏览器对我们来说是一个全新的分析对象,我们现在对其一无所知。
找出目标代码
打开IDA,把mshtml.dll这个罪魁祸首拖进IDA中。准备好了吗?让我们开始吧!我们的首要目标就是找出包含mimeType信息的二进制代码。
我们也知道JavaScript中的anchor.mimeType包含有我们需要的信息,所以我们可以直接在IDA中搜索字符串“mimeType”。很明显,前两个符合搜索条件的东西并不是我们要找的:MSMimeTypesCollections和MSMimeTypesCollectionsPrototype。第三个搜索结果是MimeType,但我们想找的是小写字母m开头的“mimeType”。接下来是两个MimeTypeArray,它们同样不是我们需要的。下一个貌似就是我们要找的东西,这是第一个真正符合“mimeType”搜索条件的数据。
双击这个字符串,我们就可以直接查看到相应的源码了。
我们发现,“mimeType”字符串的指针名为“aMimetype_2”(1)。然后在(2)中,代码使用了这个字符串,但是为了查看完整的信息,我们要点击“aMimetype”标签,然后按住“X”键,此时IDA会弹出一个窗口,里面会显示出引用了该字符串的详细引用信息。从下面这张图片中可以看到,我们找出了三个引用,中间那个似乎就是我们寻找的目标。首先要找出当我们访问anchor.mimeType时触发的二进制代码,而下面这个函数名称似乎可以提供一些线索。
如下图所示,“CHTMLAnchorElement::Trampoline_Get_mimeType”成功抓住了我的眼球。虽然“Trampoline”看起来有些陌生,但是它并不会有很大影响。
双击它,然后我们就会看到如下图所示的一大段代码。但是请别忘了我们的目标:mimeType。我们不用去研究opcode(操作码),我们只需要找出“mimeType”。
接下来,双击“CAnchorElement::get_mimeType”,我们将会得到下列代码:
接下来,让我们双击“GetFileTypeInfo”,看看能发现些什么。
终于找到了!很明显,“SHGetFileInfoW”就是我们要找的目标。
找出引用,条条大路通罗马
点击上方的“GetFileTypeInfo”,然后按下“X”查看所有的引用。请记住,我们现在的目标是要找出触发这段代码的其他方式。
尝试JavaScript代码
没错,这段代码似乎被触发了两次,操作函数为CDocument::get_mimeType和CImgElement::get_mimeType。“CDocument”看起来像是document对象,而“CImgElement”则像是Image对象或IMG HTML标签。当然了,这只是我们的猜测,所以我们现在可以在浏览器中进行测试。但是我要坦白一下,此前我甚至从未听说过这些属性…
果然如此!那么我们将其换成document 对象,看看会发生什么。
既然我们已经了解了这些属性了,那么我们就可以证实此前的猜测了:
现在的问题就是剩下的那两条红色路径。实际上,在对原始代码进行分析之后我们就会发现,“anchorElement.mimeType”和“GetFileTypeInfo”之间并没有联系。所以更新一下上面这张图片:
用代码复现,码农本色出演
我们可以在iframe中尝试传递一个“.saz”文件(Fiddler):
iframe = document.createElement("iframe");
iframe.src = "saz.saz"; // Load a saz file
document.body.appendChild(iframe);
我*!IE浏览器会尝试下载这个文件,并且抛出一个警告:
这肯定是不能接受的,黑客一般都是完美主义者。而且这样做的话,他们的攻击行为就很容易被发现了。我们可以使用“history.pushState”或者“history.replaceState”来解决这个问题:
iframe.contentWindow.history.pushState("","","saz.saz");
(iframe.contentDocument.mimeType);
你以为这样就成功了吗?大错特错!我们只是运气好而已,因为saz文件之前已经下载过了,并且浏览器进行了缓存。当IE浏览器通过一个URL地址来请求数据时,它首先会尝试猜测请求文件的类型,最后才会检查文件的扩展名。此时,我们甚至还没有获取到一个有效的saz文件,网络服务器返回的响应数据是默认的HTTP状态码200,而Content-Type也是默认的“application/octet-stream”。但是IE浏览器会推测这是一个Fiddler文件,因为它的扩展名太明显了。
构建PoC,证明自己的时候到了
mg = new Image();
img.src = "saz.saz"; // HTTP 200, any length and content-type
// The saz is precached. Let's create the iframe.
iframe = document.createElement("iframe");
// Load anything inside it because about:blank does not work with replaceState.
iframe.src = "/favicon.ico";
document.body.appendChild(iframe);
// Set the location of the iframe to the saz file
iframe.contentWindow.history.replaceState("", "", img.src);
// Bingo!
(iframe.contentDocument.mimeType);
参考资源
在IE浏览器中进行实时测试:[传送门]
下载相关文件:[传送门]
如果你使用的是Linux或者Mac的话,可以观看演示视频:
还没有评论,来说两句吧...