Cross-chain, DeFi and smart contracts · Lesson 5 of 6
Analyzing exploits and hacks
9 min read
When a protocol or an exchange is hacked, analysts are asked the same questions within hours: what happened, how much was taken, where is it now, and who did it. The answers come from a small number of transactions read in great detail, followed by a long tail of tracing. This lesson brings together the decoding skills of this and earlier modules (tokens, proxies, swaps, flash loans and bridges) and applies them to public cases.
The exploit lifecycle in data
Most incidents, whatever their root cause, pass through the same phases, and each phase leaves different evidence:
| Phase | What the attacker does | Evidence to collect |
|---|---|---|
| Funding | Obtains gas money for fresh addresses | First incoming transfer to each attacker address and its source |
| Preparation | Deploys attack contracts, tests, sometimes on other chains first | Contract creations, failed attempts and their revert reasons, bytecode |
| Execution | Runs one or more attack transactions | Traces, logs, balance changes per address and token |
| Consolidation | Swaps freezable or illiquid tokens into ETH or other neutral assets, gathers funds | DEX swaps with large price impact, transfers between attacker addresses |
| Dispersal | Splits funds, bridges them, uses mixers, swaps to Bitcoin, cashes out | Fan-out patterns, bridge deposits, mixer deposits, exchange deposit addresses |
The phases overlap, and with key compromises the preparation happens off-chain. Still, organizing evidence this way keeps an investigation from jumping straight to the most visible transaction and missing the funding that might identify the attacker.
Reading the attack transaction
Start with the trace, not the logs. Logs record what contracts chose to announce; the trace records every call, its value and whether it succeeded. A practical order of work:
- List the actors: the externally owned account that sent the transaction, the attack contract it called, the victim contracts, and any address that ends up with profit.
- Compute balance changes per address and per token for the whole transaction (the netting query in the lesson DEX swaps, instant exchanges and liquidity flows works here). The attacker's net gain and the victim's net loss frame everything else.
- Walk the call tree from the top, marking flash loans (uncollateralized loans that must be repaid, plus a premium, before the transaction ends, or the whole transaction reverts) so that borrowed principal is not counted as loss.
- Find the step where the protocol's state became inconsistent. That is the root cause, and usually a single function call.
A balance-change summary for a fictional attack transaction shows what step 2 produces:
{
"tx_hash": "0x7c0d…e91a",
"block_number": 21000417,
"balance_changes": [
{"address": "0xa77a…c0de", "role": "attack contract", "token": "DAI", "delta": "+2400000.00"},
{"address": "0x5af3…71b2", "role": "victim pool", "token": "DAI", "delta": "-2401000.00"},
{"address": "0x87f1…4e2a", "role": "flash-loan lender", "token": "DAI", "delta": "+1000.00"},
{"address": "0x3e9d…02f7", "role": "sender EOA", "token": "ETH", "delta": "-0.041"}
]
}
The lender's 2,000,000 DAI flash loan does not appear at all, because it went out and came back in the same transaction; only its 1,000 DAI premium (0.05%) remains. The victim lost 2,401,000 DAI, the attack contract kept 2,400,000, and the sender paid 0.041 ETH in gas.
The Euler Finance exploit of March 13, 2023 shows the method. Security researchers' published analyses of one of the attack transactions reduce it to these steps:
Attack contract, one of several attack transactions (DAI pool)
1. Flash loan 30,000,000 DAI from Aave
2. Deposit 20,000,000 DAI into Euler -> collateral tokens (eDAI)
3. mint(): self-leverage -> 200,000,000 dDAI debt, 195,600,000 eDAI
4. Repay 10,000,000 DAI -> 10,000,000 dDAI burned
5. mint() again -> another 200,000,000 dDAI and 195,600,000 eDAI
6. donateToReserves(100,000,000 eDAI) -> no solvency check: account now insolvent
7. Second attack contract liquidates the first at a 20% discount
8. Withdraw 38,900,000 DAI, repay flash loan plus premium, keep the rest
Step 6 is the root cause: a function that reduced collateral without checking the account's health. Steps 1 and 8 cancel out the borrowed capital. Repeated across several pools, the attack took about $197 million in DAI, WETH, WBTC, stETH and USDC. The exploiter swapped most of it into ETH and DAI, sent 100 ETH to a mixer, then negotiated with the protocol through on-chain messages and returned all recoverable funds by April 3, 2023. On-chain messages are worth decoding: they are text in the input data of plain transfers and form part of the record.
Funding, preparation and copycats
The funding of an exploiter address is often the best attribution lead. Common sources are withdrawals from mixers (Tornado Cash pools used fixed denominations such as 0.1, 1, 10 and 100 ETH), bridges from another chain, instant-exchange services, and occasionally an exchange withdrawal, which can be followed up through legal process (see the lesson Tracing through exchanges and custodial services). Check also:
- The deployer of each attack contract, and other contracts it deployed, including test versions on other chains.
- Bytecode similarity with earlier attack contracts, which can link incidents.
- Failed attempts before the successful one: their revert reasons show what the attacker was testing and when they started.
Not every address that profits is the original attacker. MEV bots copy profitable pending transactions, and some white-hat groups front-run attacks to rescue funds. In the Euler case, an address that claimed its bot had accidentally copied the first attack ended up passing the DAI to the exploiter anyway, because the copied contract had the exploiter's address hard-coded. In the Nomad bridge incident of August 2022, hundreds of addresses simply replayed the first attacker's transaction with their own address substituted. Label these actors separately, with stated confidence, and never publish the identity of a private individual on the strength of such links (see the lesson From address to entity to person: attribution and labeling).
When the bug is not in the code
Some of the largest thefts involved no smart contract flaw at all. In the Ronin bridge theft of March 2022, attackers controlled five of the nine validator keys the bridge required, enough to sign withdrawals of about 173,600 ETH and 25.5 million USDC; it went unnoticed for six days. U.S. authorities attributed the theft to North Korea's Lazarus Group in April 2022. On-chain, the withdrawals were validly signed; only the context (unusual size, recipient and timing) gave them away.
The Bybit theft of February 21, 2025, about $1.5 billion, was the largest crypto theft on record when it happened and is a lesson in reading what signers actually approved. The exchange's cold wallet was a Safe multisig proxy. Investigators found that the wallet interface had been compromised through a developer machine at the wallet provider, so signers saw a routine transfer while their hardware wallets signed something else. Decoded, the signed transaction looks like this:
SafeProxy.execTransaction(...) -- selector 0x6a761202, three valid signatures
to = attacker contract
data = transfer(...) -- harmless-looking function name
operation = 1 -- DELEGATECALL, not CALL
DELEGATECALL attacker contract.transfer(...) -- runs with the proxy's storage
SSTORE slot 0 := malicious implementation -- Safe singleton pointer replaced
Then: sweepETH(...) and sweepERC20(...) called on the proxy, no signatures needed
Because a Safe proxy keeps its implementation pointer in storage slot 0 (see the lesson Decoding smart contract interactions), a delegatecall that writes slot 0 hands over the whole wallet. The drain transactions then moved about 401,346 ETH, 90,375 stETH, 15,000 cmETH and 8,000 mETH. On February 26, 2025, the FBI attributed the theft to North Korean actors it calls TraderTraitor, published a list of addresses, and reported that the funds were being converted to Bitcoin and spread across thousands of addresses on multiple chains. The analyst's takeaways: decode every operation field in multisig transactions, alert on any delegatecall to an unknown contract, and monitor implementation slots of high-value proxies.
Post-incident tracing
The first hours decide how much can be frozen or flagged. A typical sequence:
- Build the seed set: every attacker-controlled address found from the attack transactions, funding and deployer links.
- Watch for freezable assets. Some stablecoin issuers can freeze balances, which is why attackers swap them first; report the addresses fast.
- Share addresses and transaction hashes with exchanges, bridges and other services that may receive the funds, through their established reporting channels.
- Trace consolidation and dispersal forward, hop by hop, across chains (see the lessons Tracing to an evidential standard, Chain-hopping and cross-chain tracing and Custodial mixers).
-- First two hops out of an incident's seed addresses, largest flows first
WITH seeds AS (
SELECT address FROM incident_addresses WHERE incident_id = 'INC-0142'
), hop1 AS (
SELECT t.to_address, SUM(t.amount_usd) AS usd, MIN(t.block_timestamp) AS first_seen
FROM value_transfers t JOIN seeds s ON t.from_address = s.address
WHERE t.to_address NOT IN (SELECT address FROM seeds)
GROUP BY t.to_address
)
SELECT h.to_address AS hop1, t.to_address AS hop2, SUM(t.amount_usd) AS usd
FROM hop1 h
JOIN value_transfers t
ON t.from_address = h.to_address AND t.block_timestamp >= h.first_seen
GROUP BY h.to_address, t.to_address
ORDER BY usd DESC;
The query assumes a value_transfers table that already combines native transfers from traces with token transfers, each valued at the time of transfer. The first CTE finds addresses that received funds from the seeds (excluding moves between seeds); the final step follows each of them one hop further, counting only transfers after the first receipt. Large hop-2 flows into known services are where off-chain records may exist.
Writing up an incident
Classify the root cause, because it determines what evidence matters:
| Root cause class | Public example | Typical data signature |
|---|---|---|
| Logic flaw | Euler, 2023 | Flash loan plus an unusual call sequence leaving an impossible state |
| Initialization or access control | Nomad, 2022 | Many callers succeed with the same calldata |
| Signature verification | Wormhole, 2022 | Tokens minted with no matching deposit |
| Key compromise | Ronin, 2022 | Validly signed withdrawals, no code bug |
| Interface or supply chain | Bybit, 2025 | Validly signed transaction with an unexpected delegatecall |
| Governance | Beanstalk, 2022 | Borrowed voting power, proposal executed in the same transaction |
Value losses at prices at the block of each attack transaction, list every attack transaction with block height, and keep recovered amounts separate from the loss. The lessons Writing the forensic report: confidence and language and Ethics, privacy, data protection and investigator safety cover how to present and share such findings.
Exam focus: the five phases of an exploit and the evidence each leaves; reading the attack transaction from the trace, computing balance changes and netting out flash-loan principal; the Euler attack steps and its root cause (donateToReserves without a health check); funding sources, deployer and bytecode links, failed attempts and copycats as attribution leads; key and interface compromises (Ronin, Bybit) and the Safe delegatecall that rewrote slot 0; first-hours priorities (seed set, freezable assets, notifying services); hop queries over combined native and token transfers; root cause classes and loss valuation at block prices.