DeFi AMM-Rekenkundige Foute & Virtuele Balans-kas Uitbuiting
Tip
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
Oorsig
Yearn Finance’s yETH pool (Nov 2025) het getoon hoe gas-besparende kasse binne komplekse AMMs gewapen kan word wanneer hulle nie tydens grens-toestand-oorgange versoen word nie. Die gewigte stableswap-pool hou toesig oor tot 32 liquid staking derivatives (LSDs), skakel dit om na ETH-ekwivalente virtuele balanse (vb_i = balance_i × rate_i / PRECISION), en stoor daardie waardes in ’n gepakte stoorreeks packed_vbs[]. Wanneer alle LP tokens verbrand word, totalSupply daal korrek tot nul maar die gecachte packed_vbs[i]-slots het reuse historiese waardes behou. Die volgende deposant is as die “first” liquidity provider behandel alhoewel die kas steeds fantoomlikiditeit bevat het, wat ’n aanvaller in staat gestel het om ongeveer 235 septiljoen yETH te mint vir slegs 16 wei voordat ongeveer USD 9M in LSD-onderpand uitgeput is.
Belangrike bestanddele:
- Derived-state caching: expensive oracle lookups are avoided by persisting virtual balances and incrementally updating them.
- Missing reset when
supply == 0:remove_liquidity()proporsionele afnames het nie-nul residu’s inpacked_vbs[]gelos na elke onttrekkingsiklus. - Initialization branch trusts the cache:
add_liquidity()calls_calc_vb_prod_sum()en lees eenvoudig leespacked_vbs[]wanneerprev_supply == 0, en neem aan dat die kas ook op nul gestel is. - Flash-loan financed state poisoning: deposit/withdraw-lusse het afrondingsresidu’s vererger sonder kapitaalslot, wat ’n katastrofiese oor-mint in die “first deposit” pad moontlik gemaak het.
Kas-ontwerp & ontbrekende grensbehandeling
Die kwesbare vloei word hieronder vereenvoudig:
function remove_liquidity(uint256 burnAmount) external {
uint256 supplyBefore = totalSupply();
_burn(msg.sender, burnAmount);
for (uint256 i; i < tokens.length; ++i) {
packed_vbs[i] -= packed_vbs[i] * burnAmount / supplyBefore; // truncates to floor
}
// BUG: packed_vbs not cleared when supply hits zero
}
function add_liquidity(Amounts calldata amountsIn) external {
uint256 prevSupply = totalSupply();
uint256 sumVb = prevSupply == 0 ? _calc_vb_prod_sum() : _calc_adjusted_vb(amountsIn);
uint256 lpToMint = pricingInvariant(sumVb, prevSupply, amountsIn);
_mint(msg.sender, lpToMint);
}
function _calc_vb_prod_sum() internal view returns (uint256 sum) {
for (uint256 i; i < tokens.length; ++i) {
sum += packed_vbs[i]; // assumes cache == 0 for a pristine pool
}
}
Omdat remove_liquidity() slegs proporsionele verminderinge toegepas het, het elke lus vaste-punt afrondingsstof agtergelaat. Na ≳10 deposito/onttrek-siklusse het daardie residue opgebou tot uiters groot skynbare virtual balances terwyl die on-chain token-balansies amper leeg was. Die verbranding van die finale LP-shares het totalSupply op nul gestel, maar die caches het gevul gebly, waarmee die protokol gereedgemaak is vir ’n wanfunksionele initialisering.
Exploit playbook (yETH case study)
- Flash-loan working capital – Lening wstETH, rETH, cbETH, ETHx, WETH, ens. van Balancer/Aave om nie kapitaal vas te sit terwyl die pool gemanipuleer word nie.
- Poison
packed_vbs[]– Loop deposito’s en onttrekkings oor agt LSD-assets. Elke gedeeltelike onttrekking kappacked_vbs[i] − vb_shareaf en laat >0 residue per token oor. Herhaalde lusse blaas skynbare ETH-ekwivalente balanse op sonder om verdenking te wek omdat die werklike balanse min of meer teen mekaar uitkanselleer. - Force
supply == 0– Brand al die oorblywende LP-tokens sodat die pool glo dit is leeg. ’n Implementasie-oorsig laat die vergiftigdepacked_vbs[]onaangeraak. - Dust-size “first deposit” – Stuur ’n totaal van 16 wei verdeeld oor die ondersteunde LSD-slotte.
add_liquidity()sienprev_supply == 0, loop_calc_vb_prod_sum()en lees die verouderde cache in plaas van herberekening vanaf die werklike balanse. Die mint-berekening doen dus asof triljoene USD binnegekom het en emiteer ongeveer ~2.35×10^26 yETH. - Drain & repay – Los die opgeblase LP-positie in vir alle gelokte LSD’s, ruil yETH→WETH op Balancer, omskep na ETH via Uniswap v3, betaal flash loans/fooi terug, en witwasser die wins (bv. deur Tornado Cash). Netto wins ≈ USD 9M terwyl slegs 16 wei eie fondse ooit die pool geraak het.
Generalized exploitation conditions
Jy kan soortgelyke AMMs misbruik wanneer al die volgende waar is:
- Cached derivatives of balances (virtual balances, TWAP snapshots, invariant helpers) bly tussen transaksies bestaan vir gasbesparing.
- Partial updates truncate resultate (floor division, fixed-point rounding), wat ’n aanvaller toelaat om staatshalwe residue op te bou deur simmetriese deposito/onttrek-siklusse.
- Boundary conditions reuse caches in plaas van grondwaarheid herberekening, veral wanneer
totalSupply == 0,totalLiquidity == 0, of die pool-samestelling herbegin. - Minting logic lacks ratio sanity checks (bv. afwesigheid van
expected_value/actual_valuegrense) sodat ’n stof-deposito feitlik die hele historiese voorraad kan mint. - Cheap capital is available (flash loans of interne krediet) om dosyne staat-aanpassende operasies in een transaksie of ’n nou gekoördineerde bundel te uitvoer.
Defensive engineering checklist
- Explicit resets when supply/lpShares hit zero:
if (totalSupply == 0) {
for (uint256 i; i < tokens.length; ++i) packed_vbs[i] = 0;
}
Pas dieselfde behandeling toe op elke gecachte accumulator wat van balanse of orakeldata afgelei is.
- Recompute on initialization branches – Wanneer
prev_supply == 0, ignoreer caches heeltemal en bou virtual balances op van werklike token-balansies + lewendige orakelkoerse. - Minting sanity bounds – Reverter as
lpToMint > depositValue × MAX_INIT_RATIOof as ’n enkele transaksie >X% van die historiese voorraad mint terwyl totale deposito’s onder ’n minimumdrempel is. - Rounding-residue drains – Agregeer per-token stof in ’n sink (treasury/burn) sodat herhaalde proporsionele aanpassings nie caches weg van werklike balanse laat dryf nie.
- Differential tests – Vir elke staatsoorgang (add/remove/swap), herbereken dieselfde invariant off-chain met hoë-presisie wiskunde en assereer gelykheid binne ’n stywe epsilon selfs nadat volledige liquidity onttrek is.
Monitoring & response
- Multi-transaction detection – Volg reekse van byna-simmetriese deposito/onttrek-gebeure wat die pool met lae balanse maar hoë gecachte state agterlaat, gevolg deur
supply == 0. Enkel-transaksie anomalie-detektors mis hierdie vergiftigingsveldtogte. - Runtime simulations – Voordat
add_liquidity()uitgevoer word, herbereken virtual balances van nuuts af en vergelyk met gecachte somme; reverteer of pauzeer as deltas ’n basispunt-drempel oorskry. - Flash-loan aware alerts – Merk transaksies wat groot flash loans, uitputtende pool-ontrrekke en ’n stof-grootte finale deposito kombineer; blokkeer of vereis handmatige goedkeuring.
References
Tip
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.


