Re-entrancy 그 이상 : Revest Finance 공격 이벤트 분석
BlockSec
2022-04-18 11:06
本文约5095字,阅读全文需要约20分钟
이더리움의 스테이킹 DeFi 프로젝트인 Revest Finance가 해킹을 당해 약 200만 달러를 잃었습니다.

2022년 3월 27일 이더리움의 스테이킹 DeFi 프로젝트인 Revest Finance가 해킹을 당해 약 200만 달러를 잃었습니다. BlockSecTeam 팀은 즉시 분석에 개입하여 분석 결과를 Twitter 커뮤니티와 공유했습니다. 실제로 Twitter를 통해 분석 결과를 커뮤니티와 공유했을 때 Revest Finance의 TokenVault 계약에서 치명적인 제로데이 취약점을 발견했습니다. 이 취약점을 사용하여 공격자는 더 간단한 방법으로 프로토콜의 자산을 훔칠 수 있습니다. 그래서 우리는 즉시 Revest Finance 프로젝트 당사자에게 연락했습니다. 취약점이 수정된 것을 확인한 후 이 블로그를 커뮤니티와 공유하기로 결정했습니다.

0. What's the Revest Finance FNFT

보조 제목

Revest Finance는 DeFi 분야의 스테이킹을 위한 솔루션입니다.Revest Finance를 통해 사용자가 참여하는 모든 DeFi 스테이킹은 스테이킹 포지션의 현재 및 현재 상태를 나타내는 NFT, 즉 FNFT(Finance Non-Fungible Token)를 직접 생성할 수 있습니다. 미래 가치. 사용자는 Revest Finance에서 제공하는 3가지 인터페이스를 통해 프로젝트와 상호 작용할 수 있습니다. 자신의 디지털 자산을 약속하고 해당 FNFT를 발행하십시오.

•mintTimeLock : 사용자가 약정한 디지털 자산은 일정 시간이 지난 후에만 잠금 해제가 가능합니다.

•mintValueLock : 사용자가 담보로 제공한 디지털 자산은 사전 설정된 가치로 평가 또는 평가절하된 경우에만 잠금을 해제할 수 있습니다.

•mintAddressLock : 사용자가 담보로 제공한 디지털 자산은 미리 설정된 계정으로만 잠금을 해제할 수 있습니다.

Revest Finance는 다음 세 가지 스마트 계약을 통해 사용자가 예치한 디지털 자산의 잠금 및 잠금 해제를 완료합니다.

•FNFTHandler: ERC-1155 토큰에서 상속됨(openzepplin 구현). 잠금 작업이 실행될 때마다 fnftId가 증가합니다(fnftId는 ERC721의 tokenId와 유사함). FNFT가 생성되면 사용자는 totalSupply를 지정해야 합니다. 사용자가 FNFT의 기본 자산을 인출하려면 해당 비율의 FNTF를 소각해야 합니다.

•LockManage : FNFT가 잠금 해제(unlock)될 조건을 기록합니다.

•TokenVault: 사용자가 예치한 기본 자산을 수신 및 전송하고 각 FNFT의 메타데이터를 기록합니다. 예를 들어, fnftId = 1인 FNFT 뒤에 저당 잡힌 자산 유형입니다.

이 공격 때문에 해커 공격의 진입점은 mintAddressLock 함수이므로 이 함수를 예로 들어 FNFT의 수명 주기를 설명하겠습니다.

  • •unlocker : User X ->사용자 A가 Revest의 mintAddressLock 기능을 호출합니다.

  • 사용자 X만이 이 자산을 잠금 해제할 수 있습니다. •받는 사람: [사용자 A , 사용자 B , 사용자 C] • 수량: [50 , 25 , 25] -> 발행 수량은 100(합계(수량)) , 사용자 A , 사용자 B , 사용자 C는 각각 50, 25, 25 코인을 소유하고 있습니다. •자산 : WETH -> 조폐국의 FNFT는 WETH를 담보로 합니다. • depositAmount : 1e18 -> 각 FNFT의 담보 금액은 1 WETH입니다(WETH 십진수는 18).

  • 현재 시스템에 다른 FNFT가 없다고 가정하면 사용자 A는 mintAddressLock을 통해 시스템과 상호 작용하고 FNFTHandler = 1에서 반환된 fnftId는

  • •fnftId : 1•unlocker : User X 

  • LockManger는 해당 레코드를 추가합니다.

  • •fnftId : 1•asset : WETH•depoistAmount : 1e18

Token Vault는 해당 레코드를 추가합니다.

그런 다음 Token Valut은 사용자 A로부터 100 * 1e18 WETH를 전송해야 합니다.

마지막으로 시스템은 사용자 A, 사용자 B 및 사용자 C에게 각각 50, 25 및 25개의 01-FNFT 조각을 제공합니다.

이는 mintAddressLock 기능을 통해 FNFT를 발행함으로써 수행됩니다.

사용자 X가 01-FNFT를 잠금 해제한 후 사용자 B는draftFNFT를 통해 기본 자산을 인출할 수 있습니다. 그림 2와 같이 사용자 B는 01-FNFT가 약속한 25개의 디지털 자산을 인출하려고 합니다.

프로토콜은 먼저 01-FNTF가 잠금 해제되었는지 확인하고 잠금 해제된 경우 프로토콜은 사용자 B의 01-FNFT 25개를 소각하고 25*1e18 WETH를 그에게 전송합니다. 이때 01-FNFT의 totalSupply는 75입니다.

Revest 계약은 또한 사용자가 기존 FNFT에 기본 자산을 더 추가할 수 있도록 depositAdditionalToFNFT라는 또 다른 인터페이스를 제공합니다. 아래에서 2개의 사진으로 "정상적인" 사용법을 설명합니다.

여기에는 세 가지 경우가 있습니다

1.quantity == 01-FNFT.totalSupply() 그림 3과 같이

•quantity = 75 ->그림 2의 시나리오를 컨텍스트로 삼아 사용자 A는 01-FNFT에 대해 더 많은 담보를 추가하려고 합니다.

•amount = 0.5*1e18 ->75 01-FNFT에 대한 추가 서약.

각 01-FNFT는 0.5*1e18 WETH를 추가합니다.

따라서 사용자 A는 37.5*1e18 WETH(75 * 0.5*1e18)를 Token Vault로 전송해야 하며, Token Vault는 시스템 회계를 수정하고 depositAmount를 1.5*1e18로 변경합니다. 이제 각 01-FNFT는 1.5*1e18 WETH의 자산을 보유합니다.

이때 사용자 C는 자신이 보유하고 있는 25개의 01-FNFT를 소각하기 위해withdrawalFNFT를 호출하고 25*(1.5*1e18) = 37.5*1e18 WETH를 가져갈 수 있습니다.

따라서 현재 01-FNFT의 총 공급량은 50입니다.

2.quantity < 01-FNFT.totalSupply() 그림 4와 같이

  • •quantity = 10 ->그림 3의 장면을 맥락으로 삼아 사용자 A는 01-FNFT에 대해 더 많은 담보를 계속 추가합니다.

  • 10 01-FNFT에 대한 추가 서약. •amount = 0.5*1e18 -> 10개의 01-FNFT 각각에 0.5*1e18 WETH 추가< 01-FNFT.totalSupply()
    수량으로 인해

  • •fnftId : 2•asset : WETH•depositAmount : 2.0*1e18 (1.5*1e18 + 0.5*1e18) 

  • 따라서 사용자 A는 약정에 대해 5*1e18 WETH를 지불하고 시스템은 10 01-FNFT를 소각하고 10 02-FNFT를 발행하며 10 01-FNFT가 보유한 자산과 사용자 A의 새로 이관된 자산을 소각하여 02-FNFT에 주입합니다. . 그래서 있다

  • 이때

• 01-FNFT.totalSupply : 40 01-FNFT.depositAmount : 1.5*1e18 (논리적으로 그래야 합니다. 아래 참조: 새로운 제로데이 취약점) • 02-FNFT.totalSupply : 10 02-FNFT.depositAmount : 2.0* 1e18

이 경우 거래가 취소됩니다.

1. What't the Re-entrancy vulnerability

보조 제목

mintAddressLock 함수와 depositAdditionalToFNFT 함수의 기본 워크플로를 이해한 후 공격자가 사용하는 재진입 방법을 살펴보겠습니다. 최신 fnftId = 1이라고 가정합니다(이해에 영향을 미치지 않음).
그림 5와 같이
첫 번째 단계:

•depositAmount = 0 

•quantities = [2] 

공격자는 mintAddressLock 함수를 호출합니다.

Mint는 공격자가 depositAmount를 0으로 설정하여 디지털 자산을 전송하지 않았기 때문에 01-FNFT 2개를 발행했습니다. 01-FNFT 뒤에 있는 기본 자산에 해당하는 것은 0입니다.

•depositAmount = 0

2단계: 공격자가 mintAddressLock 함수를 다시 호출합니다.

•수량 = [360000] 조폐국 36w 조각 준비 02-FNFT 보증금 금액은 0입니다.

민트의 마지막 단계에서 공격자는 ERC-1155 콜백 메커니즘을 사용하여 depositAdditionalToFNFT 기능을 다시 입력했습니다. (자세한 내용은 아래의 _doSafeTransferAcceptanceCheck 기능 참조)

•quantity = 1

•amount = 1*1e18

•fnftId = 1 

depositAdditionalToFNFT에서 공격자는 다음을 전달합니다.< fntfId.totalSupply(),수량 때문에

마지막으로 공격자는 인출NFNFFT 기능을 호출하고 360,001 02-FNFT를 소각하고 360,001*1e18 RENA를 가져갑니다.

  • 제안된 수정 사항

2. the New Zero-day Vulnerability

보조 제목

blockSecTeam 팀이 Revest Finance의 코드를 분석하는 동안 handleMultipleDeposits 기능이 우리의 관심을 끌었습니다.

사용자가 담보를 추가하기 위해 depositAdditionToNFT 함수를 호출하면 이 함수는 FNFT의 depositAmount를 변경합니다. 코드에서 newFNFTId != 0일 때 이 함수는 fnftId에 해당하는 FNFT의 depositAmount뿐만 아니라 newFNFTId에 해당하는 depositAmount도 변경한다는 것을 알 수 있습니다.

상식적으로 newFNTId !=0일 때 시스템은 newNFTId에 해당하는 depositAmount만 기록해야 합니다. fnftId에 해당하는 depositAmount는 변경하면 안됩니다.
우리는 이것이 매우 심각한 논리 버그라고 생각하며 공격자는 이 취약점을 악용하여 시스템에서 디지털 자산을 쉽게 인출할 수 있습니다. 다음 세 그림은 모의 공격의 원리를 설명합니다.

최신 fnftId = 1이라고 가정합니다.
먼저 공격자는 mintAddressLock 함수를 호출하고 mint는 360,000개의 01-FNFT를 생성합니다. 공격자는 자산을 Revest Finance 프로토콜로 전송할 필요가 없도록 금액을 0으로 설정합니다.

발행이 종료된 후 공격자는 depositAmount =0인 360000 01-FNFT를 보유합니다.

•fnftId = 1

•amount = 1 * 1e18

•quantity = 1

그런 다음 공격자는 다음 매개 변수를 사용하여 depositAdditionalToFNFT 함수를 호출합니다.
프로토콜은 공격자 수 * 토큰 수, 즉 1 * 1e18RENA를 전송합니다.
프로토콜은 공격자의 01-FNFT 1개를 소각하고 이를 위해 02-FNFT 1개를 생성합니다(최신 fnftId = 2라고 가정).
handleMultipleDeposits 함수의 논리에 따라 fnftId = 2인 자산의 depositAmount는 1.0*1e18로 설정됩니다.

그러나 fnftId = 1인 자산의 경우 depositAmount도 1.0*1e18로 설정되며 이는 0이어야 합니다!

분명히 이 방법을 사용하여 공격하는 것이 실제 재진입 공격보다 더 간단하고 직접적입니다.

  • 텍스트

이 취약점에 대응하여 blockSecTeam 팀은 해당 패치 방법을 제공했습니다.

보조 제목

TokenVault와 FNFTHandler의 두 가지 취약한 계약은 많은 주요 상태를 저장하기 때문에 단기간에 재배치할 수 없습니다. 0x36c2732f1b2ed69cf17133ab01f2876b614a2f27# 코드). 이 버전은 추가 공격을 피하기 위해 대부분의 복잡한 기능을 끕니다. 프로젝트 당사자는 상태를 마이그레이션하고 미래에 고정된 계약을 재배포할 것입니다.

보조 제목

DeFi 프로젝트의 보안을 개선하는 것은 쉬운 일이 아닙니다. 코드 감사 외에도 커뮤니티가 DeFi 커뮤니티를 더 안전하게 만들기 위해 프로젝트 모니터링 및 조기 경고, 심지어 공격 차단과 같은 보다 적극적인 방법을 채택해야 한다고 생각합니다. (https://mp.weixin.qq.com/s/o41Da2PJtu7LEcam9eyCeQ).

참조

*[1]: https://blocksecteam.medium.com/revest-finance-vulnerabilities-more-than-re-entrancy-1609957b742f

BlockSec
作者文库