작가: 지우지우 & 리사

편집자: 셰리

배경

2025년 3월 30일, SlowMist MistEye 보안 모니터링 시스템의 모니터링에 따르면, 이더리움 체인의 레버리지 거래 프로젝트 SIR.trading(@leveragesir)이 공격을 받아 30만 달러 상당의 자산을 손실했습니다. SlowMist 보안팀은 해당 사건을 분석하고 다음과 같이 결과를 공유했습니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

 (https://x.com/SlowMist_Team/상태/1906245980770746449)

관련 정보

공격자 주소:

https://etherscan.io/주소/0x27defcfa6498f957918f407ed8a58eba2884768c

취약한 계약의 주소:

https://etherscan.io/주소/0xb91ae2c8365fd45030aba84a4666c4db074e53e7#code

공격 거래:

https://etherscan.io/tx/0xa05f047ddfdad9126624c4496b5d4a59f961ee7c091e7b4e38cee86f1335736f

필수 조건

Solidity 버전 0.8.24(2024년 1월 출시)에서는 EIP-1153을 기반으로 하는 일시적 저장 기능이 도입되었습니다. 이는 개발자에게 저렴하고 거래 효율적인 방식으로 일시적으로 데이터를 저장할 수 있는 방법을 제공하도록 설계된 새로운 데이터 저장 위치입니다.

일시적 저장소는 저장소, 메모리, 호출 데이터와 함께 새로운 데이터 위치입니다. 핵심 기능은 데이터가 현재 거래 실행 중에만 유효하고 거래가 종료되면 자동으로 지워진다는 것입니다. 임시 저장소에 대한 액세스 및 수정은 두 개의 새로운 EVM 명령을 통해 수행됩니다.

  • TSTORE(키, 값): 지정된 키 키에 해당하는 메모리에 256비트 값 값을 임시 저장소에 저장합니다.
  • TLOAD(키): 임시 저장소에 있는 지정된 키 키에 해당하는 메모리에서 256비트 값을 읽습니다.

이 기능의 주요 특징은 다음과 같습니다.

  • 낮은 가스 비용: TSTORE와 TLOAD의 가스 비용은 100으로 고정되어 있으며, 이는 따뜻한 스토리지 이용과 동일합니다. 비교해 보면 일반적인 저장소 작업(SSTORE)은 첫 번째 쓰기(0에서 0이 아닌 값까지) 시 최대 20,000가스, 업데이트 시 최소 5,000가스 비용이 듭니다.
  • 트랜잭션 내 지속성: 일시적으로 저장된 데이터는 모든 함수 호출과 하위 호출을 포함하여 트랜잭션 내내 유효한 상태를 유지합니다. 이는 일시적인 상태를 여러 호출에서 공유해야 하는 시나리오에 적합합니다.
  • 자동 청산: 거래가 완료되면 일시적 저장 공간이 자동으로 0으로 재설정되어 수동 정리가 필요 없고 개발자 유지 관리 비용이 절감됩니다.

근본 원인

이 해킹의 근본 원인은 함수 호출이 종료된 후에 tstore의 임시 저장소 값이 지워지지 않기 때문에 공격자가 이 기능을 사용하여 특정 악성 주소를 구성하여 권한 검사를 우회하고 토큰을 전송할 수 있다는 것입니다.

공격 단계

1. 공격자는 먼저 두 개의 악성 토큰 A와 B를 생성한 다음 UniswapV3에서 이 두 토큰에 대한 풀을 생성하고 유동성을 주입합니다. 여기서 토큰 A는 공격 계약입니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

2. 그런 다음 공격자는 Vault 계약의 초기화 함수를 호출하여 A 토큰을 담보 토큰으로, B 토큰을 부채 토큰으로 하는 레버리지 거래 시장 APE-21을 생성합니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

3. 그 직후 공격자는 Vault 계약의 mint 함수를 호출하고 부채 토큰 B를 예치하여 레버리지 토큰 APE를 주조합니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

mint 함수에서 후속 조치를 취한 결과, 부채 토큰 B를 mint 레버리지 토큰에 입금해야 할 때 전달해야 하는 collateralToDepositMin 매개변수의 값은 0과 같을 수 없다는 것을 발견했습니다. 그 후 B 토큰은 UniswapV3를 통해 담보 토큰 A로 교환되어 Vault로 전송되고, 여기서 공격자가 이전에 생성한 UniswapV3 풀의 주소가 처음으로 일시적으로 저장됩니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

UniswapV3 풀이 스왑 작업을 수행하면 Vault 계약의 uniswapV3SwapCallback 함수가 콜백됩니다. 다음 내용을 확인할 수 있습니다. 이 함수는 먼저 tload를 사용하여 지정된 키 1에 해당하는 메모리에서 일시적으로 저장된 값을 검색하여 호출자가 UniswapV3 풀인지 확인한 다음, 채굴자 주소에서 부채 토큰 B를 전송하고 레버리지 토큰 APE를 채굴하고, 마지막으로 채굴된 금액에 대한 두 번째 일시적으로 저장을 수행하여 지정된 키 1에 해당하는 메모리에 저장하고, 이를 mint 함수의 반환 값으로 사용합니다. 여기서 발행해야 할 금액은 공격자가 미리 계산하고 통제하며, 그 값은 95759995883742311247042417521410689입니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

4. 공격자는 Keyless CREATE2 Factory 계약의 safeCreate2 함수를 호출하여 계약 주소 0x00000000001271551295307acc16ba1e7e0d4281을 갖는 악성 계약을 생성합니다. 이는 두 번째 일시적 저장소의 값과 동일합니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

5. 공격자는 악성 계약을 사용하여 Vault 계약의 uniswapV3SwapCallback 함수를 직접 호출하여 토큰을 전송합니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

uniswapV3SwapCallback 함수는 호출자가 UniswapV3 풀인지 확인하기 위해 tload(1)을 사용합니다. 그러나 이전의 주조 작업에서 지정된 키 1에 대응되는 메모리의 값은 주조량 95759995883742311247042417521410689로 저장되었으며, 주조 함수가 호출된 후에도 메모리의 값은 지워지지 않습니다. 따라서 이 시점에서 uniswapPool의 주소는 0x00000000001271551295307acc16ba1e7e0d4281로 얻어지고, 이로 인해 호출자의 신원 확인이 잘못 통과됩니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

공격자는 또한 사전에 전송해야 할 토큰 수를 계산하고 최종 주조 금액을 지정된 값인 1337821702718000008706643092967756684847623606640으로 구성했습니다. 마찬가지로 이번에는 uniswapV3SwapCallback 함수를 호출한 후 세 번째 일시적 저장을 수행하여 지정된 키 1에 해당하는 메모리에 값을 저장합니다. 이 값은 공격 계약(A 토큰) 0xea55fffae1937e47eba2d854ab7bd29a9cc29170의 주소 값과 같아야 호출자에 대한 후속 검사를 통과할 수 있습니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

6. 마지막으로, 공격자는 공격 계약(토큰 A)을 통해 Vault 계약의 uniswapV3SwapCallback 함수를 직접 호출하여 Vault 계약의 다른 토큰(WBTC, WETH)을 전송하여 이익을 얻을 수 있습니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

MistTrack 분석

온체인 자금세탁 방지 및 추적 도구인 MistTrack의 분석에 따르면 공격자(0x27defcfa6498f957918f407ed8a58eba2884768c)는 17,814.8626 USDC, 1.4085 WBTC, 119,871 WETH를 포함하여 약 30만 달러 상당의 자산을 훔쳤습니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

WBTC는 63.5596 WETH로 교환되었고 USDC는 9.7122 WETH로 교환되었습니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

그런 다음 총 193.1428 WETH가 Railgun으로 전송되었습니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

또한 공격자의 초기 자금은 Railgun에서 전송된 0.3 ETH에서 나왔습니다.

Deadly Remains: 일시적 저장소로 인해 발생한 30만 달러 규모의 온체인 강도 사건

요약하다

이 공격의 핵심은 공격자가 프로젝트 내의 임시 저장소가 함수 호출 직후 저장된 값을 지우지 않고, 트랜잭션 기간 내내 저장한다는 사실을 이용해 콜백 함수의 권한 검증을 우회해 수익을 창출한다는 것입니다. SlowMist 보안팀은 프로젝트 소유자가 함수 호출이 종료된 후 해당 비즈니스 로직에 따라 즉시 임시 저장소에 있는 값을 지우기 위해 tstore(key, 0)을 사용할 것을 권장합니다. 또한, 유사한 상황이 발생하지 않도록 프로젝트의 계약 코드를 보다 엄격하게 감사하고 보안 테스트를 실시해야 합니다.