2024년 5월 28일, 오리온 프로젝트가 악용되어 글을 쓰는 시점에 약 645,000달러의 손실이 발생했습니다. 이 공격이 어떻게 일어났는지 자세히 살펴보겠습니다.
개요
공격자 주소: https://bscscan.com/address/0x51177db1ff3b450007958447946a2eee388288d2
https://bscscan.com/address/0xf7a8c237ac04c1c3361851ea78e8f50b04c76152
공격 계약: https://bscscan.com/address/0xf8bfac82bdd7ac82d3aeec98b9e1e73579509db6
피해자 계약:
https://bscscan.com/address/0xc662cea3d8d6660ca97fb9ff98122da69a199cd8
공격 거래: https://bscscan.com/tx/0x660837a1640dd9cc0561ab7ff6c85325edebfa17d8b11a3bb94457ba6dcae18c
거래 준비:
자산을 예치하다
잠금 예치(stake)
원자를 교환하다
릴리스 예치(stake) 요청
원자를 교환하다
자산을 예치하다
잠금 예치(stake)
원자를 교환하세요
요청 해제 예치(stake)
원자를 교환하세요
ExchangeWithGenericSwap
토큰 교환 및 스테이킹 플랫폼을 구현합니다. 사용자는 lockStake
또는 doRedeemAtomic
함수를 호출하여 예치(stake) 또는 이체에 잔액을 입금할 수 있습니다. 구체적으로, 이체 함수를 사용하면 부채가 총 잔액을 초과하지 않는 한 스테이킹되는 토큰의 양에 영향을 미치지 않고 부채를 연기할 수 있습니다.
익스플로잇 분석
공격은 간단한 패턴을 따르는 것으로 보입니다. 공격자는 일련의 준비 거래를 통해 주소가 0xf7a8c237ac04c1c3361851ea78e8f50b04c76152
인 계정의 부채 배열을 세심하게 조작했습니다.
먼저 그는 계좌에 10,000,000 ORN을 입금하고 lockStake
함수를 호출하여 토큰을 잠그고 예치(stake) , 이로 인해 assetBalances 값이 0으로 줄었습니다.
그 후, 그는 redeemAtomic
함수를 사용하여 같은 양의 ORN을 B
라는 다른 통제 계정으로 이체했습니다. 이 함수를 사용하면 사용자가 setLiability
메커니즘을 통해 토큰을 이체할 때 부채를 부담할 수 있습니다. 사용자가 자산 잔액보다 큰 양의 토큰을 이체하면 시스템은 해당 사용자에게 필요한 양의 토큰을 입금할 수 있는 충분한 잔액과 권한이 있는지 확인합니다. 잔액이 0보다 작으면 시스템은 해당 사용자에 대한 부채를 설정합니다. 이 메커니즘은 담보 자산과 부채 자산을 포함한 총 잔액을 계산하는 시스템의 확인에 의해 보장됩니다.
실제 사례로 돌아가면, 공격자는 잔액이 0인 0xf7a8c237ac04c1c3361851ea78e8f50b04c76152
에서 이체했습니다. 결과적으로 시스템은 이 주소에 10,000,000의 부채를 설정했고, 이로 인해 해당 계정의 잔액은 -10,000,000이 되었습니다.
다음 단계에서 공격자는 requestReleaseStake
호출하여 스테이킹된 모든 토큰을 인출하고 해당 토큰을 0xf7a8c237ac04c1c3361851ea78e8f50b04c76152
의 잔액에 추가하여 이 계정의 자산 잔액을 0으로 되돌렸습니다. 그런 다음 그는 redeemAtomic
한 번 더 호출하여 추가로 10,000,000 ORN을 B
주소로 전송했습니다. 이 작업은 0xf7a8c237ac04c1c3361851ea78e8f50b04c76152
의 부채 배열을 조작하여 동일한 값을 갖는 두 요소 ["ORN", timestamp, 10,000,000]
를 포함했습니다.
공격자는 추가로 20,000,000 ORN을 입금하고 모든 단계를 한 번 더 반복했습니다. 이 시점에서 0xf7a8c237ac04c1c3361851ea78e8f50b04c76152
의 부채 배열에는 ["ORN", timestamp, 10,000,000]
의 세 가지 요소가 포함되었지만 이 계정의 자산 잔액은 -10,000,000에 불과했습니다.
조작된 부채 배열을 사용하여 공격자는 PancakeSwap
의 플래시론 사용하여 계정 B
에서 0xf7a8c237ac04c1c3361851ea78e8f50b04c76152
로 많은 금액을 다시 이체했습니다. 이로 인해 잔액이 -10,000,000에서 약 196만으로 변경되었습니다. 이전 정보와 결합하여 악용자는 시스템에서 마땅히 받아야 할 것보다 더 많은 자산을 빌릴 수 있었습니다. 그런 다음 공격자는 redeemAtomic
함수를 다시 사용하여 토큰을 공격 계약으로 이체했습니다.
위에서 언급했듯이, 사용자의 총 잔액(자산 잔액과 스테이킹 잔액 포함)에서 부채 잔액을 뺀 금액이 0보다 커야 한다는 것을 보장하는 확인 메커니즘이 있습니다.
시스템은 먼저 calcAssets
함수를 통해 사용자의 자산 잔액을 계산합니다. 그런 다음 calcLiabilities
호출하여 사용자의 모든 부채를 합산하고 해당 값을 빼서 최종 상태를 계산합니다. 토큰을 전송하려면 POSITIVE
상태만 허용됩니다.
사용자의 자산 잔액이 약 196만이고 부채 배열이 여전히 존재하기 때문에 음수여야 할 liabilityValue
값이 양수가 되었습니다. 따라서 공격자는 다른 토큰을 빌릴 때 헬스 체크 메커니즘을 우회할 수 있습니다. 또한 공격자는 부채 배열을 조작하여 더 큰 가치의 토큰을 빌릴 수 있습니다.
그는 이 공격 패턴을 사용해 약 645,000달러 상당의 ORN, 바이낸스 코인(BNB), BUSD-T 토큰 등을 이체하고 인출했습니다.
공격 후, 공격자는 PancakeSwap
플래시론 에서 빌린 돈을 돌려보냈습니다.
취약점의 근본 원인은 피해자가 부채를 올바르게 관리하지 않았기 때문입니다. 부채는 사용자의 자산 잔액이 변경될 때 업데이트되어야 합니다. 그러나 requestReleaseStake
에서 자산을 릴리스할 때 사용자의 부채를 업데이트하지 못하는 것이 이 악용에 기여하는 주요 요인입니다.
또한, 시스템은 부채를 업데이트하거나 제거할 때 사용자가 동일한 토큰에 대해 다른 부채를 가지고 있는지 확인해야 합니다.
결론적으로, 잔액 업데이트 프로세스 동안 부채에 대한 효과적인 통제가 부족하면 상당한 재정적 불일치와 사용자 불만으로 이어질 수 있습니다. 실시간으로 부채 수준을 모니터링하고 관리하여 사용자가 지속 가능한 잔액을 유지하고 플랫폼 내에서 재정적 불안정성을 방지하는 강력한 통제를 구현하는 것이 필수적입니다. 이러한 우려 사항을 해결함으로써 모든 사용자에게 보다 안전하고 신뢰할 수 있는 환경을 조성할 수 있습니다.