VulnScan – 自动化内存损坏问题的分类与根因分析
微软安全响应中心(MSRC)接收关于我们产品中潜在漏洞的报告,我们的工程团队负责评估这些问题的严重性、影响和根本原因。实际上,这些报告中很大一部分是内存损坏问题。为了根本原因分析,MSRC安全工程师通常需要分析崩溃并尝试理解出错的地方。这种工作流程不仅适用于外部报告的漏洞;通过模糊测试和变体调查内部发现的漏洞也通常是内存损坏问题,这些也需要进行分类和可利用性评估。
因此,MSRC多年来投入了大量资源来构建工具,以帮助我们自动化根本原因分析过程。VulnScan是MSRC设计和开发的一个工具,旨在帮助安全工程师和开发者确定内存损坏漏洞的漏洞类型和根本原因。它建立在两个内部开发的工具之上:Windows调试工具(WinDbg)和时间旅行调试(TTD)。
WinDbg是微软的Windows调试器,最近进行了用户界面改造,使其更易于使用。您可以在此处找到有关新WinDbg预览版本的更多信息。
时间旅行调试是一个内部开发的框架,用于记录和重放Windows应用程序的执行。该技术在CPPCon 2017期间发布。
通过利用WinDbg和TTD,VulnScan能够自动推断最常见类型的内存损坏问题的根本原因。应用程序验证器的PageHeap机制用于在更接近问题根本原因的位置触发访问违规。分析从崩溃位置开始,并向根本原因推进。VulnScan支持的内存损坏问题类别包括:
越界读写
无效指针值被追踪回其起源。如果起源指向有效分配,并且指针随后变为无效,VulnScan尝试确定哪条指令进行了更改以及原因。
这也意味着该工具可以检测整数溢出和下溢,以及由不良循环计数器值引起的基本越界访问。
释放后使用
访问违规时无效指针的值用于反向查找,以确定执行时间线中无效指针变为有效的点。从这一点开始,VulnScan进行应用程序代码的前向跟踪,跟踪所有内存释放操作以确定指针在何处被释放。
在选择上述方法之前,我们尝试了几种技术。最初记录了所有内存分配和释放,但这非常耗费资源和时间。它还对其他错误的分类速度产生了负面影响,因为即使对于非释放后使用的错误,也会准备堆对象映射。这种方法使我们能够在未启用PageHeap的情况下分类释放后使用的错误。它仍然可以使用,但默认情况下是禁用的。
类型混淆
对于这种漏洞类型,该工具使用启发式方法检查指针大小和对齐是否一致。如果在反向执行流中,受污染的指针值被相同大小的值部分覆盖(未对齐的结构成员内存写入)或用不同大小的值修改,则可能表示类型混淆漏洞(不同的结构成员类型)。
类型混淆错误的一个示例分类可以在本博客文章的下一部分找到。
未初始化内存使用
通过在内存的读取地址插入内存断点来验证每个内存读取操作的初始化。然后代码反向运行到写入点。如果缺少写入操作,我们向用户报告未初始化内存使用并继续分析。
未初始化内存指针的示例分类:
1
2
3
4
5
6
7
8
9
10
11
12
|
[*] 当前指令:cmp qword ptr [r8+00000558h],rax
[*] 当前位置:0x2B3DC0000001
[*] 源内存值:0x2390E31B940
[*] 污染回寄存器:r8
[*] 寄存器值:0x0
[*] ----------------------------------------------------------------------
[*] 当前指令:mov r8,qword ptr <b style="color: orange">[rcx+00000410h]</b> <- 未初始化内存
[*] 当前位置:0x2b3d80000144
[*] 源有效地址:0x2417f9a2690
[*] 源内存值:0x0
[*] ----------------------------------------------------------------------
[*] 检测到未初始化堆对象漏洞!!!
|
空/常量指针解引用
VulnScan中的多分支污染引擎将所有值追踪回其初始化。如果所有分支都未经修改地回溯到空值或常量值,我们向用户报告空或常量指针解引用。
空指针解引用错误的示例分类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
[*] 当前指令:test byte ptr [rcx+4Ch],1
[*] 当前位置:0x6328B80000001
[*] 源内存值:0x1
[*] 污染回寄存器:rcx
[*] 寄存器值:0x0
[*] ----------------------------------------------------------------------
[*] 当前指令:mov rcx,qword ptr [rcx+20h]
[*] 当前位置:0x6328B4000014E
[*] 源有效地址:0x1E3A54FDC20
[*] 源内存值:0x0
[*] 内存在@TTTPos: 1744423515849038初始化。
[*] 内存已初始化!
[*] 污染回内存:0x1E3A54FDC20
[*] ----------------------------------------------------------------------
[*] 当前指令:mov qword ptr [rbx+20h],rax
[*] 当前位置:0x6327FC00002AE
[*] 源内存值:0x0
[*] 污染回寄存器:rax
[*] 寄存器值:0x0
[*] ----------------------------------------------------------------------
[*] 当前指令:xor eax,eax
[*] 当前位置:0x6327FC00002AD
[*] 污染寄存器被清零!
[*] ----------------------------------------------------------------------
|
MSRC将VulnScan用作我们称为Sonar的自动化框架的一部分。它自动处理在所有支持的平台和软件版本上外部报告的概念验证文件。Sonar用于重现和执行根本原因分析。为此,我们采用多个不同的环境,并尝试使用不同的配置多次重现问题。
VulnScan计划纳入Microsoft安全风险检测服务(Project Springfield),用于重复崩溃的去重和通过模糊测试发现的漏洞的扩展分析。
在10个月的时间里,VulnScan被用于分类Microsoft Edge、Microsoft Internet Explorer和Microsoft Office产品的所有内存损坏问题。其成功率约为85%,为MSRC工程师节省了约500小时的工程时间。
案例研究 – 分类类型混淆错误(CVE-2017-0134)
借助时间旅行调试(TTD),我们可以在代码执行时间线的两个方向上探索代码。我们使用污染技术跟踪寄存器更改,并使用内存断点跟踪对内存的写入。污染过程中的每条指令都在先前执行的指令的上下文中进行分析,以找到问题的可能根本原因并确定错误类别。
VulnScan污染分析是多分支的,这意味着它可以顺序跟踪从单个指令获得的所有值。VulnScan有一个与执行时间线中特定位置相关联的寄存器和内存地址队列。污染分析对每个分支单独执行。使用这种技术,可以完全重现应用程序数据流。随着时间的推移,我们进行了一些简化和优化以加速分析过程。以下分析是一个简单示例,仅用于突出显示工具的基本概念和能力。
该示例来自Jordan Rabet(Microsoft攻击性安全研究团队)提交给MSRC的Chakra漏洞。
跟踪中的重要位置包括:
位置0x2D0780000001:访问违规的位置。
地址(0xA0000000A)由mov指令解引用,不指向有效的内存位置。我们通过从用于此指针计算的寄存器(rcx)反向污染开始分析。
位置0x2CFA8000014D:启发式首次触发。
这可能是此分析中最重要的点。无效指针值作为64位值被追踪回此点,但它在内存写入操作中用作32位值。此启发式在分析中触发了多次,但这些并不重要,因为它们不影响我们在访问违规位置看到的无效指针值。
位置0x1D420000037E到0x1D40400000A7:受污染的值在这些位置之间变化。
由于这是由NtReadFile系统调用外部设置的,这意味着该值可能由攻击者控制。进一步反向追踪显示内存被设置为PageHeap特定的常量值,这也表明我们正在处理堆分配。
1
2
3
4
5
6
7
8
9
|
调用堆栈:
00 ch!memcpy <- 位置 0x1D420000037E
01 ch!memcpy_s
02 ch!_fread_nolock_s <- 系统调用
03 ch!fread_s
04 ch!fread
05 ch!Helpers::LoadScriptFromFile
. . .
0n <- 位置 0x1D40400000A7
|
位置0x2A8C80001709:主分支的污染分析在此结束。
解引用的地址(0x7FFC239B2358)是ChakraCore二进制文件中的只读全局变量。从这一点开始执行其他分支(称为连接点)的分析。分析将在下一个分支继续,因为该指令是目标操作数中寄存器的算术操作。
此操作在源代码中由dbl *= g_rgdblTens[lwExp]表示,其中g_rgdblTens是全局变量。
其他分支(位置0x2A8C800016F9和位置0x1D404000001A)导致常量和空值,但无论如何调查它们以确保我们没有错过任何重要细节是值得的。
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
db db db db db d8b db .d8888. .o88b. .d8b. d8b db
88 88 88 88 88 888o 88 88' YP d8P Y8 d8' `8b 888o 88
Y8 8P 88 88 88 88V8o 88 `8bo. 8P 88ooo88 88V8o 88
`8b d8' 88 88 88 88 V8o88 `Y8b. 8b 88~~~88 88 V8o88
`8bd8' 88b d88 88booo. 88 V888 db 8D Y8b d8 88 88 88 V888
YP ~Y8888P' Y88888P VP V8P `8888Y' `Y88P' YP YP VP V8P
[*] 加载跟踪。
[*] 在跟踪文件中找到异常!
[*] 当前指令:mov rax,qword ptr [rcx+8]
[*] 当前位置:<b style="color: red">0x2D0780000001</b>
[*] 源有效地址:0xA0000000A
[*] 污染回寄存器:rcx
[*] 寄存器值:0xA00000002
[*] ----------------------------------------------------------------------
[*] 当前指令:mov rcx,qword ptr [rsp+00000088h]
[*] 当前位置:0x2D0740000FE7
[*] 源有效地址:0x99C17FDCF8
[*] 源内存值:0xA00000002
[*] 内存在@TTTPos: 49509161766887初始化。
[*] 内存已初始化!
[*] 污染回内存:0x99C17FDCF8
[*] ----------------------------------------------------------------------
[*] 当前指令:mov qword ptr [r15],rax
[*] 当前位置:0x2D0740000FD0
[*] 源内存值:0xA00000002
[*] 污染回寄存器:rax
[*] 寄存器值:0xA00000002
[*] ----------------------------------------------------------------------
[*] 当前指令:mov rax,qword ptr [rcx+18h]
[*] 当前位置:0x2D0740000FCC
[*] 源有效地址:0x2967D8CC0C0
[*] 源内存值:0xA00000002
[*] 内存在@TTTPos: 49509161766860初始化。
[*] 内存已初始化!
[*] 污染回内存:0x2967D8CC0C0
[*] ----------------------------------------------------------------------
[*] 当前指令:mov dword ptr [r10+rax*4+18h],r11d
[*] 当前位置:<b style="color: orange">0x2CFA8000014D</b>
[*] 源内存值:0xA
<b style="color: orange">[*] 检测到指针大小不匹配!</b>
[*] 污染回寄存器:r11d
[*] 寄存器值:0xA
[*] ----------------------------------------------------------------------
[*] 当前指令:mov r11d,r8d
[*] 当前位置:0x2CFA8000013E
[*] 污染回寄存器:r8d
[*] 寄存器值:0xA
[*] ----------------------------------------------------------------------
[*] 当前指令:mov r8d,r9d
[*] 当前位置:0x2CFA8000013A
[*] 污染回寄存器:r9d
[*] 寄存器值:0xA
[*] ----------------------------------------------------------------------
[*] 当前指令:mov r9d,dword ptr [rdi+rax*4+18h]
[*] 当前位置:0x2CFA8000012C
[*] 源有效地址:0x2967D8B4898
[*] 源内存值:0xA
[*] 内存在@TTTPos: 49454400930092初始化。
[*] 内存已初始化!
[*] 污染回内存:0x2967D8B4898
[*] ----------------------------------------------------------------------
[*] 当前指令:mov qword ptr [rax],rcx
[*] 当前位置:0x2CC280001D5A
[*] 源内存值:0x140000000A
[*] 检测到指针大小不匹配!
[*] 污染回寄存器:rcx
[*] 寄存器值:0x140000000A
[*] ----------------------------------------------------------------------
[*] 当前指令:mov rcx,qword ptr [rdx]
[*] 当前位置:0x2CC280001D59
[*] 源有效地址:0x2967E1B4070
[*] 源内存值:0x140000000A
[*] 内存在@TTTPos: 49213882768729初始化。
[*] 内存已初始化!
[*] 污染回内存:0x2967E1B4070
[*] ----------------------------------------------------------------------
[*] 当前指令:movups xmmword ptr [rcx-10h],xmm0
[*] 当前位置:0x2C68400005CB
[*] 源内存值:0x140000000A
[*] 污染回寄存器:xmm0
[*] 寄存器值:0x140000000A
[*] ----------------------------------------------------------------------
[*] 当前指令:movups xmm0,xmmword ptr [rdx+rcx]
[*] 当前位置:0x2C68400005C7
[*] 源有效地址:0x2967D713D30
[*] 源内存值:0x140000000A
[*] 内存在@TTTPos: 48826261964231初始化。
[*] 内存已初始化!
[*] 污染回内存:0x2967D713D30
[*] ----------------------------------------------------------------------
[*] 当前指令:mov dword ptr [rax+8],ecx
[*] 当前位置:0x2C4A80000537
[*] 源内存值:0x14
[*] 检测到指针大小不匹配!
[*] 污染回寄存器:ecx
[*] 寄存器值:0x14
[*] ----------------------------------------------------------------------
[*] 当前指令:mov ecx,dword ptr [rdx+8]
[*] 当前位置:0x2C4A80000535
[*] 源有效地址:0x2967E1BC078
[*] 源内存值:0x14
[*] 内存在@TTTPos: 48698486687029初始化。
[*] 内存已初始化!
[*] 污染回内存:0x2967E1BC078
[*] ----------------------------------------------------------------------
[*] 当前指令:mov dword ptr [rbx+rcx*4+4],eax
[*] 当前位置:0x2C4A800004E5
[*] 源内存值:0x14
[*] 污染回寄存器:eax
[*] 寄存器值:0x14
[*] ----------------------------------------------------------------------
[*] 当前指令:mov eax,dword ptr [rbp+18h]
[*] 当前位置:0x2C4A800004E0
[*] 源有效地址:0x2967DDB9AA8
[*] 源内存值:0x14
[*] 内存在@TTTPos: 48698486686944初始化。
[*] 内存已初始化!
[*] 污染回内存:0x2967DDB9AA8
[*] ----------------------------------------------------------------------
[*] 当前指令:mov dword ptr [rax+18h],ebp
[*] 当前位置:0x2A8C80001858
[*] 源内存值:0x14
[*] 污染回寄存器:ebp
[*] 寄存器值:0x14
[*] ----------------------------------------------------------------------
[*] 当前指令:mov ebp,edx
[*] 当前位置:0x2A8C80001827
[*] 污染回寄存器:edx
[*] 寄存器值:0x14
[*] ----------------------------------------------------------------------
[*] 当前指令:mov edx,dword ptr [rdi+000000E0h]
[*] 当前位置:0x2A8C8000181F
[*] 源有效地址:0x99C17FEE00
[*] 源内存值:0x14
[*] 内存在@TTTPos: 46782931277855初始化。
[*] 内存已初始化!
[*] 污染回内存:0x99C17FEE00
[*] ----------------------------------------------------------------------
[*] 当前指令:mov dword ptr [rax],edx
[*] 当前位置:0x2A8C80001738
[*] 源内存值:0x14
[*] 污染回寄存器:edx
[*] 寄存器值:0x14
[*] ----------------------------------------------------------------------
[*] 当前指令
|