Analysis of 8/5, 8/12 Front-running attack (프론트 러닝 공격 보고서)

EJ
Terra
Published in
10 min readSep 2, 2019

For our global audience: On August 5th, 12th, we witnessed an attacker who executed a front-running attack between our on-chain forex market and the primary (global) forex market. The front-running was possible due to the delay of our oracle module and the provision of zero-fee on-chain forex swaps. In this post, first we discuss the weakness of Terra’s oracle module that made the attack possible, and how the attack proceeded, step by step. Second, we analyze the attacker’s MsgSwapTx’s and calculate the total profit of the front-running. Third, we calculate historical VaR deviations of USD/KRW 1min candles, using a dataset spanning all candles between 2014–2019. Last, we explain ongoing work to introduce fees in on-chain swaps to prevent front-running attacks in the future. In short, charging a fee that exceeds potential front-running profits in the vast majority of cases makes the attack unprofitable.

지난 8월 5일과 12일, 테라 오라클의 Prevote, Vote 구조로 인한 오라클 가격과 현실 가격간의 딜레이를 이용한 Front-running 공격이 발생했습니다. 테라와 루나 사이의 스왑을 이용했던 7월의 공격들과 달리, 이번 공격은 테라 통화들간 스왑을 이용한 공격이었습니다. 지난 7월의 차익거래 공격이 궁금하신 분들은 여기를 참고하시면 됩니다. 이번 공격에서, 공격자는 테라와 루나 사이의 스왑과 달리, 테라 통화들간 스왑에는 수수료가 전혀 없다는 것을 이용하여 Front-running을 진행했습니다.

이번 리포트는 다음과 같은 순서로 진행됩니다.

  1. 오라클 딜레이와 이를 이용한 공격자의 공격 과정 설명
  2. MsgSwap 분석을 통한 공격자의 부당 수익 계산
  3. 오라클 딜레이 VaR 계산 (2014년~2019년 USD/KRW 1분봉 데이터 분석
  4. 연구중인 개선안 소개

오라클 딜레이와 이를 이용한 공격자의 공격 과정 설명

온체인 마켓에서의 스왑을 위해서는, 체인 외부의 정보인 루나의 시장 가격과, 테라 통화쌍끼리의 교환 비율을 체인 위로 불러올 필요가 있습니다. 이때 사용되는 것이 오라클 모듈입니다. 오라클 모듈은 현재 루나의 시장 가격과, 각각의 테라 통화쌍 간의 교환 비율을 투표로 결정합니다. 투표는 검증인들에 의해 이루어지며, 이때 총 보팅파워(보증금으로 묶인 루나의 총량)의 반 이상이 투표되어야지만 해당 투표가 집계될 수 있습니다. 보팅파워의 절반 이상이 투표가 완료되는 경우, 해당 투표의 중위값이 교환 비율로 결정됩니다. Col-2에서는, 해당 투표의 중위값 +- 2%안에 투표한 검증인들에 대한 보상으로 온체인 스왑에서 나오는 스프레드 수수료를 지급하고 있습니다.

테라의 오라클 투표는, 한 시점(Vote_period)의 가격을 총 두번의 투표로 결정합니다. 이때 한 Vote_period는 12블록으로 구성됩니다.

N 시점에서의 가격을 정하기 위한 첫 번째 투표는 N-2 시점에서 행해지는 MsgPricePrevote 입니다.(이하 prevote) prevote는 테라 통화와 루나의 교환 비율을 SHA256 함수로 해싱한 값을 담고 있습니다. 예를 들어, KRW, USD, SDR에 고정된 테라 통화들과 루나에 대한 온체인 스왑을 지원하기 위해서는, 각각 Luna<>KRW, Luna<>USD, Luna<>SDR에 해당하는 세가지 교환 비율과 salt 값을 해싱한 값이 포함된 prevote가 제출되어야 합니다. 여기서 salt 값이란 일종의 비밀번호라고 생각하시면 됩니다. 만일 교환 비율만 해싱하게 된다면, 현실적으로 가능한 교환 비율이 정해져 있기 때문에, 무작위 대입을 통해 교환 비율을 유추할 수 있게 됩니다. 이를 방지하기 위해, salt라는 일종의 추가적인 비밀번호를 교환 비율과 같이 해싱하여 교환 비율을 유추할 수 없게 합니다.

두 번째 투표는 MsgPriceVote입니다.(이하 vote) 기간 N-1에 제출된 vote는 N-2에서 제출된 prevote에서 해시값을 만드는데 사용된 salt값와 실제 투표된 값을 공개합니다. N에서 제출된 salt값은 검증인이 N-1에서 제출했던 prevote를 검증하기 위해 사용됩니다. N-1에 집계된 투표의 결과를 바탕으로 테라의 오라클 모듈은 N 시점의 오라클 가격을 결정합니다. 테라간 온체인 스왑의 경우에는 위에서 결정된 교환 비율들을 통해 온체인 환율을 계산하여 이루어집니다.

prevote에서 해시값을 제출하는 이유는, 만일 오라클 투표를 해시값 제출 없이 공개 투표로 진행하게 된다면, 검증인들은 해당 투표 기간의 마지막까지 기다렸다가 제출된 투표들의 중위값을 계산해서 투표를 제출하는 것을 통해 안정적으로 보상을 얻을 수 있게 됩니다. 이를 방지하기 위해 테라의 오라클 모듈은 2단계에 걸친 투표를 통해 오라클 가격을 결정하게 되었습니다. 하지만 이러한 투표 과정은, N-2에서 관측한 값을 N-1에서 공개하고, N에서 사용하기 때문에 실질적으로 현재(N)의 오라클 가격이 과거(N-2)의 시장 가격을 나타내게 되는 단점이 있습니다. 즉, 실제 가격과 오라클 가격 사이의 딜레이가 발생하게 된다는 것입니다. 각 Vote_period는 prevote와 vote 모두 12블록으로 고정되어 있기 때문에, 해당 딜레이는 1~2 Vote_period, 즉 12~24 블록 사이의 값이 될 것입니다.

앞서 언급했듯, 저희는 테라와 루나 사이의 스왑의 경우 이런 공격이 가능하다고 판단하여 스프레드 수수료를 도입했지만, 테라와 테라간의 스왑에는 가스비를 제외한 다른 수수료를 두지 않았습니다. 공격자는 이를 인지하고, 오라클 가격의 딜레이와 테라 통화간 스왑의 Zero fee를 통해서 Front-running을 진행했습니다.

이 허점을 이용한 공격자의 공격 과정은 다음과 같습니다.

  1. 환율이 상승하는 상황에서, Vote_period가 끝나기 직전까지 oracle 환율과 현실 세계의 환율간의 편차(deviation)이 존재한다면 해당 Vote_period 안에서 스왑 진행
  2. 다음 오라클 가격 반영 시, 해당 편차가 반영되는 즉시 반대 방향으로 스왑 진행을 통해서 해당 Front-running 포지션을 청산
  3. 반복

물론 이러한 과정을 통해서 공격자가 취한 편차는 매우 작습니다. 크립토 시장과 비해 외환 시장의 변동성은 매우 미미한 수준이기 때문입니다. 하지만 문제는 테라간 스왑에는 수수료가 없기 때문에 이 미미한 변동성에서 오는 편차를 모두 편취할 수 있었다는 점입니다. 가장 문제되는 부분은 해당 공격 과정에서 공격자가 가지는 리스크가 거의 0에 가깝다는 점입니다.

MsgSwap 분석을 통한 공격자의 부당 수익 계산

실제 공격자의 공격 트랜잭션들을 살펴보겠습니다. 공격자는 8/5일 25개, 8/12일 30개의 MsgSwapTx를 발생시켰습니다. 해당 MsgSwapTx들의 상세한 내용이 궁금하신 분들은 여기를 참고하시면 됩니다.

공격자는 최종 수익을 KRT로 확정지었고, 총 55번의 스왑을 통해서, 약 155만 KRT의 수익을 얻었습니다.

위 MsgSwapTx를 바탕으로 Front-running 거래에서 공격자가 취한 온체인 환율간 편차를 계산한 결과는 여기에서 확인하실 수 있습니다.

공격자는 최종 수익을 대부분 KRT로 확정짓는 모습을 보여주었고, 따라서 하나의 공격은 두번의 MsgSwap으로 이루어져 있습니다. 따라서 위 편차들은 총 2번의 MsgSwapTx가 개입되었다고 할 수 있습니다.

공격자가 취한 편차중 최대 편차는 0.078337%이며, 최소 편차는 0.00029% 입니다. 31개의 편차값의 평균은 0.017998%입니다.

오라클 딜레이에 따른 VaR 계산 (2014년~2019년 USD/KRW 1분봉 데이터 분석)

이번 Front-running 공격은 테라 오라클 모듈의 근본적인 한계점을 이용한 공격입니다. 따라서 추가적인 개선안을 위해, 테라간 스왑이 Front-running의 위험에 어느정도 노출되어 있는지에 대해 분석할 필요가 있습니다. 이를 분석하기 위해서 현재 테라 통화쌍들중 가장 변동성이 큰 USD/KRW 쌍에 대하여, 2014년부터 2019년 까지의 USD/KRW 1분봉 데이터를 수집해 Front-running이 일어날 수 있는 최악의 상황들을 수집하여 Historical VaR을 계산했습니다.

VaR 계산을 위해, 연속되는 2개의 1분봉에서 관측되는 최대 편차를 수집합니다. 연속되는 2개의 1분봉을 사용하는 이유는, 테라의 온체인 환율과 현실 세계의 환율 사이의 딜레이가 1~2 Vote_period, 즉 12~24 블록 사이의 값이기 때문입니다. 현재 col-2의 평균 블록 시간이 6.45초이기 때문에, 이는 약 77초 ~ 155초 사이의 값입니다. 연속되는 2개의 1분봉(120초)에서 관측되는 최대 편차를 VaR 계산에 사용한다면 실제 오라클 가격과 현실 가격간의 딜레이에서 관측될 수 있는 편차값과 매우 흡사할 가능성이 높기 때문에, 연속되는 2개의 1분봉을 사용했습니다. 발생가능한 최악의 상황을 고려하기 위해, 2개의 1분봉에서 관측되는 최대 편차를 사용하였습니다. 편차들은 절댓값의 형태로 분석에 사용되었습니다.

계산된 Historical VaR은 다음과 같습니다.

위 히스토그램에서 x축은 편차의 정도를 나타내며, y축은 이에 해당하는 편차의 갯수들을 나타냅니다. 시각화를 위해 y축에는 로그를 취했습니다.

계산된 Historical VaR에 따르면, 2014~2019 USD/KRW 2분 최대 편차들의 90%가 0.05071% 이하이며, 이를 95%로 확장하면 0.06315%, 99%로 확장하면 0.0977%라는 것을 알 수 있습니다. 물론 계산된 Historical VaR은 최악의 상황(2분 내에서 관측되는 최대 편차)을 가정하였기에, 실제 리스크는 이보다 낮을 가능성이 매우 높습니다. 하지만 극단적인 최악의 상황을 가정하는 것을 통해 Front-running의 가능성을 최대한 배제하는 것이 더 중요하기 때문에 해당 결과값이 충분히 유효하다고 생각합니다.

연구중인 개선안 소개

VaR 분석을 토대로, Front-running을 방지하기 위한 두 가지 개선안을 연구중에 있습니다. 첫 번째 개선안은 Default Tobin tax입니다. 테라간 스왑에 약간의 고정 수수료를 추가하는 것을 통해서 취할 수 있는 편차보다 더 많은 수수료를 지불하게 하면 대부분의 Front-running을 방어할 수 있을 것입니다. 이 고정 수수료값 설정에 VaR 분석이 사용될 예정입니다. 두 번째 개선안은 서킷 브레이커 도입입니다. 실제 현실 세계의 이벤트로 인해서 급격한 환율 변동이 관측되거나, 혹은 기술적인 오류로 인해 예상 밖의 편차가 관측되는 경우, 서킷 브레이커가 발동되어 일정 기간동안 오라클 작동을 멈추는 것을 통해서 블랙 스완 이벤트들을 방지할 수 있을 것이라고 기대하고 있습니다. 서킷 브레이커를 발동하는 조건에도 역시 VaR 분석 결과를 사용할 예정입니다.

감사합니다.

--

--