Balancer가 $120M 도난당한 취약점 기술 분석
서문
2025년 11월 3일, Balancer 프로토콜이 Arbitrum, Ethereum 등 여러 공공 블록체인에서 해킹을 당해 1억 2천만 달러의 자산 손실이 발생했습니다. 공격의 핵심 원인은 정밀도 손실과 불변값(Invariant) 조작의 이중 취약점에 있습니다.
이번 공격의 핵심 문제는 프로토콜이 소액 거래를 처리하는 논리에 있습니다. 사용자가 소액 교환을 할 때, 프로토콜은 _upscaleArray 함수를 호출하며, 이 함수는 mulDown을 사용하여 수치를 내림합니다. 거래의 잔액과 입력 금액이 동시에 특정 내림 경계(예: 8-9 wei 구간)에 있을 경우, 상대적인 정밀도 오류가 발생합니다.
정밀도 오류는 프로토콜의 불변값 D 계산 과정으로 전달되어 D 값이 비정상적으로 축소됩니다. D 값의 변동은 Balancer 프로토콜 내의 BPT(Balancer Pool Token) 가격을 직접적으로 낮추며, 해커는 이 낮아진 BPT 가격을 이용해 사전에 설계된 거래 경로를 통해 차익 거래를 완료하여 결국 막대한 자산 손실을 초래했습니다.
취약점 이용 Tx:
https://etherscan.io/tx/0x6ed07db1a9fe5c0794d44cd36081d6a6df103fab868cdd75d581e3bd23bc9742
자산 이동 Tx:
https://etherscan.io/tx/0xd155207261712c35fa3d472ed1e51bfcd816e616dd4f517fa5959836f5b48569
기술 분석
공격 진입점
공격의 진입점은 Balancer: Vault 계약이며, 해당 진입 함수는 batchSwap 함수로, 내부에서 onSwap을 호출하여 토큰 교환을 수행합니다.

함수의 매개변수와 제한 사항을 통해 몇 가지 정보를 얻을 수 있습니다:
공격자는 Vault를 통해 이 함수를 호출해야 하며, 직접 호출할 수 없습니다.
함수 내부에서 _scalingFactors()를 호출하여 스케일링 인자를 가져와 스케일링 작업을 수행합니다.
스케일링 작업은 _swapGivenIn 또는 _swapGivenOut에 집중되어 있습니다.
공격 패턴 분석
BPT 가격의 계산 메커니즘 :
Balancer의 안정적인 풀 모델에서, BPT 가격은 중요한 참고 기준으로, 사용자가 얼마나 많은 BPT를 얻고 각 BPT가 얼마나 많은 자산을 얻는지를 결정합니다.

여기서 D = 불변값(Invariant)은 Curve의 StableSwap 모델에서 유래합니다.
풀의 교환 계산에서:

여기서 BPT 가격 기준 역할을 하는 부분은 불변값 D이며, 즉 BPT 가격을 조작하려면 D를 조작해야 합니다. 다음은 D의 계산 과정을 분석합니다:

위 코드에서, D의 계산 과정은 스케일링된 balances 배열에 의존합니다. 즉, 이러한 balances의 정밀도를 변경하는 작업이 필요하며, 이는 D 계산 오류를 초래합니다.
정밀도 손실의 근원:

스케일링 작업:

위의 _upscaleArray를 통해, 잔액이 매우 작을 경우(예: 8-9 wei), mulDown의 내림이 상당한 정밀도 손실을 초래합니다.
공격 프로세스 상세 설명

이러한 교환은 모두 동일한 batchSwap 거래 내에서 이루어지며, 동일한 잔액 상태를 공유하지만, 매번 교환 시 _upscaleArray를 호출하여 balances 배열을 수정합니다.
Callback 메커니즘의 결여
주 프로세스는 Vault가 시작되며, 어떻게 정밀도 손실이 누적되었는지에 대한 답은 balances 배열의 전달 메커니즘에 있습니다.

위 코드를 분석하면, 매번 onSwap을 호출할 때 Vault가 새로운 currentBalances 배열을 생성하지만, Batch Swap 내에서는:
첫 번째 교환 후 잔액이 업데이트됩니다(하지만 정밀도 손실로 인해 업데이트된 값이 부정확할 수 있습니다).
두 번째 교환은 첫 번째 결과를 기반으로 계속 계산됩니다.
정밀도 손실이 누적되어 결국 불변값 D가 현저히 작아집니다.
핵심 문제:

비록 Vault가 매번 새로운 배열을 전달하지만:
- 잔액이 매우 작을 경우(8-9 wei), 스케일링 시 정밀도 손실이 큽니다.
- Batch Swap 내에서 후속 교환은 이미 손실된 정밀도의 잔액을 기반으로 계속 계산됩니다.
- 불변값 D의 변화가 합리적인 범위 내에 있는지 검증하지 않았습니다.
요약
Balancer의 이번 공격은 다음과 같은 몇 가지 원인으로 요약됩니다:
스케일링 함수가 내림을 사용함: _upscaleArray가 mulDown을 사용하여 스케일링할 때, 잔액이 매우 작을 경우(예: 8-9 wei) 상당한 상대 정밀도 손실이 발생합니다.
불변값 계산이 정밀도에 민감함: 불변값 D의 계산은 스케일링된 balances 배열에 의존하며, 정밀도 손실은 D의 계산에 직접적으로 전달되어 D를 작게 만듭니다.
불변값 변화 검증 부족: 교환 과정에서 불변값 D의 변화가 합리적인 범위 내에 있는지 검증하지 않아 공격자가 정밀도 손실을 반복적으로 이용해 BPT 가격을 낮출 수 있었습니다.
Batch Swap 내 정밀도 손실 누적: 동일한 batch swap 내에서 여러 번의 교환으로 인한 정밀도 손실이 누적되어 결국 막대한 재정 손실로 확대됩니다.
이 두 가지 문제인 정밀도 손실 + 검증 부족이 공격자가 경계 조건을 정교하게 설계한 것과 결합되어 이번 손실을 초래했습니다.












