검열을 이용해 이더리움에 DoS 공격하기

Luke Park
취미로 논문 읽는 그룹

--

Yaish, Aviv, et al. “Speculative denial-of-service attacks in ethereum.” Cryptology ePrint Archive (2023).

트랜잭션이 블록에 포함될 수 있을지 확인하려면 현재 블록체인 상태에 기반해 트랜잭션을 실행해봐야 한다.

통상은 이는 트랜잭션 수수료의 형태로써 실행에 대한 보상으로 수행자에게 제공되는데, 본 논문에서는 공격자가 블록체인 참여자의 작업과 그 보상의 연결성을 끊어내는 류의 공격들을 제시한다. 보다 구체적으로, 다음의 세 가지 공격을 소개하고 있다.

  1. ConditionalExhaust: 수행자에 대한 조건부 리소스 고갈 공격 (REA, Resource Exhaustion Attack)
  2. MemPurge: 수행자의 멤풀(mempool)에서 트랜잭션을 퇴거시켜버리는 방법
  3. GhostTX: 이더리움의 현 PBS 시스템 상 평점 시스템을 공격하는 방법

이더리움 테스트넷 상에서 공격자들은 조건부 REA와 MemPurge를 결합해 피해자의 연산 자원에 부담을 주고, 블록에 트랜잭션을 포함할 수 없을 정도로 멤풀을 방해할 수 있었다. 이는 피해자가 빈 블록을 만들게하고 결국 시스템의 라이브니스가 저해된다.

공격자가 검증자로서 역할하고 있으면 더욱 쉽게 공격할 수 있고, 또한 검열이 네트워크에 더 많이 반영되고 있으면 비용 또한 감소하게 된다. 블록체인에 검열이 웬말인가 싶지만 실제로 이미 트랜잭션 검열은 절찬리에 일어나고 있고, 공격자들은 이 검열 시스템을 공격에 이용한다.

이더리움은 매우 개방적인 컴퓨터로써 많은 표현을 가능하게 하지만, 이러한 유연하고 개방적인 특성은 공격자가 대량의 컴퓨팅 리소스를 소비하는 컨트랙트를 배포할 수 있고, 블록체인을 검증하는 모든 노드가 고비용의 계산으로 과부하가 걸릴 수 있다는 위험성을 내포하고 있다.

물론 기본적으로 트랜잭션 수수료를 통해 많은 비용을 지출하게 만들고 가스 제한을 통해 과부하를 방지하고 있지만, 만일 이를 우회하고 공격을 가할 수 있으면 어떨까?

논문의 저자들은 가장 널리 사용되는 이더리움 클라이언트인 Go-Ethereum (geth) 기반 클라이언트에 대한 몇 가지 효과적인 공격을 제시한다. 특히 이러한 부류의 기존 연구들이 이미 geth에 패치/핫픽스되어 방어되고 있지만, 기존 방어 체계를 우회하여 피해 노드의 성능을 심각하게 저하시킬 수 있었다. 가령 로컬 테스트넷에서 공격을 평가한 결과, 공격자는 단지 140개의 트랜잭션을 통해 피해자가 어떤 트랜잭션도 채굴하지 못하게 할 수 있음을 보였다.

이러한 공격이 가능한 몇 가지 인사이트는 다음과 같다:

  • 현재 이더리움은 블록 생성 과정을 여러 역할 — 검색자, 빌더, 릴레이, 제안자 — 로 분할하여 일부 노드가 휴리스틱 또는 추측적으로 트랜잭션을 실행하게 되어 있음
  • 컨트랙트의 동작은 기반되는 상태, 특히 다른 컨트랙트와 계정의 상태에 따라 크게 달라질 수 있음
  • 일부 노드는 트랜잭션에 대한 검열 정책을 수행하고 있음

이들을 통해 투기적으로 실행될 때 리소스 집약적이지만 블록체인에서 제외되는 트랜잭션을 생성할 수 있고, 실제로 트랜잭션이 실행되지 않더라도 제한된 메모리 풀 (멤풀) 공간을 차지할 수 있다.

ConditionalExhaust

ConditionalExhaust 공격은 트랜잭션을 실행하는 검증자의 신원에 따라 계산 집약적인 코드를 조건부로 실행하는 트랜잭션을 생성, 검증자가 트랜잭션을 블록에 포함할 수 없는 경우에만 이러한 고비용 계산을 수행하도록 한다.

예를 들어 트랜잭션이 검열되어야할 주소와의 상호작용으로 끝나는 경우, 검증자가 법률을 준수하기 위해 검열을 수행하는 경우, 이러한 일이 발생할 수 있다.

위 코드에서 block.coinbase를 통해 검증인의 신원을 파악하고, 만일 DoS 공격을 가할 수 있는 주소로 식별되면, 계산 집약적인 연산을 반복적으로 수행해 리소스에 부하를 가한다. 본 코드에서는 extcodehash를 통해 (컨트랙트 코드의 keccak256 해시값을 구하는 연산) 랜덤 위치(블록 해시와 남은 gas의 XOR 연산)의 스토리지 access를 다수 발생시켜 부하를 가한다.

이는 원래 막대한 양의 트랜잭션 수수료를 제출해야 하는 상황이다. 정상 상황이라면 수행을 실패처리해도 그간 수행된 비용만큼은 지불해야 하는데, 여기서 공격자는 마지막 부분에서 CensoredAddress, 즉 검열 대상이 되는 주소를 호출(상호작용)하여 이 트랜잭션 수행 시도 자체가 검열되게 만든다. 즉, “아무일도 없었다”가 되어버린다.

검열처리되어 노드에서 이 트랜잭션 자체를 무시하게되니, 트랜잭션 수수료가 나갈 일도 없다. 그러나 이미 노드는 이 계산 집약적인 연산을 수행해 피해를 봤다.

공격의 가성비를 한번 따져보면, 논문에서 실험한 컴퓨팅 리소스 기준으로 공격 트랜잭션을 만들고 서명하는 것에는 5.5 * 10^-5 초가 소요된다. 트랜잭션 생성에는 state가 관여되지 않기 때문에 매우 빠르다. 반면 검증에는 0.1 +/- 0.011 초가 소요되었다. 참고로 일반적인 ETH 전송 트랜잭션은 0.001초만에 검증된다. 즉, 생성보다 검증에 1972배 더 오랜 시간이 요구되는 가성비 있는 공격이다.

이러한 공격 컨트랙트를 배포하는 것에는 (22년 11월부터 23년 5월을 기준으로) $27.13가 요구되며 공격을 가하는 것에는 하나당 $5.3가 소요되었다. 이 비용은 coinbase 주소 조건에 걸려 실제 공격이 수행되지 않은 상황이고, 검열되어 실제로 블록에 포함되지 않았으면 비용이 나가지도 않는다.

MemPurge

노드는 수령한 트랜잭션을 멤풀에 추가하기 전 수행하지 않고 휴리스틱하게 검증한다. MemPurge 공격은 이를 이용, 대충 보면 유효해보이지만 실제로 수행하면 무효가되는 트랜잭션 체인을 생성해 피해자의 멤풀에서 정직한 트랜잭션을 받아들일 수 없게 만든다.

트랜잭션 체인이란 연속된 (트랜잭션 요청자의 단조증가하는 카운터인) 논스로 순서대로 엮인 트랜잭션들을 칭한다.

만일 어느 트랜잭션에서 가스 당 지불 비용을 높게 설정하면, 노드는 제한된 멤풀 공간에서 최대한의 효율을 위해 이들을 멤풀에 담고 다른 트랜잭션들은 배제하게 된다. 높은 수익을 기대하며 실제로 수행될때까지를 기대하며 멤풀에 보관하고 있는데 —

그러나 이러한 대기 중인 트랜잭션들은 공격자에 의해 한 방에 무효화될 수 있다. 그럼 들고 있던 트랜잭션 체인, 즉 트랜잭션 뭉치들은 한 순간에 쓰레기가 되어버리고 공간만 낭비한 셈이 되고, 이들을 보관하기 위해 내쳤던 (저렴하다고 생각했던) 정직한 트랜잭션들은 날려버린 기회비용이 되어버린다.

보다 구체적으로, 우선 멤풀에 들어가기 위해 트랜잭션들은 그럴싸해 보여야 한다. 이미 이러한 류의 공격이 잘 알려져 있고 실제로 이를 완화하기 위한 패치가 진행되었었기 때문에(https://github.com/ethereum/go-ethereum/releases/tag/v1.11.4) 검사를 우회해야 한다. 대표적인 검사가 바로 송신자의 자금 이상을 보낼 수 없다는 것이다(https://github.com/ethereum/go-ethereum/pull/26648).

또한 pending queue에 있는 트랜잭션들이 future queue보다 더 우선시되고, 멤풀이 최대 용량에 도달하면 “대기 중인 트랜잭션이 적은 사용자가 보낸 트랜잭션이 더 우선시”된다.

https://github.com/ethereum/go-ethereum/pull/26648

이러한 휴리스틱은 확실히 look okay해 보이지만 문제가 하나 있다. Worstcase를 고려해 충분한 돈이 있는지를 검증하는 과정이 멤풀에 들어오는 극초반에만 이루어진다는 것이다.

공격자는 트랜잭션 체인을 쭉 형성하고, 가장 첫 트랜잭션을 제외한 나머지 체인을 노드에게 보내 멤풀에 들어가게 만든다. 이후 첫 트랜잭션을 뒤늦게 공시하는데, 이 트랜잭션이 모든 자금을 다른 주소로 빼는 트랜잭션이다(컨트랙트를 쓸 필요도 없다).

자명하게 다른 트랜잭션들은 이제 invalid하다. 수행할 돈이 없기 때문이다. 그러나 이들은 이미 멤풀에 들어와서 자리를 잡고있고 더 이상의 추가 검증 휴리스틱을 받을 일이 없기 때문에, 정확하게는 overspend funds인지 검증을 받을 일이 없기 때문에 편안하게 멤풀 자리를 차지하고 있다.

ConditionalExhaust + MemPurge

MemPurge 공격은 ConditionalExhaust 공격과 결합될 수도 있다. 단순히 MemPurge 트랜잭션들의 받는사람 필드를 ConditionalExhaust 컨트랙트로 삼으면 된다.

이 컨트랙트에서는 블록번호를 기준으로 하는 분기가 하나 더 생겼는데, 이를 통해 DoS를 끝내고 MemPurge의 첫 번째 트랜잭션인 모든 자금을 다음 주소로 전송하는 행위를 수행할 수 있다.

후행 트랜잭션들은 어차피 자금이 없어서 수행되지 않을 것이며, 비용도 발생하지 않는다. 반면, 첫 번째 트랜잭션은 검열을 하지 않는 행위자에 의해서만 블록에 포함되기 때문에, 네트워크에 검열이 만연한 상황에서는 후행 트랜잭션들이 더 오래 멤풀에 남아있을 수 있다. MemPurge 공격이 단순 자금 전송으로도 행할 수 있지만 ConditionalExhaust와 결합되면 더 효과적인 이유이다.

GhostTX

이 공격은 이더리움의 PBS (Proposer-builder separation) 구조를 이용한다.

PBS에서 검색자(searcher)는 MEV 기회를 파악하고 이를 활용하는 트랜잭션 번들을 모으는 데 특화되어 있다. 번들은 빌더(builder)에게 전송되며, 빌더는 이를 P2P 트랜잭션과 함께 사용하여 수익성 있는 블록을 구성한다. 릴레이(relay)는 MEV-Boost 프로그램을 사용해 가장 수익성이 높은 블록을 검증하고 다음 블록 제안자로 지정된 검증자와 공유한다. 제안자(proposer)는 릴레이 블록을 사용하거나 P2P 레이어에서 전송된 트랜잭션 또는 직접 블록을 구성할 수 있다.

이 중에서도 특히 빌더와 제안자 간의 분업에 초점을 두기 때문에 이 구조는 PBS로 널리 불리고 있다.

플래시봇의 구현체에서는 평판을 사용하여 검색자로부터의 에코시스템 접근 우선순위를 정한다. 검색자의 평판은 과거 실적으로부터 구해지며, 이는 제안자를 위해 생성한 가스 단위당 수익에 따라 측정된다. 평판은 주소와 연결되어 있으므로, 평판이 손상된 검색자는 새 주소를 사용해 평판을 처음부터 다시 구축해야 한다.

이는 시간이 오래 걸리는 과정이며, 그 동안 수익이 감소할 수 있다. 공격으로 인해 고수익 검색자가 강등될 경우 PBS 생태계의 효율적인 기능에 해를 끼치고 관련 빌더 및 제안자의 수익이 감소할 수 있다.

비록 아직 이러한 평판을 어떻게 활용하고 있는지는 증거가 없지만, 빌더와 릴레이를 운영하고 있는 Blocknative에 따르면, 대부분의 빌더가 평판 메커니즘을 가지고 있다고 한다. 실제로 중요할 것이, 평판을 고려하지 않은 빌더는 적대적인 검색자의 여러 DoS 공격에 노출될 수 있기 때문이다.

본 논문에서 제안하는 GhostTX 공격을 통해 공격자는 플래시봇의 PBS 구현에서 검색자의 평판을 낮출 수 있다. 공격자는 검색자와 빌더에게는 수익성이 높아 보이지만 블록에 포함될 수 없는 트랜잭션을 만들어 평판 메커니즘을 공격한다. 이는 빌더와 릴레이의 검열에 불일치가 있기 때문이다.

보다 구체적으로, 빌더는 잔액을 기준으로 검열하기 때문에 대상 주소로 0원을 보내는 것을 허가한다. 그러니 릴레이는 해당 블록에 관련된 모든 주소 중에 블랙리스트에 포함되는 주소가 없는지 검열한다. 따라서 블랙리스트 주소로 0원을 전송을 하는 트랜잭션이 빌더에게는 유효하지만 릴레이에게는 비유효하다.

이제 그러한 주소로 0원을 이체하는 트랜잭션을 검색자가 포함해 제출하면, 빌더는 받아들이고 이 트랜잭션이 담긴 블록이 만들어진다. 그리고 결국 릴레이로부터 거절당하기 때문에, 이 트랜잭션을 수수료 등으로부터 수익성 높은 트랜잭션이라 생각하고 포함시킨 검색자는 평판이 깎이게 된다.

본 논문은 이더리움의 프로토콜 레벨에서 한 발자국 떨어져, 현실적인 문제들로 인해 발생한 “검열”이라는 요소가 어떻게 보안성을 해칠 수 있는지를 역설하고 있다. 멤풀을 공격하는 연구는 다수 있어왔지만 “검열”이라는 weak point를 이용하면 더 강력한 공격이 되기도 한다.

나름 더 안전한 사용을 위한답시고 (애초에 본인은 그렇게 생각하지도 않지만) 도입한 검열이 성능과 보안성을 되려 해치는 모습이, 탈중앙성을 다루는 것이 얼마나 민감하고 예민한 일인지를 다시금 일깨운다.

논문에서는 이러한 공격을 제안함과 더불어 여러 완화책들을 제시하고 있다. 멤풀 휴리스틱을 수정하거나 OPCODE 당 수행 비용을 바꿀 수도 있지만, 개인적으로는 라디우스(Radius, https://www.theradius.xyz/)에서 연구 및 개발하고 있는 Encrypted Mempool 방법이 검열을 원천적으로 우회하며 이러한 공격의 완화책으로도 응용될 수 있을 것 같다. 자세한 내용은 라디우스의 Docs를 참고하자.

--

--