2023년 11월 23일, KyberSwap을 대상으로 한 일련의 공격이 발생했습니다. 이 공격들로 인해 총 4,800만 달러 이상의 손실이 발생했습니다. 근본적인 문제는 KyberSwap의 재투자 과정에서 발생한 잘못된 반올림 방향에서 비롯되었습니다. 이로 인해 부적절한 틱 계산이 이루어졌고, 궁극적으로 유동성의 이중 계산이 발생했습니다.
저희는 "또 하나의 정밀도 손실 비극: KyberSwap 사건의 심층 분석"이라는 포괄적인 보고서를 발표했으며, 이 보고서는 사건의 세부 사항을 심층적으로 다루고 있습니다. 더 깊은 이해를 위해 전체 분석을 참고하시길 권장합니다. 아래에서는 이 사건을 2023년 10대 보안 사고 중 하나로 소개하는 간략한 개요를 제공합니다.
배경
KyberSwap은 분산형 자동화 시장 조성자(CLAMM) 플랫폼입니다. 집중 유동성에 대한 시장 수요를 충족하기 위해 KyberSwap Elastic은 Uniswap V3를 기반으로 출시되었으며, 유동성 공급 수익의 자동 복리를 가능하게 하는 재투자 곡선을 포함한 여러 개선 사항이 적용되었습니다.
1. 틱과 제곱근 가격
Uniswap V3 유형의 CLAMM에서 Tick은 LP가 전체 범위 대신 고정된 범위 내에서 유동성을 제공할 수 있도록("집중"이라는 용어가 여기서 유래) 가격을 이산적인 방식으로 표시하는 데 사용됩니다.
유동성은 임의의 두 틱 사이의 범위(인접할 필요 없음), 즉 틱 인덱스 쌍(하위 틱과 상위 틱)에 배치될 수 있습니다. 구체적으로, 각 틱의 가격(정수 인덱스 i에서)은 다음과 같이 정의됩니다:
실제로는 제곱근 가격(sqrtP 또는 sqrtPrice로 표기)이 사용됩니다:
현재 제곱근 가격을 기반으로 현재 틱을 계산하는 것도 가능합니다:
명백히, 주어진 틱에 대해 하나의 제곱근 가격만 계산되지만, 여러 제곱근 가격이 동일한 틱을 가리킬 수 있습니다. 더 자세한 설명은 Uniswap V3 및 KyberSwap의 문서를 참고하십시오.
2. 재투자 곡선
Uniswap V3 기반 CLAMM은 LP 수수료의 풀 활용도 문제와 재투자에 필요한 상당한 가스 비용 문제를 겪고 있습니다. 이에 따라 KyberSwap은 이 문제를 해결하기 위해 재투자 곡선을 채택했습니다.
재투자 곡선의 핵심은 각 스왑에서 수집된 수수료가 무한한 범위 내에서 재투자 유동성으로서 풀에 추가 유동성으로 누적된다는 것입니다. 재투자 토큰은 LP에게 발행되며 누적된 재투자 유동성은 그에 따라 LP에게 할당됩니다. 또한, 재투자 유동성은 스왑 및 가격 계산 과정에도 참여합니다.
위에서 소개한 계산에 해당하는 코드는 해당 풀의 다음 코드 스니펫에서 computeSwapStep 함수에 나타나 있습니다.

이 함수에서 재투자 유동성으로 인해 liquidity는 두 가지 구성 요소의 합임을 주목해야 합니다: 기본 유동성을 위한 baseL과 재투자를 위한 누적 유동성인 reinvestL.
3. KyberSwap에서의 스왑
앞서 논의한 KyberSwap 풀의 swap 함수 구현은 아래 다이어그램으로 추상화할 수 있습니다:

틱 계산과 관련된 핵심 로직은 파란색 사각형으로 강조된 스와핑 while 루프 내에 있습니다. 구체적으로, 주요 로직은 computeSwapStep 함수와 _updateLiquidityAndCrossTick 함수로 구성됩니다. 전자는 주어진 스왑에 대한 입출력 금액 및 nextSqrtP와 같은 주요 상태를 계산하고, 후자는 크로스틱이 발생할 때의 경우를 처리합니다.
전통적으로 가격이 상승할 때 틱이 오른쪽/위쪽으로 이동한다고 하며, 반대의 경우 틱이 왼쪽/아래쪽으로 이동한다고 합니다.
나중에 논의될 취약점을 더 잘 이해하기 위해서는 다음 그림에 나타난 computeSwapStep 함수의 관련 코드 로직을 살펴보는 것이 필수적입니다:

구체적으로, calcReachAmount 함수는 목표 가격 targetSqrtP에 필요한 입력 토큰을 계산합니다(50-57번 줄). usedAmount가 specifiedAmount보다 크면 틱이 넘어가지 않으며, nextSqrtP는 deltaL(즉, 델타 유동성, 59-62번 줄)로부터 계산됩니다. deltaL은 estimateIncrementalLiquidity 함수를 사용하여 결정되고, 최종 가격 nextSqrtP는 calcFinalPrice 함수로 계산됩니다(70-79번 줄). 필요한 입력이 더 적으면 nextSqrtP는 다음 틱의 가격으로 설정되지만, 이 경우는 공격에서 사용되지 않습니다.
위에서 설명한 단계들로 보면, 틱이 넘어가지 않는 경우 computeSwapStep이 반환하는 nextSqrtP는 다음 틱의 sqrtP보다 크지 않아야 합니다. 그러나 유동성(기본 유동성과 델타 유동성)에 대한 가격 의존성과 정밀도 손실로 인해, 공격자는 틱이 넘어가지 않는 상태에서 nextSqrtP를 더 크게 조작할 수 있습니다.
취약점 분석
근본 원인은 SwapMath 컨트랙트(computeSwapStep 함수에 의해 호출됨)의 델타 유동성 계산(즉, estimateIncrementalLiquidity 함수) 내에서 잘못된 반올림 방향으로 인한 결함 있는 틱 계산에 있습니다. 이는 차례로 이후의 틱 계산에 부적절하게 영향을 미칩니다.

흥미롭게도, 188번 줄의 주석(파란색 사각형으로 강조)을 살펴보면, deltaL은 nextSqrtP를 내림하기 위해 올림 처리되어야 함을 알 수 있습니다. 그러나 189번 줄에서 mulDivFloor 함수를 사용함으로써 deltaL이 잘못되게 내림 처리됩니다. 결과적으로 nextSqrtP는 부정확하게 올림 처리됩니다.
공격 분석
공격자들은 여러 공격 트랜잭션을 시작했으며, 각 트랜잭션은 여러 풀을 드레인했습니다. 간단하게 설명하기 위해 다음 논의는 공격 트랜잭션 내의 첫 번째 공격을 기반으로 합니다.
핵심 공격 로직은 다음 여섯 단계로 구성됩니다:
-
AAVE에서 플래시 론을 통해 2,000 WETH를 차용합니다.
-
피해자 풀 0xfd7b에서 6.850 WETH를 6.371 frxETH로 스왑합니다. 이 단계는 현재 틱과
currentSqrtP를 현재 유동성이 없는 위치로 밀어 넣는 데 사용됩니다.
currentSqrtP는 공격자가 무작위로 선택한 것처럼 보이며, 스왑은 이 가격에서 정확히 멈춥니다.- 이 단계 이후 기본 유동성(
baseL)은 0이지만 재투자 유동성(reinvestL)은 0이 아닙니다.
- 풀에 유동성을 추가한 다음 유동성의 일부를 제거합니다. 이 단계는 범위와 총 유동성을 원하는 양으로 제어하는 데 사용됩니다.
- 틱 범위는
currentSqrtP를 기반으로 선택됩니다. - 공격에 필요한 유동성은 틱 범위에서 도출될 수 있지만, 해당 계산 로직에는 추가 탐색이 필요합니다.
- 풀에서 387.170 WETH를 0.06 frxETH로 스왑합니다. 이 단계는 **
nextTick==currentTick**이 되도록 현재 틱을 조작하는 데 사용됩니다. 구체적으로, 4단계의 스왑은 교묘하게 풀을 속여 틱 111,310이 넘어가지 않았다고 믿게 만듭니다. 그러나 실제로currentSqrtP는 틱 111,310의sqrtP보다 큽니다.
- 풀에서 0.06 frxETH를 396.244 WETH로 스왑합니다. 스왑 방향이 이전 단계와 반대임에 유의하십시오. 이 단계에서는 스왑을 수익성 있게 만들고 결과적으로 풀을 드레인하기 위해 유동성이 이중으로 계산됩니다.
- 플래시 론을 상환하고 6.364 WETH와 1.117 frxETH를 수확합니다.
심층적인 검토를 위해서는 계산 및 그림을 포함한 더 많은 세부 정보가 담긴 저희의 종합 분석을 참고하십시오.
요약
이 사건의 근본적인 문제는 KyberSwap의 재투자 과정에서 발생한 잘못된 반올림에서 비롯되었으며, 이로 인해 부정확한 틱 계산과 궁극적으로 유동성의 이중 계산이 발생했습니다. 이 사건은 DeFi 프로토콜 내 정밀도 손실 문제의 복잡하고 파악하기 어려운 특성을 부각시키며, 전체 커뮤니티에 심각한 도전을 제기합니다.
2023년의 이 공격은 매우 복잡하고 섬세한 계산이 특징이며, 커뮤니티를 크게 시험한 많은 정밀도 관련 보안 사고의 대표적인 사례로 꼽힙니다. 또한, 당국과의 광범위한 협상 끝에 공격자는 도발적인 어조의 메시지를 공개하여 프로토콜에 대한 완전한 통제권을 요구했습니다.
이 시리즈의 다른 글 읽기:
- 도입: 2023년 10대 "주목할 만한" 보안 사고
- #1: 플래시봇 릴레이의 취약점을 악용한 MEV 봇 수확
- #2: Euler Finance 사건: 2023년 최대 규모의 해킹
- #4: Curve 사건: 컴파일러 오류로 인해 정상 소스 코드에서 잘못된 바이트코드 생성
- #5: Platypus Finance: 운 좋게 세 번의 공격에서 살아남다
- #6: Hundred Finance 사건: 취약한 포크 프로토콜에서 정밀도 관련 익스플로잇의 물결을 촉발시키다
- #7: ParaSpace 사건: 업계 최대 규모의 공격을 막기 위한 시간과의 싸움
- #8: SushiSwap 사건: 어설픈 구조 시도가 일련의 모방 공격으로 이어지다
- #9: MEV 봇 0xd61492: 독창적인 익스플로잇에서 포식자에서 먹잇감으로
- #10: ThirdWeb 사건: 신뢰할 수 있는 모듈 간의 비호환성으로 취약점 노출





