Cinder27 — iOS 17.0-27.1 vulnerability verification lab: 51 modules, 31 per-CVE POC folders, 11 no-CVE module folders, harness app source

This commit is contained in:
jbjf6666 committed 2026-10-29 02:11:12 +08:00
commit 1ea76ab005
89 files changed
+16485

No files matched your search

+197
View File
@@ -0,0 +1,197 @@
import Foundation
import Combine
enum Reach: String, Codable, CaseIterable {
case inProcess = "进程内"
case systemProcess = "系统进程"
case kernel = "内核"
case entitlementGated = "权限受限"
case radio = "无线链路"
case peripheral = "外设"
/// Localized display name; rawValue stays Chinese as the stable storage key.
var label: String { tl(rawValue) }
}
enum PrimitiveKind: String, Codable, CaseIterable {
case oobWrite = "OOB 写"
case oobRead = "OOB 读"
case uaf = "UAF"
case infoLeak = "信息泄露"
case typeConfusion = "类型混淆"
case intOverflow = "整数溢出"
case probe = "探测"
/// Localized display name; rawValue stays Chinese as the stable storage key.
var label: String { tl(rawValue) }
}
struct ModuleOutcome {
var headline: String
var detail: String = ""
var evidence: [String] = []
var artifacts: [URL] = []
var elapsedMs: Int = 0
/// Set only when the module threw. The headline is display text and may be localized,
/// so it must never be compared against a literal to detect failure.
var failed: Bool = false
}
final class RunContext {
let workdir: URL
let journal: GuardJournal
init(workdir: URL, journal: GuardJournal) {
self.workdir = workdir
self.journal = journal
}
@discardableResult
func write(_ name: String, _ data: Data) -> URL {
let url = workdir.appendingPathComponent(name)
try? data.write(to: url, options: .atomic)
return url
}
func seed(_ name: String) -> Data? {
guard let url = Bundle.main.url(forResource: name, withExtension: nil) else { return nil }
return try? Data(contentsOf: url)
}
func arm(_ id: String, _ detail: String) { journal.arm(id, detail) }
func disarm(_ id: String, _ detail: String = "") { journal.disarm(id, phase: "done", detail: detail) }
func fail(_ id: String, _ detail: String) { journal.disarm(id, phase: "threw", detail: detail) }
}
struct ExploitModule: Identifiable {
let id: String
/// Chinese source text. English lives in ModuleText, keyed by `id`.
let titleZh: String
let cve: String
let component: String
let primitive: PrimitiveKind
let reach: Reach
/// 1 = primitive fired on a device before, 2 = fix site proven at instruction level,
/// 3 = reachability probe / honest negative.
let tier: Int
let summaryZh: String
let notesZh: String
let run: (RunContext) throws -> ModuleOutcome
var title: String { ModuleText.resolve(id, .title, zh: titleZh) }
var summary: String { ModuleText.resolve(id, .summary, zh: summaryZh) }
var notes: String { ModuleText.resolve(id, .notes, zh: notesZh) }
}
struct LabError: LocalizedError {
let message: String
var errorDescription: String? { message }
}
final class LabEngine: ObservableObject {
@Published private(set) var outcomes: [String: ModuleOutcome] = [:]
@Published private(set) var running: Set<String> = []
@Published private(set) var logLines: [String] = []
let journal: GuardJournal
let workdir: URL
init() {
// Create every standard directory before anything writes into it. This is the single
// place that decides whether the container is usable.
Container.ensureAll()
journal = GuardJournal()
workdir = Container.work
try? FileManager.default.createDirectory(at: workdir, withIntermediateDirectories: true)
}
func log(_ line: String) {
let stamped = String(format: "[%02d:%02d:%02d] %@",
Calendar.current.component(.hour, from: Date()),
Calendar.current.component(.minute, from: Date()),
Calendar.current.component(.second, from: Date()),
line)
DispatchQueue.main.async {
self.logLines.append(stamped)
if self.logLines.count > 400 { self.logLines.removeFirst(self.logLines.count - 400) }
}
}
func run(_ module: ExploitModule) {
guard !running.contains(module.id) else { return }
running.insert(module.id)
log("▸ " + module.title + " [" + module.cve + "]")
let ctx = RunContext(workdir: workdir, journal: journal)
let started = Date()
DispatchQueue.global(qos: .userInitiated).async {
do {
var out = try module.run(ctx)
out.elapsedMs = Int(Date().timeIntervalSince(started) * 1000)
DispatchQueue.main.async {
self.outcomes[module.id] = out
self.running.remove(module.id)
self.log("✓ \(module.title) — \(out.headline) (\(out.elapsedMs) ms)")
}
} catch {
let message = (error as? LocalizedError)?.errorDescription ?? error.localizedDescription
ctx.fail(module.id, message)
var out = ModuleOutcome(headline: tl("失败"), detail: message, failed: true)
out.elapsedMs = Int(Date().timeIntervalSince(started) * 1000)
DispatchQueue.main.async {
self.outcomes[module.id] = out
self.running.remove(module.id)
self.log("✗ \(module.title) — \(message)")
}
}
}
}
func outcome(for id: String) -> ModuleOutcome? { outcomes[id] }
}
extension LabEngine {
/// Sequential run — used by the chain view, where stage N only means something after
/// stage N-1 has produced its artifact.
func runSequence(_ modules: [ExploitModule], label: String) {
log("══ " + tl("序列开始: ") + "\(label)(\(modules.count) " + tl("个原语") + ")")
// Kick the whole sequence off on main, then hand each stage to a background queue.
// Never `main.sync` from the worker: that is a deadlock waiting for main to be busy
// on something that needs the worker.
runStage(modules, index: 0, label: label)
}
private func runStage(_ modules: [ExploitModule], index: Int, label: String) {
guard index < modules.count else {
log("══ " + tl("序列结束: ") + "\(label)")
return
}
let module = modules[index]
running.insert(module.id)
let ctx = RunContext(workdir: workdir, journal: journal)
let started = Date()
DispatchQueue.global(qos: .userInitiated).async {
var out: ModuleOutcome
do {
out = try module.run(ctx)
} catch {
let message = (error as? LocalizedError)?.errorDescription ?? error.localizedDescription
ctx.fail(module.id, message)
out = ModuleOutcome(headline: tl("失败"), detail: message, failed: true)
}
out.elapsedMs = Int(Date().timeIntervalSince(started) * 1000)
let finished = out
let next = index + 1
DispatchQueue.main.async {
self.outcomes[module.id] = finished
self.running.remove(module.id)
self.log(finished.failed
? " ✗ \(module.title) — \(finished.detail)"
: " ✓ \(module.title) — \(finished.headline)")
self.runStage(modules, index: next, label: label)
}
}
}
}