
Blast 체인 9700만 달러 다툼, 어느 국가의 해커가 서툴러졌나?
글: CertiK
배경
Blast는 Blur의 창립자 Pacman(Tieshun Roquerre, aka. 철순)이 개발한 이더리움 레이어 2 네트워크로, 2월 29일 메인넷을 론칭했으며 현재 약 19,500 ETH와 640,000 stETH가 Blast 메인넷에 스테이킹되어 있다.
공격을 당한 프로젝트 Munchables는 Blast가 주최한 Bigbang 대회에서 우승한 고품질 프로젝트이다.
Blast 공식은 Blast 메인넷에 ETH를 스테이킹한 사용자들에게 일반 포인트를 지급한다:

사용자가 Blast 생태계 내 DeFi 프로젝트에 참여하도록 장려하기 위해, Blast 공식은 우수한 프로젝트를 선정하여 추천하고, 사용자들이 ETH를 DeFi에 재스테이킹하면 더 빠른 포인트 적립 속도와 골드 포인트를 받을 수 있도록 유도하고 있다. 이에 따라 다수의 사용자들이 Blast 메인넷에 스테이킹된 ETH를 새롭게 출시된 DeFi 프로젝트에 다시 스테이킹했다.
하지만 이러한 DeFi 프로젝트들의 성숙도와 보안성은 여전히 검증되지 않았으며, 이러한 계약이 수천만 달러에서 수억 달러에 달하는 사용자 자산을 안전하게 보호할 수 있는지에 대한 의문이 제기된다.

사건 개요
Blast 메인넷 론칭 후 한 달도 채 되지 않아, 2024년 3월 21일 SSS 토큰(Super Sushi Samurai)을 대상으로 한 공격이 발생했다. 토큰 컨트랙트 내 전송 로직 오류로 인해 공격자는 특정 계좌의 SSS 토큰 잔액을 무한정 증가시킬 수 있었고, 결국 프로젝트는 1,310 ETH 이상(약 460만 달러)의 피해를 입었다.
SSS 토큰 공격 사건이 발생한 지 일주일도 되지 않아, Blast에서는 또 다른 대규모 공격이 발생했으며, Munchables 프로젝트는 공격자에게 17,413.96 ETH(약 6,250만 달러)를 모두 탈취당했다.
이 공격 거래 발생 30분 후, 프로젝트 팀의 컨트랙트에 있던 73.49 WETH도 공격자에 의해 다른 주소로 도난당했다.
이 시점에서 프로젝트 팀의 컨트랙트 주소에는 여전히 7,276개의 WETH, 7,758,267개의 USDB, 4개의 ETH가 남아 있었으며, 언제든지 해커의 손에 넘어갈 위험에 처해 있었다. 해커는 프로젝트 전체 자금을 인출할 권한을 보유하고 있었고, 총 약 9,700만 달러가 위험에 노출된 상태였다.
사건 직후 X(Twitter)의 유명 체인상 탐정 zachXBT는 이번 공격의 근본 원인이 특정 국가의 해커를 고용했기 때문이라고 지적했다.
그렇다면 '특정 국가의 해커'는 어떻게 거의 1억 달러 규모의 공격을 수행했는지 자세히 살펴보자.

현장 복원
피해자 입장
[UTC+0] 2024년 3월 26일 오후 9시 37분(공격 발생 5분 후), Munchables 공식 계정은 X(Twitter)를 통해 공격을 당했다고 발표했다.

체인상 탐정 ZachXBT의 조사에 따르면, 그들의 개발자 중 한 명이 '특정 국가의 해커'였기 때문이다. Aavegotchi의 창립자 coderdannn도 X(Twitter)에서 "Aavegotchi 개발팀 Pixelcraft Studios는 2022년에 Munchables 공격자를 게임 개발 업무를 위해 단기간 고용한 적이 있으며, 그는 기술 수준이 매우 낮았고 실제로 특정 국가의 해커처럼 보였으며, 한 달 만에 해고했다. 그는 우리에게 친구 한 명을 고용하도록 설득하려 했는데, 그 친구 역시 아마도 해커일 가능성이 높다"고 밝혔다.
이번 공격으로 커뮤니티 사용자들이 막대한 피해를 입었기 때문에, 우리는 즉각 체인상 조사를 시작했으며, 이제 '특정 국가의 해커'의 공격 세부 사항을 자세히 살펴보자.
첫 번째 현장
[UTC+0] 2024년 3월 26일 오후 9시 32분, 17,413.96 ETH 규모의 공격이 발생했다.
Blastscan을 통해 해당 공격 거래를 확인할 수 있다: https://blastscan.io/tx/0x9a7e4d16ed15b0367b8ad677eaf1db6a2a54663610696d69e1b4aa1a08f55c95

피해를 입은 컨트랙트(0x29..1F)는 프록시 컨트랙트이며, 사용자의 스테이킹 자금을 보관하고 있다. 공격자가 스테이킹 컨트랙트의 unlock 함수를 호출했고, 모든 권한 검사를 통과한 후, 컨트랙트 내 모든 ETH를 공격자 주소 1(0x6E..c5)로 전송한 것을 확인할 수 있다.

마치 공격자가 withdraw와 유사한 unlock 함수를 호출하여 피해 컨트랙트(0x29..1F)의 대부분의 ETH를 인출한 것처럼 보인다.
프로젝트 팀의 금고가 자물쇠를 잊어버린 것일까?
피해 컨트랙트(0x29..1F)의 unlock 함수에는 두 가지 관련 검사가 존재하며, 하나씩 살펴보자.
우선, 권한 검사 과정에서 컨트랙트(0x16..A0)의 isRegistered 메서드를 호출하여 현재 msg.sender, 즉 해커 주소 1(0x6E..c5)이 등록되었는지를 확인한다:


답변은: 검사를 통과했다.

여기에는 컨트랙트(0x16..A0) 및 그에 대응하는 최신 로직 컨트랙트(0xe7..f1)가 관련된다.
[UTC+0] 2024년 3월 24일 오전 8시 39분(공격 발생 2일 전), 컨트랙트(0x16..A0)의 로직 컨트랙트가 업그레이드되었다.

로직 컨트랙트 업그레이드 거래:
https://blastscan.io/tx/0x9c431e09a80d2942319853ccfdaae24c5de23cedfcef0022815fdae42a7e2ab6
로직 컨트랙트가 0xe7..f1로 업데이트되었다.
초기 로직 컨트랙트 주소는 다음에서 확인할 수 있으며, 0x9e..CD이다.
https://blastscan.io/tx/0x7ad050d84c89833aa1398fb8e5d179ddfae6d48e8ce964f6d5b71484cc06d003

이때 우리는 공격자가 프록시 컨트랙트의 로직 구현 컨트랙트를 업데이트하여 0x9e..CD를 악의적인 0xe7..f1로 바꾸고, 권한 검사를 우회했는지 의심하게 된다.
정말 그런가?
Web3.0 세계에서는 결코 추측하거나 남의 말을 믿을 필요 없다. 기술만 익히면 직접 답을 얻을 수 있다.
우리는 두 개의 컨트랙트(비공개 컨트랙트)를 비교하여 초기 0x9e..CD 컨트랙트와 업데이트된 0xe7..f1 컨트랙트 사이에 몇 가지 명백한 차이점을 발견했다:
0xe7..f1의 initialize 함수 부분 구현은 다음과 같다:

0x9e..CD의 initialize 함수 부분 구현은 다음과 같다:

확인할 수 있듯이, 공격자는 초기 로직 컨트랙트(0x9e..CD) 내에서 공격자 주소(0x6e..c5)를 register로 설정했으며, 다른 두 개의 공격자 주소 0xc5..0d, 0xbf..87도 register되었고, field0 값은 초기화 당시 블록 시간으로 설정되었다. field0의 용도는 이후 설명하겠다.
실제로 우리의 추측과 정반대로, 진짜 백도어가 숨겨진 로직 컨트랙트는 처음부터 존재했으며, 이후 업데이트된 것이 오히려 정상적인 컨트랙트였다!
잠깐, 이 업데이트는 [UTC+0] 2024년 3월 24일 오전 8시 39분(공격 발생 2일 전)에 나타났으며, 즉 이 사건 이전에 로직 컨트랙트는 이미 백도어 없는 컨트랙트로 변경된 것이다. 그런데 왜 이후에도 공격자가 공격을 완료할 수 있었을까?
이는 delegatecall 때문이며, 실제 상태 저장 업데이트는 컨트랙트(0x16..A0) 내에서 이루어지기 때문에, 이후 로직 컨트랙트가 백도어 없는 정상 컨트랙트 0xe7..f1로 업데이트되더라도, 컨트랙트(0x16..A0) 내에서 변경된 slot은 복구되지 않는다.
검증해보자:

컨트랙트(0x16.....A0)의 해당 slot에 값이 있음을 확인할 수 있다.
이로 인해 공격자는 isRegistered 메서드의 검사를 통과할 수 있었다:

공격자는 이후 백도어 컨트랙트를 정상 컨트랙트로 교체하여 눈속임을 했지만, 사실 백도어는 이미 심어져 있었다.
또한, unlock 과정에서는 두 번째 검사도 존재한다:
자산 잠금 시간을 확인하는 것으로, 이는 잠긴 자산이 만기 전에 인출되는 것을 방지하기 위한 것이다.

공격자는 unlock 함수가 호출될 때의 블록 시간이 요구되는 잠금 만기 시간(field3)보다 커야 한다는 조건을 충족해야 한다.
이 부분 검사는 피해 컨트랙트(0x29..1F)와 해당 로직 컨트랙트 0xf5..cd에 관련된다.
[UTC+0] 2024년 3월 21일 오전 11시 54분(공격 발생 5일 전) 거래에서,
https://blastscan.io/tx/0x3d08f2fcfe51cf5758f4e9ba057c51543b0ff386ba53e0b4f267850871b88170

피해 컨트랙트(0x29..1F)의 초기 로직 컨트랙트가 0x91..11임을 확인할 수 있으며, 단지 4분 후,
https://blastscan.io/tx/0xea1d9c0d8de4280b538b6fe6dbc3636602075184651dfeb837cb03f8a19ffc4f

0xf5..cd로 업그레이드되었다.
같이 두 개의 컨트랙트를 비교해보면, 공격자가 이전과 동일하게 initialize 함수에 조작을 했다는 것을 알 수 있다.
0xf5..cd의 initialize 함수 부분 구현:

0x91..11의 initialize 함수 부분 구현:

매우 명확하게, 동일한 방법이 다시 사용되었으며, 자신의 ETH 보유량과 해제 시간을 모두 조작한 후, 정상 컨트랙트로 교체하여 눈속임을 했다. 프로젝트 팀이나 보안 연구원이 디버깅할 때는 로직 컨트랙트가 모두 정상으로 보이며, 게다가 모든 컨트랙트가 비공개이기 때문에 핵심 문제를 파악하기 더욱 어렵다.
이제 우리는 17,413 ETH 규모의 거래가 어떻게 이루어졌는지 이해했지만, 이 사건 뒤에는 더 많은 정보가 있을까?
위 분석에서, 우리는 해커가 컨트랙트 내부에 3개의 주소를 미리 내장했음을 확인했다:
0x6e..c5(공격자 주소 1)
0xc5..0d(공격자 주소 2)
0xbf..87(공격자 주소 3)
하지만 우리가 발견한 공격 거래에서는 0x6e..c5만 확인되었고, 나머지 두 주소는 무엇을 했는가? 또한 address(0), _dodoApproveAddress, _uniswapV3Factory에는 어떤 비밀이 숨겨져 있는가?
두 번째 현장
먼저 공격자 주소 3(0xbf..87)을 살펴보자. 동일한 방법으로 73.49 WETH를 탈취했다:
https://blastscan.io/tx/0xfc7bfbc38662b659bf6af032bf20ef224de0ef20a4fd8418db87f78f9370f233
또한 공격 가스의 출처 주소(0x97..de)는 0xc5..0d(공격자 주소 2)와 0xbf..87(공격자 주소 3) 모두에게 수수료를 제공했다.

공격 가스 출처 주소(0x97..de)의 0.1 ETH 자금 출처는 owlto.finance(크로스체인 브릿지)이다.
0xc5..0d(공격자 주소 2)는 수수료를 받은 후 아무런 공격도 수행하지 않았지만, 사실은 숨겨진 계획을 수행하고 있었다. 계속해서 살펴보자.
실제로 공식 구조 거래에 따르면, 피해 컨트랙트(0x29..1F) 주소에는 73.49 weth 외에도 공격 종료 시점까지도 7,276.5 WETH & 7,758,267 USDB가 남아 있었다.
구조 거래:
https://blastscan.io/tx/0x1969f10af9d0d8f80ee3e3c88d358a6f668a7bf4da6e598e5be7a3407dc6d5bb

원래 공격자는 이러한 자산을 훔쳐가려 했으며, 0xc5..0d(공격자 주소 2) 주소는 USDB를 훔치기 위해 사용될 예정이었다.

여기서 _dodoApproveAddress는 0x0000000000000000000000004300000000000000000000000000000000000003이다.

USDB의 주소이다.

0xbf..87(공격자 주소 3) 주소는 weth를 훔치기 위해 사용되었다:

여기서 _uniswapV3Factory는 0x0000000000000000000000004300000000000000000000000000000000000004이다.

weth의 주소이다.

0x6e..c5(공격자 주소 1)는 address(0), 즉 기본 자산 ETH를 훔쳤다.
공격자는 field0을 설정함으로써 아래 로직을 통해 해당 자산을 훔칠 수 있었다:


문제점
왜 공격자는 모든 자산을 훔치지 않았는가?
이론적으로 그는 나머지 WETH와 USDB를 포함한 모든 자산을 훔칠 수 있었다.
0xbf..87(공격자 주소 3)는 단지 73.49 WETH만 훔쳤다. 0xbf..87(공격자 주소 3)은 사실 7,350 WETH 전체를 가져갈 수도 있었고, 0xc5..0d(공격자 주소 2)를 이용해 7,758,267 USDB도 모두 가져갈 수 있었다. 그런데 왜 아주 소량의 WETH만 가져간 후 멈췄는가? 우리는 이유를 알 수 없으며, 유명 체인상 탐정이 내부 조사를 통해 알아내야 할 것이다.
https://blastscan.io/tx/0xfc7bfbc38662b659bf6af032bf20ef224de0ef20a4fd8418db87f78f9370f233

왜 공격자는 17,413 ETH를 이더리움 메인넷으로 옮기지 않았는가?
众所周知, Blast 메인넷은 중심화된 방식으로 이러한 ETH를 차단하여 영구히 여기에 머물게 하고, 실질적인 사용자 손실을 피할 수 있다. 하지만 일단 이 ETH가 이더리움 메인넷에 들어가면 더 이상 차단할 수 없다.
현재 Blast의 크로스체인 브릿지를 평가해보면, 공식 브릿지는 수량 제한이 없지만 14일의 출금 시간이 필요하므로, Blast 공식이 차단 계획을 준비할 충분한 시간을 갖게 된다.
그러나 제3자 크로스체인 브릿지는 거의 실시간으로 도착할 수 있으며, 공격자의 수수료 출처와 같이 빠르게 크로스체인을 완료할 수 있다. 그런데 왜 공격자는 즉시 크로스체인을 수행하지 않았을까?
사실 공격자는 첫 순간(공격 후 2분 이내)에 크로스체인을 수행했다:
https://blastscan.io/tx/0x10cf2f2b884549979a3a1dd912287256229458ef40d56df61738d6ea7d9d198f

자금은 20초 만에 이더리움 메인넷에 도착했으며, 이론적으로 공격자는 지속적으로 크로스체인을 수행하여 인공 개입 전에 대량의 ETH를 이전할 수 있었다.

왜 매번 3 ETH만 가능한가? 이유는 크로스체인 브릿지의 유동성 제한 때문이다. Blast에서 ETH로 크로스체인할 경우:

다른 Blast를 지원하는 크로스체인 브릿지는 더 적은 양을 지원한다:

이 크로스체인 거래 후, 공격자는 추가적인 크로스체인 작업을 수행하지 않았으며, 그 이유는 알 수 없다. 마치 '특정 국가의 해커'가 자금을 Blast에서 인출하기 위한 충분한 준비를 하지 않은 것처럼 보인다.
공격 후 사건 전개
커뮤니티 사용자 Nearisbuilding의 피드백에 따르면, 그는 공격자의 더 많은 신원 정보를 찾아냈으며, 공격자가 자금을 반환하도록 설득하는 방법을 모색했다.
https://twitter.com/Nearisbuilding/status/1772812190673756548


결국 암호화 커뮤니티의 관심과 노력 덕분에, '특정 국가의 해커'는 신원 노출을 두려워하여 프로젝트 팀에게 위의 3개 공격자 주소의 개인키를 제공했으며, 모든 자
TechFlow 공식 커뮤니티에 오신 것을 환영합니다
Telegram 구독 그룹:https://t.me/TechFlowDaily
트위터 공식 계정:https://x.com/TechFlowPost
트위터 영어 계정:https://x.com/BlockFlow_News










