Back to Blog

#3: KyberSwap 사건: 극도로 정교한 계산을 통한 반올림 오류의 완벽한 악용

Code Auditing
February 10, 2024
6 min read

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번 줄). usedAmountspecifiedAmount보다 크면 틱이 넘어가지 않으며, nextSqrtPdeltaL(즉, 델타 유동성, 59-62번 줄)로부터 계산됩니다. deltaLestimateIncrementalLiquidity 함수를 사용하여 결정되고, 최종 가격 nextSqrtPcalcFinalPrice 함수로 계산됩니다(70-79번 줄). 필요한 입력이 더 적으면 nextSqrtP는 다음 틱의 가격으로 설정되지만, 이 경우는 공격에서 사용되지 않습니다.

위에서 설명한 단계들로 보면, 틱이 넘어가지 않는 경우 computeSwapStep이 반환하는 nextSqrtP는 다음 틱의 sqrtP보다 크지 않아야 합니다. 그러나 유동성(기본 유동성과 델타 유동성)에 대한 가격 의존성과 정밀도 손실로 인해, 공격자는 틱이 넘어가지 않는 상태에서 nextSqrtP를 더 크게 조작할 수 있습니다.

취약점 분석

근본 원인은 SwapMath 컨트랙트(computeSwapStep 함수에 의해 호출됨)의 델타 유동성 계산(즉, estimateIncrementalLiquidity 함수) 내에서 잘못된 반올림 방향으로 인한 결함 있는 틱 계산에 있습니다. 이는 차례로 이후의 틱 계산에 부적절하게 영향을 미칩니다.

흥미롭게도, 188번 줄의 주석(파란색 사각형으로 강조)을 살펴보면, deltaLnextSqrtP를 내림하기 위해 올림 처리되어야 함을 알 수 있습니다. 그러나 189번 줄에서 mulDivFloor 함수를 사용함으로써 deltaL이 잘못되게 내림 처리됩니다. 결과적으로 nextSqrtP는 부정확하게 올림 처리됩니다.

공격 분석

공격자들은 여러 공격 트랜잭션을 시작했으며, 각 트랜잭션은 여러 풀을 드레인했습니다. 간단하게 설명하기 위해 다음 논의는 공격 트랜잭션 내의 첫 번째 공격을 기반으로 합니다.

핵심 공격 로직은 다음 여섯 단계로 구성됩니다:

  1. AAVE에서 플래시 론을 통해 2,000 WETH를 차용합니다.

  2. 피해자 풀 0xfd7b에서 6.850 WETH를 6.371 frxETH로 스왑합니다. 이 단계는 현재 틱과 currentSqrtP를 현재 유동성이 없는 위치로 밀어 넣는 데 사용됩니다.

  • currentSqrtP는 공격자가 무작위로 선택한 것처럼 보이며, 스왑은 이 가격에서 정확히 멈춥니다.
  • 이 단계 이후 기본 유동성(baseL)은 0이지만 재투자 유동성(reinvestL)은 0이 아닙니다.
  1. 풀에 유동성을 추가한 다음 유동성의 일부를 제거합니다. 이 단계는 범위와 총 유동성을 원하는 양으로 제어하는 데 사용됩니다.
  • 틱 범위는 currentSqrtP를 기반으로 선택됩니다.
  • 공격에 필요한 유동성은 틱 범위에서 도출될 수 있지만, 해당 계산 로직에는 추가 탐색이 필요합니다.
  1. 풀에서 387.170 WETH를 0.06 frxETH로 스왑합니다. 이 단계는 **nextTick == currentTick**이 되도록 현재 틱을 조작하는 데 사용됩니다. 구체적으로, 4단계의 스왑은 교묘하게 풀을 속여 틱 111,310이 넘어가지 않았다고 믿게 만듭니다. 그러나 실제로 currentSqrtP는 틱 111,310의 sqrtP보다 큽니다.
  1. 풀에서 0.06 frxETH를 396.244 WETH로 스왑합니다. 스왑 방향이 이전 단계와 반대임에 유의하십시오. 이 단계에서는 스왑을 수익성 있게 만들고 결과적으로 풀을 드레인하기 위해 유동성이 이중으로 계산됩니다.
  1. 플래시 론을 상환하고 6.364 WETH와 1.117 frxETH를 수확합니다.

심층적인 검토를 위해서는 계산 및 그림을 포함한 더 많은 세부 정보가 담긴 저희의 종합 분석을 참고하십시오.

요약

이 사건의 근본적인 문제는 KyberSwap의 재투자 과정에서 발생한 잘못된 반올림에서 비롯되었으며, 이로 인해 부정확한 틱 계산과 궁극적으로 유동성의 이중 계산이 발생했습니다. 이 사건은 DeFi 프로토콜 내 정밀도 손실 문제의 복잡하고 파악하기 어려운 특성을 부각시키며, 전체 커뮤니티에 심각한 도전을 제기합니다.

2023년의 이 공격은 매우 복잡하고 섬세한 계산이 특징이며, 커뮤니티를 크게 시험한 많은 정밀도 관련 보안 사고의 대표적인 사례로 꼽힙니다. 또한, 당국과의 광범위한 협상 끝에 공격자는 도발적인 어조의 메시지를 공개하여 프로토콜에 대한 완전한 통제권을 요구했습니다.

이 시리즈의 다른 글 읽기:

Sign up for the latest updates
~$160만 손실: Moke 토큰, LpdFi 익스플로잇 | BlockSec 위클리
Security Insights

~$160만 손실: Moke 토큰, LpdFi 익스플로잇 | BlockSec 위클리

2026년 8월 3~9일, BNB 체인에서 2건의 보안 사고가 발생해 총 약 160만 달러의 손실이 발생했으며, 모두 가격 조작이 원인이었습니다. LpdFi(약 69.7만 달러)는 PancakeSwap 유동성 풀을 주문 평가와 이자 정산에 동시 사용해 공격자가 원금을 부풀리고 과도한 이자를 탈취했습니다. Moke Token(약 90.6만 달러)은 조작 가능한 현물 가격과 중복 LP 배당 회계를 결합해 MOKE를 부풀려 BNB 배당을 반복 수령했습니다.

COLDCARD 사건: 지갑의 "무작위" 시드가 무작위가 아니었을 때
Security Insights

COLDCARD 사건: 지갑의 "무작위" 시드가 무작위가 아니었을 때

COLDCARD 펌웨어의 빌드 통합 버그로 비트코인 시드 생성이 취약한 소프트웨어 RNG로 라우팅되어 지갑 시드가 오프라인 복구 가능해졌습니다. 시드 자체의 취약점이므로 펌웨어 업데이트로 해결 불가하며, 2026년 8월 7일 기준 확인된 피해는 1,405 BTC(~9,100만 달러), 비공개 추정치는 최대 2,055 BTC입니다.

~$88M 손실: COLDCARD 및 LULA 익스플로잇 | BlockSec 위클리
Security Insights

~$88M 손실: COLDCARD 및 LULA 익스플로잇 | BlockSec 위클리

2026년 7월 27일~8월 2일, 비트코인과 BNB 체인에서 약 8,800만 달러 손실을 유발한 두 건의 보안 사고가 발생했습니다. COLDCARD 사고는 하드웨어 지갑 펌웨어의 엔트로피 오류로, RNG 매크로 활성화 여부 미확인으로 결정론적 폴백이 실행되어 약 1,370 BTC(~8,800만 달러)가 탈취됐습니다. BNB 체인의 LULA 토큰은 비즈니스 로직 취약점으로 `recycle()` 함수가 악용되어 PancakeSwap V2 유동성에서 약 57만 8천 달러가 유출됐습니다.

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit