← Dashboard
BNB Smart Chain
Chain ID: 56
PMA Case #—
Spartan Protocol
calcLiquidityShare()가 현재 잔고(balanceOf)를 참조하여, 직접 전송으로 풀 잔고를 부풀린 뒤 LP 소각 시 초과 인출이 가능한 취약점을 이용, ~$30.5M 탈취
LP Share Inflation
Flash Loan
Single Tx
Protocol Logic Flaw
1. 프로토콜 의존성 구조도
전체 공격 흐름: 공격자는 PancakeSwap에서 100,000 WBNB를 플래시론으로 차입한 뒤, Spartan Pool(SPARTA/WBNB)에서 반복 스왑 → LP 획득 → 잔고 인플레이션 → LP 소각 → 초과 인출 사이클을 반복하여 풀을 고갈시켰다.
Single atomic transaction — Block #7,048,833
Attacker 0x3b6e...3671
PancakeSwap Flash Loan
Spartan Pool SPARTA/WBNB 0x3de6...426f
Utils Contract calcLiquidityShare()
Attack Contract 0x2883...6A51
SPT1-WBNB LP Pool Token
100K WBNB
1
Swap WBNB→SPARTA
2
Add Liq → Mint LP
3
Direct Transfer (Inflate)
4
Burn LP → Overclaim
5
balanceOf() ⚠
!
100,260 WBNB
7
2. 종합 대시보드
2-1. 트랜잭션 정보
Date / Time 2021-05-01 16:38:39 UTC
Attack Type LP Share Inflation (Protocol Logic Flaw)
Total Loss ~$30,500,000
Flash Loan 100,000 WBNB (~$62.42M)
Flash Loan Fee 260 WBNB (~$162,286)
Attack Cycles 8 cycles (460 events)
Gas Fee ~$66
2-2. 공격 실행 흐름
Step 1 — Flash Loan
PancakeSwap에서 100,000 WBNB 플래시론 차입
PancakeSwap의 CAKE-WBNB LP 풀에서 100,000 WBNB를 플래시론으로 차입. 트랜잭션 종료 시 260 WBNB(0.26%) 수수료 포함 상환 필요.
100,000 WBNB (~$62.42M)
Step 2 — Swap WBNB → SPARTA (5회 분할)
슬리피지 보호 우회를 위한 분할 스왑
Spartan Pool에서 1,913 WBNB씩 5회 반복 스왑. 슬리피지 기반 수수료 체계를 우회하기 위해 분할 실행. 총 9,566 WBNB → 2,536,613 SPARTA 획득.
9,566 WBNB → 2,536,613 SPARTA
Step 3 — Add Liquidity & Mint LP
LP 토큰 획득을 위한 유동성 추가
2,536,613 SPARTA + 11,853 WBNB를 풀에 유동성으로 추가하여 933,351 SPT1-WBNB LP 토큰 발행(mint).
933,351 LP tokens minted
Step 4 — Swap WBNB → SPARTA (10회 분할)
풀 인플레이션용 SPARTA 매집
1,674 WBNB씩 10회 반복 스왑으로 2,639,122 SPARTA 추가 확보. 이 자금은 다음 단계에서 풀 잔고 인플레이션에 사용됨.
16,740 WBNB → 2,639,122 SPARTA
Step 5 — 풀 잔고 인플레이션 ⚠
addLiquidity()를 거치지 않고 직접 전송으로 풀 잔고 부풀리기
21,632 WBNB + 2,639,122 SPARTA를 풀 컨트랙트에 직접 전송 (ERC20 transfer). addLiquidity()를 통하지 않았으므로 풀의 내부 추적 변수(baseAmountPooled/tokenAmountPooled)는 갱신되지 않지만, 실제 토큰 잔고(balanceOf)는 증가. 이것이 취약점의 핵심 트리거.
21,632 WBNB + 2,639,122 SPARTA → Pool에 직접 전송
Step 6 — LP 소각 → 초과 인출
부풀린 잔고 기준으로 LP 소각하여 초과 자산 인출
933,351 LP 토큰을 소각(burn). calcLiquidityShare()가 인플레이션된 balanceOf()를 참조하여 LP 지분을 계산. 결과적으로 Step 3에서 예치한 것보다 훨씬 많은 20,694 WBNB + 2,538,199 SPARTA 인출. 11,853 WBNB만 예치했으므로 ~9K WBNB 순이익.
LP 소각 → 20,694 WBNB + 2,538,199 SPARTA (순이익 ~9K WBNB)
Step 7 — 잔여 자산 회수
인플레이션 자산을 유동성으로 추가 후 즉시 인출
Step 5에서 전송한 자산을 addLiquidity()로 추가 → 1,414,010 LP 발행 → 즉시 소각 → 2,643,882 SPARTA + 21,556 WBNB 회수.
21,556 WBNB + 2,643,882 SPARTA 회수
Step 8 — 반복 & 추가 드레인
동일 사이클 반복으로 풀 고갈
위 사이클(Step 2~7)을 총 8회 반복. 매 사이클마다 SPARTA를 풀에 스왑백하여 추가 WBNB를 확보. 반복할수록 풀 유동성이 감소하여 드레인 가능 WBNB가 급감 (사이클 1: 21,284 WBNB → 사이클 8: 1,111 WBNB).
Step 9 — Flash Loan 상환 & 이익 확정
100,260 WBNB 상환 후 순이익 확보
PancakeSwap에 100,000 + 260(수수료) = 100,260 WBNB 상환. 잔여 자금이 공격자 지갑(0x3b6e)으로 이전.
순이익: ~$30.5M (30.7K WBNB ≈ $19.16M + 4.6M SPARTA ≈ $11.56M + 기타)
2-3. 관련 엔티티 & PnL
Attacker (EOA)
ATTACKER
PnL: +$30,500,000
Attack Contract
ATTACKER
공격 로직 실행 컨트랙트
Spartan Pool (V1)
VICTIM
PnL: −$30,500,000
PancakeSwap
FLASH LOAN
PnL: +260 WBNB ($162,287)
2-4. 취약점 상세 — calcLiquidityShare()
정상 동작 (Uniswap 방식)
잔고 참조 캐시된 변수
baseAmountPooled 내부 추적값
인출 비율 LP/totalSupply
조작 가능성 없음
⚠ Spartan 구현 (취약)
잔고 참조 IERC20.balanceOf()
실시간 잔고 조작 가능
인출 비율 LP × (현재잔고/totalLP)
조작 가능성 직접 전송으로 부풀리기
LP 예치 (정상 경로)
WBNB 예치 11,853
SPARTA 예치 2,536,613
LP 발행 933,351
기대 인출 ~11,853 WBNB
⚠ 인플레이션 후 실제 인출
인플레이션 전송 21,632 WBNB
LP 소각 933,351
실제 인출 20,694 WBNB
1회 순이익 ~8,841 WBNB
2-5. Key Events (전체 8 사이클, 온체인 이벤트 로그 기반)
# Event From To Amount Deviation
Flash Loan (event #819)
1 Swap (Flashloan borrow) PancakeSwap LP Attack Contract 100,000 WBNB —
Cycle 1 (events #81–#171)
2 Swapped (×5) Attack CA Spartan Pool 9,565.86 WBNB → 2,536,613.21 SPARTA
$666 avg ($817→$538) +6.6%
3 AddLiquidity Attack CA Spartan Pool 2,536,613 SPARTA + 11,853.33 WBNB —
4 Swapped (×10) Attack CA Spartan Pool 16,740.26 WBNB → 2,639,121.98 SPARTA
$396 avg ($505→$308) −36.6%
5 Direct Transfer ⚠ Attack CA Spartan Pool 21,632.15 WBNB + 2,639,121.98 SPARTA —
6 RemoveLiquidity (burn) Spartan Pool Attack CA 933,351 LP → 20,694.06 WBNB + 2,538,199.15 SPARTA —
7 AddLiquidity (잔여) Attack CA Spartan Pool 2,639,121.98 SPARTA + 21,632.15 WBNB —
8 RemoveLiquidity (회수) Spartan Pool Attack CA 1,414,010 LP → 21,555.70 WBNB + 2,643,882.07 SPARTA —
9 Swapped (×10, drain) Attack CA Spartan Pool 5,182,081 SPARTA → 21,283.57 WBNB
$2,563 avg (3,562→1,230) WBNB 점감
Cycle 2 (events #174–#264)
10 Swapped (×5) Attack CA Spartan Pool 9,565.86 WBNB → 2,786,314.99 SPARTA
$732 avg ($918→$576) +17.3%
11 AddLiquidity → Swapped(×10) → Inflate → Burn → Recover 동일 패턴 RemoveLiquidity 21,928.88 + 21,827.80 WBNB —
12 Swapped (×10, drain) Attack CA Spartan Pool 5,565,507 SPARTA → 20,499.44 WBNB
$2,300 avg (3,600→1,122) WBNB 점감
Cycle 3 (events #267–#357)
13 Swapped (×5) + Exploit cycle Attack CA Spartan Pool RemoveLiquidity: 23,538.47 + 22,205.56 WBNB —
14 Swapped (×10, drain) Attack CA Spartan Pool 6,041,924 SPARTA → 19,452.16 WBNB
$2,010 avg (3,635→990) WBNB 점감
Cycle 4 (events #360–#450)
15 Swapped (×5) + Exploit cycle Attack CA Spartan Pool RemoveLiquidity: 25,821.03 + 22,660.51 WBNB —
16 Swapped (×11, drain) Attack CA Spartan Pool 7,312,798 SPARTA → 19,323.36 WBNB
$1,650 avg (3,648→542) WBNB 점감
Cycle 5 (events #453–#543)
17 Swapped (×5) + Exploit cycle Attack CA Spartan Pool RemoveLiquidity: 29,059.50 + 23,188.18 WBNB —
18 Swapped (×11, drain) Attack CA Spartan Pool 8,174,506 SPARTA → 15,735.46 WBNB
$1,201 avg (3,579→629) WBNB 점감
Cycle 6 (events #546–#636)
19 Swapped (×5) + Exploit cycle Attack CA Spartan Pool RemoveLiquidity: 33,971.10 + 23,720.00 WBNB —
20 Swapped (×11, drain) Attack CA Spartan Pool 9,297,240 SPARTA → 12,223.30 WBNB
$821 avg (3,253→395) WBNB 점감
Cycle 7 (events #639–#729)
21 Swapped (×5) + Exploit cycle Attack CA Spartan Pool RemoveLiquidity: 41,285.70 + 23,971.79 WBNB —
22 Swapped (×10, drain) Attack CA Spartan Pool 9,691,157 SPARTA → 6,898.39 WBNB
$444 avg (2,155→167) WBNB 점감
Cycle 8 (events #732–#813)
23 Swapped (×5) + Exploit cycle Attack CA Spartan Pool RemoveLiquidity: 48,766.72 + 23,902.15 WBNB —
24 Swapped (×7, drain) Attack CA Spartan Pool 7,542,829 SPARTA → 1,111.12 WBNB
$92 avg (216→55) WBNB 급감
Flash Loan 상환 (event #819)
F Swap (Flashloan repayment) Attack Contract PancakeSwap LP 100,260 WBNB → 100,000 WBNB —
📊 사이클별 드레인 효율 감소: Cycle 1에서 21,284 WBNB를 드레인했으나, Cycle 8에서는 단 1,111 WBNB만 가능했다. 풀 유동성이 고갈되면서 매 사이클마다 드레인 가능 금액이 급감. 이는 공격자가 8사이클 이후 자동 중단한 이유이다.
2-6. 유사 공격 비교표
공격 날짜 체인 취약점 손실
★ Spartan Protocol 2021-05-01 BSC LP Share Inflation (balanceOf) ~$30.5M
Uranium Finance 2021-04-28 BSC Balance Calculation Bug ~$57.2M
Warp Finance 2020-12-18 ETH LP Token Oracle Manipulation ~$7.7M
Cheese Bank 2020-11-06 ETH LP Token Price Manipulation ~$3.3M
PancakeBunny 2021-05-20 BSC Price Manipulation + Flash Loan ~$45M
2-7. Root Causes & Lessons
취약점
calcLiquidityShare()가 캐시된 내부 추적 변수 대신 IERC20(token).balanceOf(pool)을 실시간 조회하여 LP 지분을 계산
슬리피지 보호 메커니즘이 분할 호출(여러 건의 소액 스왑)로 쉽게 우회 가능
addLiquidity() 없이 직접 전송된 토큰이 잔고에 반영되지만 내부 추적에는 미반영 — 이 불일치가 공격의 핵심
CertiK 감사를 받았으나 이 취약점이 발견되지 않음
교훈
유동성 계산 시 반드시 캐시된 내부 변수(baseAmountPooled/tokenAmountPooled)를 사용해야 함 — Uniswap V2의 reserve0/reserve1 패턴 참조
프로토콜 레벨에서 분할 호출 우회에 대한 추가 방어 필요 (단일 트랜잭션 내 호출 횟수 제한 등)
"독자적으로 작성한 코드"는 검증된 구현 대비 더 높은 감사 기준이 필요 — Uniswap 포크가 아닌 커스텀 AMM은 특히 주의
직접 전송(transfer)과 프로토콜 함수 호출의 차이를 항상 고려 — 잔고 불일치 공격에 대한 불변 검증(invariant check) 필수
2-8. 공격 당시 토큰 가격 (tokenPriceCache 기준)
SPARTA (공격 후)
~$1.00
공격 후 −60% 급락
공정 교환비
248.68
SPARTA/WBNB (공정가 기준)
💡 핵심: 이 공격은 가격 오라클 조작이 아닌 프로토콜 로직 결함 이다. calcLiquidityShare()가 실시간 balanceOf()를 참조하기 때문에, 직접 전송으로 풀 잔고를 부풀린 뒤 LP를 소각하면 예치보다 많은 자산을 인출할 수 있었다.
3. 자금 흐름 (Fund Flow)
단계별 버튼을 클릭하여 공격 자금 흐름을 추적하세요.
PancakeSwap
Flash Loan
Attacker CA
0x2883...6A51
Spartan Pool
SPARTA/WBNB
Attacker EOA
0x3b6e...3671
100K WBNB
9,566 WBNB
2.5M SPARTA
Add Liq
933K LP
21.6K WBNB + 2.6M SPARTA
(Direct Transfer ⚠)
20.7K WBNB + 2.5M SPARTA
Burn LP → Overclaim
Repeat drain ×N
100,260 WBNB
30.7K WBNB + 4.6M SPARTA
WBNB flow
SPARTA / LP flow
Repay
Exploit (inflate/drain)
4. WBNB 잔고 변화 (Waterfall)
Flash Loan 유입
사이클별 순이익
Flash Loan 상환
Net profit (30,842 WBNB)