DeFi AMM 회계 버그 및 가상 잔액 캐시 악용
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
개요
Yearn Finance의 yETH 풀(2025년 11월)은 복잡한 AMMs 내부의 가스 절약용 캐시가 경계 상태 전환 중에 정산되지 않을 경우 어떻게 악용될 수 있는지를 드러냈습니다. 가중 stableswap 풀은 최대 32개의 liquid staking derivatives (LSDs)를 추적하고, 이를 ETH 동등값인 virtual balances (vb_i = balance_i × rate_i / PRECISION)로 변환하여 패킹된 스토리지 배열 packed_vbs[]에 저장합니다. 모든 LP 토큰이 소각되면 totalSupply는 올바르게 0으로 떨어지지만 캐시된 packed_vbs[i] 슬롯들은 엄청난 과거 값을 유지했습니다. 이후의 예금자는 캐시가 유령 유동성을 여전히 보유하고 있었음에도 불구하고 “첫 번째” 유동성 공급자로 취급되었고, 공격자는 약 235 septillion yETH을 단지 16 wei로 민트한 뒤 ≈USD 9M 상당의 LSD 담보를 탈취할 수 있었습니다.
핵심 요소:
- Derived-state caching: 비싼 오라클 조회를 피하기 위해 가상 잔액을 지속 저장하고 점진적으로 업데이트합니다.
- Missing reset when
supply == 0:remove_liquidity()의 비례 감소가 각 출금 사이클 후packed_vbs[]에 0이 아닌 잔여값을 남겼습니다. - Initialization branch trusts the cache:
add_liquidity()가_calc_vb_prod_sum()을 호출하고prev_supply == 0일 때 캐시도 0으로 초기화되었다고 가정하여 단순히packed_vbs[]를 읽습니다. - Flash-loan financed state poisoning: 예치/출금 루프가 자본 잠금 없이 반올림 잔여를 증폭시켜 ‘첫 예치’ 경로에서 치명적인 과다 민트를 가능하게 했습니다.
캐시 설계 및 경계 처리 누락
취약한 흐름은 아래와 같이 단순화됩니다:
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
}
}
Because remove_liquidity() only applied proportional decrements, every loop left fixed-point rounding dust. After ≳10 deposit/withdraw cycles those residues accumulated into extremely large phantom virtual balances while the on-chain token balances were almost empty. Burning the final LP shares set totalSupply to zero yet caches stayed populated, priming the protocol for a malformed initialization.
Exploit playbook (yETH case study)
- Flash-loan working capital – 풀을 조작하는 동안 자본을 묶지 않기 위해 Balancer/Aave에서 wstETH, rETH, cbETH, ETHx, WETH 등을 차용한다.
- Poison
packed_vbs[]– 8개 LSD 자산을 대상으로 예치/인출을 반복한다. 각 부분 인출은packed_vbs[i] − vb_share를 truncates하여 토큰당 >0의 잔여물을 남긴다. 반복 루프는 실제 잔액이 대체로 상쇄되기 때문에 의심을 유발하지 않으면서 팬텀 ETH 상당 잔액을 부풀린다. - Force
supply == 0– 남아있는 모든 LP 토큰을 소각해 풀이 비어있다고 인식하게 한다. 구현 상의 누락으로 인해 오염된packed_vbs[]는 그대로 남는다. - Dust-size “first deposit” – 지원되는 LSD 슬롯들에 걸쳐 총 16 wei를 분배해서 보낸다.
add_liquidity()는prev_supply == 0을 감지하고_calc_vb_prod_sum()을 실행하며 실제 잔액에서 재계산하지 않고 오래된 캐시를 읽는다. 따라서 민트 계산은 수조 달러가 들어온 것처럼 작동해 ~2.35×10^26 yETH를 발행한다. - Drain & repay – 부풀려진 LP 포지션을 모든 보관된 LSD로 상환하고, Balancer에서 yETH→WETH로 스왑한 뒤 Uniswap v3를 통해 ETH로 변환하고, 플래시론/수수료를 상환한 뒤 이익을 세탁(예: Tornado Cash)한다. 순이익은 약 USD 9M이고 본인 자금은 단지 16 wei만 풀이 접촉했다.
Generalized exploitation conditions
You can abuse similar AMMs when all of the following hold:
- Cached derivatives of balances (virtual balances, TWAP snapshots, invariant helpers) persist between transactions for gas savings.
- Partial updates truncate results (floor division, fixed-point rounding), letting an attacker accumulate stateful residues via symmetric deposit/withdraw cycles.
- Boundary conditions reuse caches instead of ground-truth recomputation, especially when
totalSupply == 0,totalLiquidity == 0, or pool composition resets. - Minting logic lacks ratio sanity checks (e.g., absence of
expected_value/actual_valuebounds) so a dust deposit can mint essentially the entire historic supply. - Cheap capital is available (flash loans or internal credit) to run dozens of state-adjusting operations inside one transaction or tightly choreographed bundle.
Defensive engineering checklist
- Explicit resets when supply/lpShares hit zero:
if (totalSupply == 0) {
for (uint256 i; i < tokens.length; ++i) packed_vbs[i] = 0;
}
Apply the same treatment to every cached accumulator derived from balances or oracle data.
- Recompute on initialization branches – When
prev_supply == 0, ignore caches entirely and rebuild virtual balances from actual token balances + live oracle rates. - Minting sanity bounds – Revert if
lpToMint > depositValue × MAX_INIT_RATIOor if a single transaction mints >X% of historic supply while total deposits are below a minimal threshold. - Rounding-residue drains – Aggregate per-token dust into a sink (treasury/burn) so repeated proportional adjustments do not drift caches away from real balances.
- Differential tests – For every state transition (add/remove/swap), recompute the same invariant off-chain with high-precision math and assert equality within a tight epsilon even after full liquidity drains.
Monitoring & response
- Multi-transaction detection – Track sequences of near-symmetric deposit/withdraw events that leave the pool with low balances but high cached state, followed by
supply == 0. Single-transaction anomaly detectors miss these poisoning campaigns. - Runtime simulations – Before executing
add_liquidity(), recompute virtual balances from scratch and compare with cached sums; revert or pause if deltas exceed a basis-point threshold. - Flash-loan aware alerts – Flag transactions that combine large flash loans, exhaustive pool withdrawals, and a dust-sized final deposit; block or require manual approval.
References
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.


