오라클 조작: 2026년 Silo Finance에서 발생한 392,000달러 손실 사례
요약
Silo Finance에서 발생한 오라클 조작 공격 사례를 통해 DeFi 프로토콜의 취약점을 분석합니다. 스팟 가격(spot price)을 사용하는 방식의 위험성과 TWAP(시간 가중 평균 가격)의 한계를 기술적으로 설명합니다.
핵심 포인트
- 스팟 DEX 풀의 예비금을 직접 읽는 방식은 플래시 론 공격에 매우 취약함
- 공격자는 단일 트랜잭션 내에서 가격을 조작해 부풀려진 담보 가치로 대출 가능
- TWAP은 단일 트랜잭션 공격을 방어하지만, 짧은 윈도우 설정 시 여전히 조작 위험 존재
- 유동성이 낮은 풀일수록 오라클 조작 비용이 낮아져 공격에 노출되기 쉬움
2026년의 9자리 수(억 단위) 해킹 사건들이 헤드라인을 장식했지만, 더 작은 규모의 해킹들이 더 교훈적입니다. 왜냐하면 그것들이 바로 당신과 내가 실제로 작성할 수도 있는 버그들이기 때문입니다. 2026년 4월 3일, 대출 프로토콜(lending protocol)인 Silo Finance는 잘못 설정된 오라클(oracle)로 인해 약 392,000달러를 잃었습니다. 이례적인 공격은 아니었습니다. 거짓을 말하는 가격 피드(price feed)와 그것을 믿어버린 컨트랙트(contract)가 있었을 뿐입니다. 오라클 조작(oracle manipulation)이 어떻게 작동하며, 왜 프로토콜의 자금을 계속해서 빼가고 있는지에 대해 알아보겠습니다.
모든 오라클 버그의 형태
대출 프로토콜은 당신의 담보가 얼마의 가치가 있는지 알아야 합니다. 이를 위해 오라클에 문의합니다. 만약 오라클이 속을 수 있다면, 프로토콜도 속을 수 있습니다. 담보의 가치가 갑자기 실제보다 훨씬 더 높은 것으로 나타날 때 이를 담보로 대출을 받거나, 담보 가치가 갑자기 훨씬 낮아졌을 때 청산(liquidated)을 당할 수 있습니다.
모든 오라클 익스플로잇(exploit)은 동일한 질문으로 귀결됩니다: 공격자가 컨트랙트가 신뢰하는 숫자를, 조작 비용이 수익보다 적을 정도로 충분히 저렴하게 움직일 수 있는가?
전형적인 실수: 스팟 DEX 풀(spot DEX pool)을 통한 가격 책정
가장 흔한 형태는 AMM 풀의 예비금(reserves)에서 직접 가격을 읽어오는 방식입니다:
function getPrice() public view returns (uint256) {
(uint112 reserve0, uint112 reserve1, ) = pair.getReserves();
return (reserve1 * 1e18) / reserve0; // 스팟 가격(spot price), 현재 시점
...
이것은 이 순간의 스팟 가격(spot price)입니다. 플래시 론(flash loan)을 사용하는 공격자는 풀에 엄청난 양을 스왑(swap)하여 예비금을 움직이고, 이 가격을 읽는 함수를 호출한 뒤, 다시 원래대로 되돌리는 과정을 단 하나의 트랜잭션(transaction) 내에서 모두 수행할 수 있습니다. 해당 트랜잭션이 지속되는 동안 가격은 공격자가 밀어붙인 값이 됩니다. 그들은 부풀려진 가치를 담보로 대출을 받고 떠납니다.
비용은 스왑 수수료와 플래시 론 수수료입니다. 수익은 초과 대출된 금액입니다. 풀의 유동성이 낮을 때(shallow), 이 비용은 매우 적습니다.
TWAP이 도움이 되는 이유와 그렇지 않은 이유
표준적인 방어책은 시간 가중 평균 가격(TWAP, time-weighted average price)입니다. 즉각적인 예비금 대신, 일정 시간 범위 동안 평균화된 가격을 읽어오는 방식입니다:
// Uniswap V3 스타일: `secondsAgo` 동안의 평균 틱 (average tick)
function getTwap(uint32 secondsAgo) public view returns (uint256) {
uint32[] memory secondsAgos = new uint32[](2);
...
플래시 론 (Flash loan)은 단일 트랜잭션 내에서 수 분 단위의 평균값을 움직일 수 없습니다. 왜냐하면 평균값에는 공격자가 제어할 수 없는 블록들이 포함되어 있기 때문입니다. 이는 단일 트랜잭션 기반의 플래시 론 공격 버전을 무력화합니다.
하지만 TWAP (시간 가중 평균 가격)이 만능은 아닙니다. 여전히 두 가지 문제가 발생할 수 있습니다:
- 짧은 윈도우 (Short window). 유동성이 낮은 풀 (shallow pool)에서의 30초 TWAP은, 공격자가 몇 개의 블록에 걸쳐 포지션을 유지할 의사가 있다면 여전히 조작될 수 있습니다. 윈도우가 길수록 안전하지만 실제 가격보다 뒤처지게(lag) 되며, 이는 가격이 급변할 때 그 자체로 또 다른 리스크를 생성합니다.
- 얇은 풀 (Thin pool). TWAP은 조작을 평균화할 뿐, 조작 자체를 방지하지는 못합니다. 유동성이 낮은 페어 (low-liquidity pair)에서는 윈도우 전체에 걸친 지속적인 조작이 비용 측면에서 감당 가능한 수준일 수 있습니다.
"설정 오류 (misconfigured)"가 보통 의미하는 것
사후 분석 (post-mortem) 보고서에서 "설정 오류가 있는 오라클 (misconfigured oracle)"이라고 언급할 때, 이는 팀이 오라클이 무엇인지 몰랐다는 뜻인 경우는 드뭅니다. 대개 다음 중 하나에 해당합니다:
- 주요 자산에는 견고한 오라클 (Chainlink 등)을 사용했지만, 신규 자산이나 유동성이 낮은 자산에는 스팟 DEX 가격 (spot DEX price)을 사용하여 공격자가 취약한 쪽을 공격한 경우.
- 풀의 유동성에 비해 TWAP 윈도우를 너무 짧게 설정한 경우.
- 오라클의 신선도 (freshness)를 검증하지 않아, 일시 중단되었거나 지연된 피드 (feed)로부터 온 오래된 가격 (stale price)을 현재 가격으로 취급한 경우.
사람들이 가장 자주 잊어버리는 것이 바로 신선도 검증 (freshness check)입니다:
(, int256 price, , uint256 updatedAt, ) = priceFeed.latestRoundData();
require(price > 0, "bad price");
require(block.timestamp - updatedAt < MAX_STALENESS, "stale price"); // 흔히 누락되는 부분
내가 이를 감사 (audit)하는 방법
자산의 가격을 책정하는 프로토콜을 검토할 때, 나는 모든 가격 읽기 (price read) 과정을 소스까지 추적하며 세 가지 질문을 던집니다:
- 단일 트랜잭션으로 이 수치를 움직일 수 있는가? 만약 스팟 예비금 (spot reserves)을 읽어온다면, 그렇습니다. 이를 플래그(flag) 처리하십시오.
- 평균화 윈도우 (averaging window)가 풀의 깊이 (depth)에 적절한가? 5만 달러 규모의 풀에서 30초 TWAP (시간 가중 평균 가격)을 사용하는 것은 안전하지 않습니다.
- 데이터의 신선도 (staleness)를 확인하는가? 만약 코드가
updatedAt을 확인하지 않고 피드 (feed)를 신뢰한다면, 중단된 피드는 공격 통로가 됩니다.
이 지점은 AI 보조 검토 (AI-assisted review)가 진정으로 도움이 되는 영역입니다. 왜냐하면 취약점이 여러 함수에 걸쳐 있으며, 단순한 구문 (syntax)이 아닌 경제적 논리에 대한 추론을 요구하기 때문입니다. 제가 사용하는 프롬프트는 모델에게 "각 가격 소스를 추적하고, 단일 트랜잭션 또는 단일 블록 내에서 조작 가능한지 판단하며, 프로토콜의 차입 한도 (borrow limits) 대비 조작 비용을 추정하라"고 요청합니다. 이러한 경제적 프레임워크 (economic framing)가 "getReserves 호출을 발견했다"와 "이 5만 달러 규모의 풀이 100만 달러의 차입 한도를 제어하고 있으며, 이는 악용 가능하다"를 구분 짓는 핵심입니다.
Silo의 손실은 2026년 기준으로 규모가 작았지만, 그 교훈은 확장 가능합니다. 프로토콜은 자신이 신뢰하는 가장 저렴한 수치만큼만 정직합니다. 가장 저렴한 수치를 찾는다면, 당신은 버그를 찾은 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기