RPC 서비스의 취약점을 활용한 DApp 장애 유발

Jaeseung Lee
Decipher Media |디사이퍼 미디어
14 min readOct 1, 2021

28th Annual Network and Distributed System Security Symposium에 게재된 “As Strong As Its Weakest Link: How to Break Blockchain DApps at RPC Service” 논문을 기반으로 작성된 글입니다.

Introduction

현재 많은 DApp 서비스는 서비스 운영의 유연성을 위해 사용자들에게 각자 직접 노드를 돌리지 않게 하고 RPC(Remote Procedure Call) 서비스를 통해 메인넷과 연결하게 한다. 즉, 사용자들이 메인넷과 연결되있는 Metamask같은 지갑 서비스를 사용하고 이를 DApp 웹사이트에 연결해 트랜잭션을 보내는 방법이 일반적이다. 다른 방법으로는 서비스 제공자가 직접 노드를 돌리면서 아이디/패스워드를 통해 회원관리를 하고 이에 대해 각각 HD wallet 기반 키관리 및 트랜잭션 생성을 대리하거나 사용자가 직접 특정 컨트랙트 주소로 ABI를 통해 트랜잭션을 보내는 방법 등이 있지만 이는 전자의 방법에 비해 일반적이지 않다.

이 논문의 저자들은 무료로 제공되는 RPC 서비스들을 활용하여 eth_call, eth_estimateGas를 통해 가스 소비없이 서비스 거부 공격을 하는 가능성을 제시했고 이를 DoERS (Denial of Ethereum RPC Service) 라고 명명하였다. 이런 류의 메서드는 트랜잭션 발생없이 그냥 이더리움 블록체인 상태를 읽어오는 것이기 때문에 가스비가 들지 않거나 혹은 특정 RPC 서비스에서는 매우 적은 소량의 가스를 사용하게 한다. 저자들은 해당 연구가 P2P 네트워크의 취약점을 활용한 DoS 공격이라던지 EXCODESIZE, SUICIDE같은 낮은 비용의 EVM instruction을 반복 실행하게하는 기존에 알려진 취약점과는 다른 관점의 공격이라는 것이 차별점이라고 주장한다.

실용적인 연구 수행을 위해 저자들은 9가지 종류의 실제 이더리움 RPC 서비스들에서 실험을 진행했다. 또한, 로드밸런싱과 가스 제한을 포함한 RPC 서비스를 이용하면서 숨겨진 정보를 알기 위해 고아 트랜잭션을 기반으로 한 새로운 측정방법을 제시하였다. 그 결과 9개 서비스 모두에서 최소 2배에서 최대 50배까지 서비스 처리 지연이 일어났고 일부 공격은 단일 요청(CODECOPY)으로 진행할 수 있을만큼 간단하다. (2020년 4월 기준) 최적화된 예시로 150 여개의 DoERS 요청을 보내 타겟 RPC의 블록 동기화 속도를 91% 늦췄다. 이러한 공격을 통해 트랜잭션 순서를 인위적으로 지연시킬 수 있는데 이것은 DeFi 거래나 NFT minting에서 유저에게 악영향을 끼칠 충분한 유인이 된다. 저자들은 이런 취약점을 발견하는 것에 더해 예측불가능한 로드밸런싱이나 이상행동탐지 등을 활용해 서비스 사용성을 떨어뜨리지 않으면서도 이러한 공격을 완화시키는 방법을 제안했다.

그럼 이제부터 순서대로 저자들이 논한 취약점과 이에 대한 대책을 자세히 살펴보겠다.

DoERS ATTACK

저자들이 설계한 모델은 간단하다. 우선 서비스로 악의적인 RPC 요청을 보내는 공격자와 일반적인 RPC 요청을 보내는 정직한 클라이언트로 구성된다. 공격자의 목적은 RPC 서비스에 부하를 가해 정직한 클라이언트가 이더리움에서 의도한 동작이 지연되게 하는 것이다.

DoERS 공격을 효율적으로 하기 위해 저자들은 컴퓨팅 리소스를 많이 사용하게 하는 아래의 컨트랙트 코드를 사용하였다. 이 컨트랙트의 메서드의 공통점은 모두 리소스 사용량을 조절하는 payload_size 파라미터를 받는 것이다.

가장 기본적인 DoERS 공격은 다음 두 단계로 이루어진다.

  1. 공격자는 위 컨트랙트를 이더리움에 배포한다.
  2. 공격자는 한개 혹은 다수의 eth_call RPC를 통해 위 메서드 중 아무거나 실행시킨다.

이를 통해 RPC 서비스에서 운용하는 노드의 블록 동기화 속도나 정상적인 마이닝에 장애를 유발할 수 있다. 하지만 RPC 서비스에서 eth_call의 파라미터 사이즈에 대해 가스 제한을 걸어놓는다던가 타임아웃, 요청속도 제한, 로드밸런싱 같은 솔루션을 구축해놓을 경우 공격하기가 좀 더 까다로워진다. 따라서 해당 논문에서는 이러한 요인들을 본인들이 설계한 방법을 통해 측정하고 이를 활용해 좀 더 정교한 공격을 진행한다.

1) Load Balancer

첫번째로 저자들은 RPC 서비스들의 로드밸런서 구성을 추측하기 위해 다음 4가지 질문을 기반으로 분석을 진행한다.

(가) 같은 IP에서 같은 API key를 사용한 두 개의 RPC 요청은 로드밸런서가 같은 RPC peer로 포워딩하나?
(나) 같은 IP에서 다른 API key를 사용한 두 개의 RPC 요청은 로드밸런서가 같은 RPC peer로 포워딩하나?
(다) 다른 IP에서 발생한 두 개의 RPC 요청은 로드밸런서가 같은 RPC peer로 포워딩하나?
(라) 같은 IP에서 같은 API key를 사용했지만 x초의 간격이 있는 RPC 요청은 로드밸런서가 같은 RPC peer로 포워딩하나?

로드밸런서 구성이 어떠한 지 전혀 알 수 없는 상황에서 저자들은 GethParity의 고아 트랜잭션 처리 방식을 활용하여 이를 추정했다. 각 이더리움 트랜잭션은 발행자를 기준으로 nonce 값이 할당된다. 고아 트랜잭션은 해당 클라이언트의 최근 트랜잭션 nonce 값보다 2 이상 큰 트랜잭션들을 지칭하는데 일정 시간이 지날 때까지 그 앞전 nonce값을 가진 트랜잭션들이 오지 않으면 이것들은 버려지게 된다. 또한, 다른 gas price로 동일 nonce 값의 고아 트랜잭션을 연속해서 보냈을 때 두번째 트랜잭션이 성공할 경우 이것이 다른 peer에 할당되었다는 것을 추정할 수 있다.

위 코드는 앞서 제시한 개념을 통해 로드밸런서의 존재를 검증하기 위한 코드이다. RPC 서비스 별로 어떤 메서드인지에 따라 로드밸런서 적용 유무가 갈리는 것도 있어서 추가적으로 getTransaction()getBlockNumber()로 결과값 확인을 통해 검증을 진행했다. 만약 이 메서드들의 리턴값이 반복적으로 실행해도 동일 및 선형적 증가를 하면 이것은 단일 peer에 할당되고 있을 가능성이 매우 높다.

해당 논문에서는 9종류의 서비스 중 단 한개만이 RPC 타입별로 로드밸런서 할당 유무가 다르게 나왔다. 동일 IP, 동일 API key에 대해서 같은 peer에 할당한 API 서비스는 X1, X2, X3. 다른 IP, 동일 API key에 대해서 같은 peer에 할당한 서비스는 X4. 동일 IP, 다른 API key에 대해서 같은 peer에 할당한 서비스는 X5. 나머지 X6, X7, X8, X9는 동일 IP, 동일 API key에 대해서도 다른 peer에 할당하였다. 시간적 제한 측면에서는 X6은 5초가 넘어가면 새로운 peer를 할당했고 X9는 25초가 넘어가면 새로운 peer를 할당했다.

우선, 다른 IP, 동일 API key에 대하여 같은 peer를 할당하는 X4의 경우 DApp 웹사이트에서 손쉽게 노출된 API key를 획득한 후 DoERS 공격을 진행한다. 동일 IP에 다른 API key를 사용하는데 같은 peer에 할당하는 X5의 경우 공격자가 악의적인 ERC20 토큰 컨트랙트를 만들고 이를 에어드랍 등으로 임의로 뿌린다. 유저는 해당 토큰을 받고 balanceOf로 잔액 체크를 하는데 이 함수는 공격자에 의해 CPU 집약적인 연산이 추가되어 있는 상태다. 결국 해당 함수 리턴값이 발생하기 전까지 해당 유저는 다른 DApp 서비스를 이용할 수 없게 된다.

시간경과에 따라 다른 peer를 할당하는 X6의 경우 time window를 1분으로 봤을 때 최대 3개의 peer가 번갈아가며 할당되는 것을 포착하였고 이러한 사실을 활용해 경매 마감 직전이나 NFT minting 직전 1분 단위로 악의적 요청을 보내 트랜잭션을 방해할 수 있다.

2) Gas Limits

일반적으로 거의 가스를 사용하지 않는 eth_call을 활용해 메모리 집약적 연산인 exhaustMem을 실행시키고 이분 탐색을 통해 예외가 발생하는 배열 길이를 찾고 이 값을 로컬 노드의 estimateGas에 대입하여 해당 RPC 서비스의 eth_call gas limit을 정확하게 측정한다. 코드는 아래와 같다.

그 결과 X3, X6, X8, X9에서 각각 gas limit을 50/10/5/1.5 블록 가스를 설정하였음을 확인할 수 있었다.

gas limit이 설정되지 않은 RPC 서비스의 경우 공격자는 매우 큰 페이로드 파라미터(e.g. 10⁹)를 대입해 단일 RPC 명령으로 exhaustMem을 실행시킨다. 이것은 단지 단일 EVM instruction인 CODECOPY를 통해 실행되기 때문에 인터럽트 되지 않고 이더리움의 5초 타임아웃 제한도 걸리지 않는다. gas limit이 설정된 RPC 서비스의 경우 위에서 측정된 gas limit 대비 적정 배열 사이즈를 넣어 여러번 RPC 명령을 서비스로 보내야 한다. 이 요청 속도가 충분히 빠를 경우 가시적인 서비스 지연 효과를 보여줬다고 한다.

Evaluation of DoERS attack

해당 논문에서는 DoERS 공격의 유효 정도를 서비스의 지연 정도, 블록 동기화 시간 증가 등으로 측정하였고 공격 비용은 공격 비율에 의해 측정되었다. 또한, 실제 서비스 중인 DApp에 실험해보기 위해 우선 로컬에서 환경을 구성하여 유의마한 변화를 주기 시작하지만 그 값이 제일 작은 최솟값을 찾고 이를 기점으로 조금씩 값을 증가시키면서 서비스 지연 정도가 달라지는 경향을 파악했다는 점이 꽤나 인상깊었다. 실제 지연 정도 측정은 두개의 VM 인스턴스를 만들고 하나는 일반적인 RPC를 보내면서 이거에 대한 응답을 로깅, 이후 30초가 지난 후부터는 남은 VM 인스턴스를 활용해 DoERS 요청을 보냈다.

exhaustCPU attacks to RPC services (Type-i and iii)

그 결과 X2에서는 30000 payload, 30 request/sec 으로 부하를 줬을 때 응답시간이 5배 정도 증가하였다. (b)에서는 payload를 0.07M로 고정하고 요청 비율을 증가시키던가 payload를 증가시키고 요청 비율은 고정시킨 두가지의 케이스인데 전부 응답시간이 증가하는 것을 확인할 수 있다.

같은 IP, 같은 API key여도 특정 time window를 기준으로 라운드 로빈 형식으로 peer를 배정하는 X6의 경우 DoERS 공격을 시도했을 시 15초부터 해당 요청에 대해 null을 반환하였고 이후 응답시간 개선이 이루어짐을 볼 수 있다. 하지만 공격이 성공했던 순간에는 최대 5배 응답시간이 증가하였다.

다른 IP에 대해 다른 peer를 할당하는 X5나 다른 API key에 대해 다른 peer를 할당하는 X4를 대상으로 한 실험 결과는 아래와 같다. 동일 peer가 할당되는 상황에서는 역시 응답시간이 최대 10배까지 증가함을 볼 수 있다.

마지막은 gas limit이 없는 X2, X5에 대하여 DoERS 공격을 진행한 것인데 각각 20배, 150배 응답시간이 증가할만큼 효과적인 공격 양상을 보였다.

해당 논문에서 진행된 실험들은 전부 실제 RPC 서비스 장애를 우려해 최대한 영향이 적게 파라미터를 설계하였으므로 실제로는 더욱 강력한 공격을 설계할 수 있다.

마지막으로는 메타마스크 기반 DApp에 대하여 DoERS 공격을 진행한 실험이다. dappradar.com 기준으로 상위 26개 DApp 중 20개가 메타마스크를 사용하므로 메타마스크 기반 DApp에 대해 실험을 진행하였다고 한다. 이 때 첫번째 그래프는 200 req/sec에 페이로드 값은 8*10⁵을 준 것이다. 또한, 이렇게 동작하는 자바스크립트 코드를 실행하는 주기를 0~50초로 다양하게 변화를 주며 실행한 것에 대한 결과는 두번째 그래프이다. 메타마스크는 최근 요청(20초 내외)에 대하여 캐싱을 하기 때문에 이보다 주기가 짧으면 응답시간이 짧아지는 것을 볼 수 있다.

Proposed Countermeasures

DoERS가 발생하는 근본적인 원인은 여러 DApp이 사용하는 공개된 RPC 서비스에서 자유롭게 임의의 스마트 컨트랙트 함수를 실행시킬 수 있다는 것이다. 하지만 이를 제한한다면 서비스 사용성이 떨어진다. 따라서 해당 논문의 저자들은 DoERS 공격에 대한 피해를 최대한 경감시키면서도 RPC 서비스의 사용성을 저해하지 않는 몇 가지 방법을 제안한다.

1) Unpredictable load balancing

저자는 하나의 밸런서는 오직 트랜잭션만 처리하고 다른 밸런서는 오직 eth_call을 포함한 단순 RPC 쿼리만 처리하면서 균등 분포(혹은 무작위의 다른 방법)로 peer에 할당하는 듀얼밸런서 체제를 제안한다. 이 때 트랜잭션 전용 밸런서는 X6처럼 시간에 따른 제약성을 가진다. 이 구조의 단점은 트랜잭션과 트랜잭션과 관련된 RPC 쿼리의 순서가 보장되지 않을 수 있다는 점이 있다.

2) Interruptible EVM instructions

단일 EVM instruction으로 타임 아웃없이 DoERS 공격을 실행하는 케이스가 있었다. 따라서 EVM 자체에서 아토믹한 EVM instruction에 대해서도 타임 아웃 규칙을 도입하는 방법이 있다. 혹은 좀 더 구체적으로 단일 CODECOPY에 할당할 수 있는 메모리 양을 제한하는 방법이 있다.

3) Performance anomaly detection plus security deposit

매우 단순한 아이디어다. RPC 서비스를 이용하기 원하는 클라이언트에게 보증금을 받고 해당 고객의 RPC만 서비스에서 처리. 서비스 제공업체는 이 때 모니터링하면서 성능 감소 등의 이상 징후가 있는지 확인 후 별 다른 이상이 없으면 해당 보증금을 반환. 만약 이상 징후가 발견되면 공격자 식별 후 해당 공격자의 보증금을 몰수하는 아이디어다.

Conclusion

기존에 연구되던 취약점 공격과는 조금 다른 방향으로 RPC 서비스를 활용해 이더 사용없이 거의 제로 코스트로 공격을 시도하는 방법론을 제시하고 실제 실험도 유의미하게 진행한 논문이다. 특정 서비스에 과부하를 줘서 장애를 일으키는 방식은 기존에서 크게 벗어나지 않지만 eth_call의 특성을 살린 공격이나 RPC 서비스들의 로드밸런서 구성을 나름 독특한 방법을 통해 추정하려 했던 점에서 의미가 있는 논문이라고 생각한다.

- CURG(Crypto United Research Group)는 2018년 5월 대학(원), 기업, 스타트업 등 다양한 분야의 열정적인 블록체인-er들이 모인 연합 연구 그룹입니다. CURG는 2018년 5월 출범된 이후, ‘Trendy, Thoughtful, and Transdisciplinary’ 한 자세로 한 주도 빠짐없이 블록체인 연구를 지속해오고 있습니다.

- 디사이퍼(DECIPHER)는 “건강한 블록체인 생태계 조성에 기여한다” 라는 미션 아래 블록체인에 대해 연구하고 이를 실용적으로 응용하는 서울대 블록체인 연구 학회입니다. 2018년 3월에 처음 조직 되어 현재까지 블록체인 기술을 다방면에서 연구하고 산업계에 응용하고 있는 100명 이상의 회원들을 배출해왔습니다. 다양한 팀별 연구활동과 프로젝트, 컨퍼런스 개최, 서울대학교 블록체인 강의 개설, 유수 기업들과의 산학협력과 파트너십 체결을 해오며 국내 최고의 블록체인 학회로 자리 잡았습니다.

커그와 디사이퍼는 향후 적극적인 협력을 통해 블록체인 필드에서의 강력한 시너지를 구축하고자 합니다.

--

--