首页
社区
课程
招聘
[翻译]消失的字节:对 MS Office RTF 解析器的逆向工程
发表于: 2018-3-30 15:21 4623

[翻译]消失的字节:对 MS Office RTF 解析器的逆向工程

2018-3-30 15:21
4623

微软的Office是2017年的众多攻击目标之一,除了发现的大量漏洞和发布的POC之外,恶意软件作者认为有必要防止由防病毒软件检测到“one-day”和“old-day”漏洞。很明显,使用RTF解析功能和特性已不足以有效逃避检测。随着对MS Office利用的兴起,我们遇到了大量的RTF被用作攻击容器的情况,这些样本“利用”Microsoft Word RTF解析器的实现来混淆所有其他第三方的RTF解析器,包括那些在AV软件中的解析器。

为了还原MS Office中的解析,我们需要对其进行逆向工程。

我决定首先看看较早版本的MS Office 2010,可以先研究一下早期的实现方法。然后,再与新版本中的相关实现方法进行比较。

一个RTF解析器包含一个具有37个状态的状态机,其中的22个状态是唯一的:

我们先看下最重要的状态以及那些对解析 \objdata(包含对象数据的目标控制字)有影响的那些状态。Microsoft OLE链接,Microsoft OLE嵌入对象和Macintosh版本管理器订阅服务器对象在RTF中用对象表示。以下是所有的情况:

由于没有Microsoft Office的调试符号,所以无法恢复其原始的状态名称。不过,我已根据其基本功能选择了合适的名称。

在打开的RTF文件上执行的第一个状态是PARSER_BEGIN。在大多数情况下,它也是在处理完一个控制字之后执行的。此状态的主要目标是根据遇到的char,目标以及存储在'this'结构中并由控制字处理器设置的其他值来确定下一个状态。默认情况下,下一个状态是PARSER_CHECK_CONTROL_WORD

PARSER_CHECK_CONTROL_WORD将检查下一个字符是控制字的开始还是控制符号,并相应地设置下一个状态。

PARSER_PARSE_CONTROL_WORDPARSER_PARSE_CONTROL_WORD_NUM_PARAMETER 两种状态下, 由ASCII 字母组成的以Null结尾的控制字和以非Null 结尾的数字参数(如果有的话)会被存储在一个固定大小的缓冲区里。

然后在 PARSE_PROCESS_CMD状态下调用另一个函数来处理 控制字和控制标志。它根据当前状态,相应地设置下一个状态。

有很多种状态来负责解析十六进制数据,我们最感兴趣的是PARSER_PARSE_HEX_DATA状态。正如你所看到的,如果设置了objdata的目的地的话,就会在PARSER_BEGIN中设置该字段。

如果设置,该状态就会解析十六进制数据和二进制数据。

PARSER_PARSE_HEX_NUM_MSBPARSER_PARSE_HEX_NUM_LSB状态用来解析十六进制值(\ panose控制字和\'控制符号的数据)。

看下PARSER_PARSE_HEX_NUM_MSBPARSER_PARSE_HEX_NUM_LSBPARSER_PARSE_HEX_DATA这三种状态,很容易发现其中的错误。即使它们使用不同的变量来存储解码后的十六进制值,但它们还是使用了相同的位来确定哪个半字节现在被解码了 - 高位(最高有效位或MSB)或低位(低位有效位或LSB)。并且,PARSER_PARSE_HEX_NUM_MSB状态总是将此位重置为MSB。

因此,通过改变PARSER_PARSE_HEX_NUM_MSB的状态,就可以使PARSER_PARSE_HEX_DATA状态中的上下文里的一些字节消失。

为了达到我们的目的,需要在\objdata控制字之后的数据中加入\'XX。在这种情况下,当解析器在PARSER_PARSE_HEX_DATA状态中遇到\时,它就会返回到状态PARSER_BEGIN,之后将进入状态PARSER_PROCESS_CMD\'控制符号的处理程序不会改变最终的目的地,但会将下一个状态更改为PARSER_PARSE_HEX_NUM_MSBPARSER_PARSE_HEX_NUM_MSBPARSER_PARSE_HEX_NUM_LSB控制权转移回PARSER_BEGIN后,最终转换为PARSER_PARSE_HEX_DATA,因为最终的目的地仍然是objdata。在那之后,下一个字节将被解码为高半字节。

值得注意的是,PARSER_PARSE_HEX_NUM_LSB不检查提供的值是否为有效的十六进制数;因此,\'后面可以是任意的两个字节。

比如说,下面的这个例子:

图片描述

最终结果会删除"f\'cc"

当控制首次转到PARSER_PARSE_HEX_DATA状态时,处理\ objdata控制字后,MSB位已经设置。我们来详细地看下这个过程的处理细节:
图片描述

在逆向了关键字处理函数后,我找到了所有控制字及其相应结构的列表:

图片描述

有了这些信息,我们可以找到并查看objdata构造函数:

图片描述

可以看到它设置了MSB位,分配一个新的缓冲区并用新的指针替换旧的指针。因此,在两个\ objdata控制字之间解码的数据从来没有用到过。

图片描述

最终结果会删除掉"d0cf11e0a1b11ae1"

我们知道如果\\ objdata被放入数据中,会改变输出结果。但如果是其他控制字和控制符号呢?这里有超过1500个,但几乎没有!

由于某些控制字表示目的地,因此不能使用它们 - 它们自己更改objdata目标,并解码需要objdata目标的对象。其他的一些控制字并不影响objdata的目的地。

在不丢失先前解码的数据的情况下返回到objdata目的地的唯一方法是使用特殊符号 -{},这些符号表示组的开始和结束。

当解析器遇到PARSER_BEGIN状态的组末尾时,将在组开始之前设置的目标将被恢复。因此,通过在\objdata之后放置{\aftncn FF},FF不会作为解码数据,因为FF现在应用于目标aftncn并将根据此目标进行处理。但是,通过使用{\aftnnalc FF},FF将进入解码数据,因为目标仍然等于objdata。还值得注意的是{\ objdata FF}仍然不能使用,因为缓冲区不会被恢复。所有目标控制字的准确列表都是用简单的fuzzer创建的。

在查看RTF解析器的代码时想到课另一种混淆技术(与'MSB'bug无关),但也可用于从十六进制流中移除字节。 该技术与临时缓冲区大小以及在PARSER_PARSE_CONTROL_WORDPARSER_PARSE_CONTROL_WORD_NUM_PARAMETER状态下控制字和数值参数如何解析有关。 在下面的截图中,你可以看到实际样例:

图片描述

在此示例中,将使用以下公式计算将那部分被作为数字参数删除的数据的大小:

尽管上述技术与通常的RTF解析相关,但某些特定关键字的处理隐藏了一些进一步的混淆措施。

根据规范所称,如果发现了\*,但在查找表中却找不到之后的控制字或控制符号,它将被视为一个未知的目标组,并且直到右括号之前的所有数据都应该被丢弃。 而MS Office中的查找表包含规范中不存在的控制字,使人们担忧它将来会改变,从而影响不同版本的MS Office上的同一文档的解析过程。 当负责处理关键字的函数遇到这种情况或某个特定控制字(如\comment\generator\nonshppict等)时,它会将状态PARSER_SKIP_DATA和遇到的{设置为1。

在分析PARSER_SKIP_DATA *状态的过程中,我发现不仅与规范相反,而且还与解析器代码的其余部分相反。

在查找\bin控制字时,此状态将跳过数据,更改所遇到{}大括号的数量,直到该数字等于零。隐藏的问题在于数字参数处理的方式。

首先,数字参数的最大允许长度增加到0xFF--它的计算没有考虑控制字的长度。

第二个问题是数字参数不再是数字了! 解析器不仅允许使用十进制字符,而且还允许传递拉丁字符。 然后将此参数传递给自定义strtol,从而可以指定应该跳过的数据的长度,而不用将{}视为十六进制数字。

目前来看,还尚未在野外遇到使用这两种情况的混淆。

逆向工程是建立一个解析器的最有效方式,然而对于RTF,却有可能无法达到预期的行为。

精确的解析依赖于小的实现细节和算法错误,而不是一个可能令人困惑的规范或陈述不真实的规范。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2019-2-1 20:24 被admin编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (2)
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
2
Cool  Job
2018-3-30 15:21
0
雪    币: 10962
活跃值: (2925)
能力值: ( LV5,RANK:71 )
在线值:
发帖
回帖
粉丝
3
看着就很牛逼,代码逆的如此详细。
2018-3-30 18:59
0
游客
登录 | 注册 方可回帖
返回
//