
SharkTeam: 멀티콜(Multicall) 임의 주소 위조 취약점 원리 분석
글: SharkTeam
2023년 12월 8일, OpenZeppelin은 커뮤니티에 중요한 보안 경보를 공식 발표했습니다. 이 경보는 프로젝트 통합 시 ERC-2771 표준과 멀티콜(Multicall) 유사 방식을 함께 사용할 경우 임의 주소 위조 공격의 위험이 있을 수 있음을 지적했습니다.
SharkTeam은 이 사건에 대해 즉각 기술 분석을 수행하고 보안 대응 방안을 정리하여 향후 프로젝트들이 교훈 삼아 블록체인 산업의 보안 방어선을 함께 구축하기를 기대합니다.
1. 공격 거래 분석
이 취약점과 관련된 일련의 공격 거래가 발생했기 때문에, 그 중 한 건의 공격 거래를 선정하여 분석하겠습니다.
공격자 주소:
0xFDe0d1575Ed8E06FBf36256bcdfA1F359281455A
공격 거래:
0xecdd111a60debfadc6533de30fb7f55dc5ceed01dfadd30e4a7ebdb416d2f6b6
공격 흐름:
1. 먼저 공격자(0xFDe0d157)는 5개의 WETH를 이용해 약 3,455,399,346개의 TIME을 교환했습니다.

2. 이후 공격자(0xFDe0d157)는 악성 calldata 매개변수를 구성한 후 [Forwarder].execute 함수를 호출했습니다.
3. [Forwarder].execute 함수를 호출할 때, 악성 calldata는 TIME 컨트랙트의 multicall 함수를 트리거합니다. 그 후 남은 calldata를 사용해 TIME 컨트랙트의 burn 함수를 실행하며, 풀 내 TIME 토큰을 소각합니다.

2. 취약점 분석
우선 이번 공격 사건은 주로 다음 세 가지 요소와 관련이 있습니다: ERC2771, Multicall, 정교하게 구성된 calldata. 우리는 TIME 토큰 컨트랙트에서 다음과 같은 상속 관계를 확인할 수 있습니다:

1. ERC2771은 가상의 msg.sender를 갖는 기능을 제공하며, 사용자가 제3자 [Forwarder]에게 트랜잭션 실행을 위임할 수 있게 하여 가스 비용을 절감할 수 있습니다. 트랜잭션 제출 시, msg.sender 주소가 calldata에 추가됩니다.
2. TIME 토큰 컨트랙트는 ERC2771Context를 상속받고 있습니다. [Forwarder]가 컨트랙트를 호출하면 _msgSender()는 calldata를 검사하고 이를 우측으로 이동시켜 마지막 20바이트를 예상되는 msg.sender로 간주합니다.

3. Multicall은 단일 함수 호출을 동일한 컨트랙트 내에서 여러 함수를 순차적으로 호출하는 방식으로 변환하는 방법입니다. 사용자가 인코딩된 호출 배열을 전달하면 해당 컨트랙트 자체에서 이를 실행합니다. 이 함수는 호출 배열을 반복 처리하며 각 작업에 대해 delegatecall()을 수행합니다. 이를 통해 사용자는 자신만의 일련의 작업을 조합해 하나의 트랜잭션 안에서 순차적으로 실행할 수 있으며, 프로토콜 측에서 미리 특정 작업 조합을 정의할 필요가 없습니다. 주요 목적 역시 가스 절약입니다.

4. 정교하게 구성된 calldata의 경우, 공격자는 [Forwarder].execute 함수를 호출하고 관련 매개변수를 전달합니다.

data 값을 읽기 쉬운 형식으로 포맷팅한 결과는 다음과 같습니다:

공격자(0xFDe0d157)는 현재 calldata에 오프셋 연산을 적용해 새로운 data 값을 얻고, 이를 multicall(bytes[]) 함수에 전달합니다. 새 data의 처음 4바이트는 burn(uint256) 함수의 선택자이며, amount 매개변수는 62227259510000000000000000000입니다.
5. multicall(bytes[]) 함수 내부에서 delegatecall을 통해 burn(uint256) 함수가 호출됩니다. 0x20 행에서, 0x760dc1e043d99394a10605b2fa08f123d60faf84 주소는 calldata 생성 시 맨 끝에 추가된 것입니다. 이 주소는 Uniswapv2 상의 TIME-ETH 유동성 풀 주소와 일치하며, 앞서 언급한 예상 msg.sender입니다.

6. 위에서 언급한 msg.sender가 어떻게 TIME-ETH 유동성 풀 주소가 되었을까요? 이유는 처음 msg.sender가 [Forwarder] 컨트랙트 주소였기 때문입니다. 신뢰할 수 있는 [Forwarder]인지 여부를 판단하기 위해, 만약 신뢰할 수 있는 [Forwarder]라면 msg.sender를 calldata의 마지막 20바이트로 설정합니다.
3. 보안 권고사항
이번 공격 사건의 근본 원인은 ERC-2771에서 [Forwarder]가 멀티콜(multicall) 전용으로 설계되지 않았다는 점에 있습니다. 공격자는 _msgSender() 함수의 관련 매개변수를 multicall 외부 호출에 추가했는데, 이는 본 사례에서 [Forwarder].execute 함수에 해당합니다. multicall 함수 내부에서 일부 함수들도 _msgSender()의 관련 매개변수를 추가하게 되며, 이로 인해 공격자는 _msgSender()를 속일 수 있게 됩니다. 따라서 공격자는 multicall을 사용해 관련 함수를 호출함으로써 임의 주소의 호출을 모방할 수 있었고, 결국 풀 내 TIME 토큰을 소각할 수 있었습니다.
이 사건에 대응하여 다음과 같은 완화 및 예방 조치를 취할 수 있습니다:
1. 버그 수정된 최신 버전을 사용하세요. OpenZeppelin의 최신 버전 Multicall은 ERC2771context 데이터에 context 접미사 길이를 포함하여 ERC-2771이 예상하는 context 접미사 길이를 식별합니다. 따라서 신뢰할 수 있는 [Forwarder]로부터 오는 모든 호출은 각 서브 함수 호출마다 식별되고 적절히 처리됩니다.

다음은 버그 버전과 업데이트된 버전의 비교 이미지입니다:

2. 어떤 컨트랙트도 multicall을 호출하지 못하도록 차단하여 [Forwarder]의 사용을 방지하세요. ThirdWeb의 사례를 들 수 있습니다. 이 방법은 OpenZeppelin의 해결책과 비교할 때, OpenZeppelin은 여전히 컨트랙트를 통한 multicall을 허용합니다. 다음은 ThirdWeb의 관련 버그 버전과 업데이트된 버전의 비교 이미지입니다.

【면책 조항】시장에는 위험이 따르며, 투자는 신중해야 합니다. 본 문서는 투자 권유를 목적으로 하지 않으며, 독자는 본문에 포함된 의견, 견해 또는 결론이 본인의 특정 상황에 부합하는지 여부를 고려해야 합니다. 이를 바탕으로 한 투자 결정에 따른 책임은 전적으로 본인에게 있습니다.
TechFlow 공식 커뮤니티에 오신 것을 환영합니다
Telegram 구독 그룹:https://t.me/TechFlowDaily
트위터 공식 계정:https://x.com/TechFlowPost
트위터 영어 계정:https://x.com/BlockFlow_News










