CVE-2019-8604 analysis

info

1
2
3
4
5
6
7
Available for: macOS Sierra 10.12.6, macOS High Sierra 10.13.6, macOS Mojave 10.14.4

Impact: An application may be able to execute arbitrary code with system privileges

Description: A memory corruption issue was addressed with improved memory handling.

CVE-2019-8604: Fluoroacetate working with Trend Micro's Zero Day Initiative

zdi的描述:

1
2
3
This vulnerability allows remote attackers to escape the sandbox on affected installations of Apple Safari. An attacker must first obtain the ability to execute low-privileged code on the target system in order to exploit this vulnerability.

The specific flaw exists within the securityd service. The issue results from the lack of proper validation of the length of user-supplied data prior to copying it to a heap-based buffer. An attacker can leverage this in conjunction with other vulnerabilities to execute code under the context of the current user.

还是zdi的描述详细,苹果的描述真的是不怎么可信。

vuln

两种找到这个洞的方式,靠diff或者沿着这个描述尝试挖。

我首先试了试diff,因为我用的是 10.14.310.14.6,变化有点大,diff真的不好使。

所以我选择了第二种方式。 既然是直接获取了长度并使用,那就先确定该服务使用的ipc方式,然后找获取数据的函数的xref,然后一个一个看,没一会儿就能看到这个可疑的地方了:

10.14.3

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
char __fastcall sub_100053185(__int64 a1, __int64 a2, __int64 a3)
{
__int64 v3; // rbx
__int64 v4; // r15
const void *source; // r13
size_t length; // r14
void *v7; // r12
size_t v8; // r14
unsigned int v9; // eax
char result; // al
size_t v11; // [rsp+10h] [rbp-80h]
char v12; // [rsp+18h] [rbp-78h]
__int64 v13; // [rsp+60h] [rbp-30h]

v3 = a3;
v4 = xpc_dictionary_get_string(a3, "_item_name");
source = (const void *)xpc_dictionary_get_data(v3, "_item_value", &v11);
if ( xpc_dictionary_get_value(v3, "_item_sensitive_value_length") )
{
length = xpc_dictionary_get_uint64(v3, "_item_sensitive_value_length");
v7 = malloc(length);
memcpy(v7, source, length);
memset_s(source, v11, 0LL, length);
v11 = length;
}
else
{
v8 = v11;
v7 = malloc(v11);
memcpy(v7, source, v8);
}
v9 = xpc_dictionary_get_uint64(v3, "_item_flags");
sub_100005588(&v12, v4, (unsigned int)v11, v7, v9);
sub_1000532D4(*(_QWORD *)(a1 + 32), &v12, &v12);
free(v7);
sub_10005222C(&v12);
result = __stack_chk_guard;
if ( __stack_chk_guard == v13 )
result = 1;
return result;
}

copy的length可控,dst是根据用户的length分配的,但是source不一定有那么大,length又没有check,所以就越界读了。

patch by diff

10.14.6

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
char __fastcall sub_10004405C(__int64 a1, __int64 a2, __int64 a3)
{
__int64 v3; // rbx
__int64 v4; // r15
__int64 v5; // r12
const void *v6; // r13
unsigned __int64 length; // rax
size_t v8; // r14
size_t v9; // r15
__int64 v10; // rbx
size_t v11; // r14
void *v12; // r12
unsigned int v13; // eax
char result; // al
__int64 v15; // [rsp+10h] [rbp-90h]
size_t data_length; // [rsp+18h] [rbp-88h]
int v17; // [rsp+20h] [rbp-80h]
size_t v18; // [rsp+24h] [rbp-7Ch]
__int64 v19; // [rsp+70h] [rbp-30h]

v3 = a3;
v4 = a1;
v5 = xpc_dictionary_get_string(a3, "_item_name");
v6 = (const void *)xpc_dictionary_get_data(v3, "_item_value", &data_length);
if ( !xpc_dictionary_get_value(v3, "_item_sensitive_value_length") )
{
v15 = v5;
v11 = data_length;
v12 = malloc(data_length);
memcpy(v12, v6, v11);
LABEL_7:
v13 = xpc_dictionary_get_uint64(v3, "_item_flags");
sub_1000045A2(&v17, v15, (unsigned int)data_length, v12, v13);
sub_10004422A(*(_QWORD *)(v4 + 32), &v17, &v17);
free(v12);
sub_10004306E(&v17);
goto LABEL_8;
}
length = xpc_dictionary_get_uint64(v3, "_item_sensitive_value_length");
v8 = length;
v9 = data_length;
if ( length <= data_length ) // add length check
{
v15 = v5;
v12 = malloc(length);
memcpy(v12, v6, v8);
memset_s(v6, v9, 0LL, v8);
data_length = v8;
v4 = a1;
goto LABEL_7;
}
v10 = sub_100093B1E("SecurityAgentXPCQuery");
if ( (unsigned __int8)os_log_type_enabled(v10, 0LL) )
{
v17 = 134217984;
v18 = v8;
_os_log_impl(&_mh_execute_header, v10, 0LL, aSensitiveDataL, &v17, 12LL);
}
LABEL_8:
result = __stack_chk_guard;
if ( __stack_chk_guard == v19 )
result = 1;
return result;
}

取了要copy的data的长度,并且检查了用户传递来的size是不是小于等于这个值,之后再拷贝。

ref

ZDI-19-766