본문 바로가기

Audit Report

[Audit Report] Axelar Network (1)

https://code4rena.com/reports/2023-07-axelar

 

Code4rena

Code4rena is a competitive audit platform that finds more high-severity vulnerabilities, more quickly than any other auditing method.

code4rena.com

 

[H-01] expressReceiveToken can be abused using reenty

호출에 실행할 데이터가 포함된 경우 다른 사람을 대신해서 토큰을 빠르게 보낼 수 있다.

    function expressReceiveTokenWithData(
        // ...params
  ) external {
        if (gateway.isCommandExecuted(commandId)) revert AlreadyExecuted(commandId);

        address caller = msg.sender;
        ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
        IERC20 token = IERC20(tokenManager.tokenAddress());

        SafeTokenTransferFrom.safeTransferFrom(token, caller, destinationAddress, amount);

        _expressExecuteWithInterchainTokenToken(tokenId, destinationAddress, sourceChain, sourceAddress, data, amount);

        _setExpressReceiveTokenWithData(tokenId, sourceChain, sourceAddress, destinationAddress, amount, data, commandId, caller);
    }
    
    ...
        function _expressExecuteWithInterchainTokenToken(
        //...params
    ) internal {
        IInterchainTokenExpressExecutable(destinationAddress).expressExecuteWithInterchainToken(
            sourceChain,
            sourceAddress,
            data,
            tokenId,
            amount
        );
    }

여기에 두 가지 경우가 있다.

 

1. 공격자(누구나), 제 3자가 on-chain call을 통해서 expressReceiveTokenWithData를 실행한다고 할때 

- 공격자는 public mempool이 있는 체인에 대량의 토큰을 전송한다.

- 공격자는 AxelarGateway::exectute가 멤풀에 보이는 것을 보고 프론트러닝을 통해서 expressReceiveTokenWithData를 먼저 제 3자에게 실행한다

- 제 3자(피해자)는 토큰을 destinationAddress컨트랙트에 보내고 공격자는 amount만큼을 받는다.

- destinationAddress 컨트랙트의 expressExecuteWithInterchainToken함수는 AxelarGateway::execute를 호출하고, 그 후 InterchainTokenService::execute에 자신의 commandId로 재진입 호출을 한다. 이는 TokenManager에서 destinationAddress로 두 번째 전송을 수행한다(이때 _setExpressReceiveTokenWithData가 아직 호출되지 않았음). 공격자 계약은 이제 원래 전송과 표현 전송 모두를 받아 2배의 양을 얻는다.

- 끝으로 _setExpressReceiveTokenWithData가 설정되지만, 해당 commandId는 이미 실행되었고, 피해자의 자금은 도난당했다.

 

2. AxelarGateway 운영자: 제3자가 오프체인 호출로 expressReceiveTokenWithData를 제공한다고 할 때

- 운영자는 위와 같은 방식으로 대량의 토큰을 전송한다. 그다음 operator(운영자)는 AxelarGateway::execute 업데이트를 유지하고 대신 이것을 공격컨트랙트인 destinationContract에 보낸다. expressReceiveTokenWithData가 호출되면 공격컨트랙트는 이제 1번(공격자가 할 때)랑 똑같은 패턴을 수행한다. 

 

따라서 외부함수를 호출하기 전에 _setExpressReceiveTokenWithData 를 먼저 실행해야 한다. 

_setExpressReceiveTokenWithData에서 통과된 tokenId인지를 체크해주기 때문에.

 

 

[H-02] ERC777 and similar token implementations allow a stealing of funds when transferring tokens

공격자는 TokenManager를 속여 더 많은 수량의 토큰이 전송되었다고 하게 할 수 있다. dstChain에서 공격자는 그들은 srcChain에서 보낸 것보다 많은 토큰을 받을 수 있다. 

 

TokenManagerLockUnlock과 TokenManagerLiquidityPool은 체인 간 토큰을 보낼 때 from/to 유저에게 토큰을 전송하는 TokenManger impl이다. low-level _tokeToken함수는 크로스체인 메시지가 나오기 전에 scrChain에서 유저로부터 토큰을 가져오는 데 사용된다. (ex. TokenManager.sendToken) 

TokenManager.sendToken

이 함수는 이체 전후의 유동성 풀 또는 TokenManager컨트랙트의 잔액 차이를 계산하여 실제 전송된 토큰 수량을 계산한다.

그런 다음 그 수량은 크로스체인 메시지로 전달되어 dstChain의 InterchainTokenService contract의 receiver에게 제공할 토큰 수를 알려 준다.

 

그러나 _takeToken함수는 reentrance공격을 막지 않았다.  공격 시나리오는 아래와 같다. 

1. 악의적인 컨트랙트가  TokenManager.sendToken을 호출해서  100 ERC777토큰을 전송한다.

2. _takeToken 함수는 ERC777 토큰 컨트랙트에서 transferFrom을 호출한다. 이는 악의적인 컨트랙트(발신자)에서 tokensToSend를 호출한다.

3. 그 훅에서 악의적인 컨트랙트는 또 다른 TokenManager.sendToken을 위한 콜을 만들고 100 토큰을 더 보낸다. 

4. 반복된 _takeToken에서 잔액이 100이 된다. 왜냐하면 잔액의 상태는 tokensToSend이후에만 업데이트되기 때문에. 그래서 재진입해서 토큰을 전송한 것만 카운트된다. 

5. 재진입한 call은 100토큰만 크로스체인에 전송할 것이다. 

6. 처음에 _takeToken을 호출했을때 transferFrom 호출 중에 receiver의 잔액이 두 번 증가해서 200이 된다.

7. 결과적으로 이런식으로 하면 srcChain에서는 100개를 보내는데 dstChain에서는 200개를 받을 수 있다. 

 

따라서 Openzeppelin에서 제공하는 ReentrancyGuard를 _takeToken에 추가해야 한다. 

'Audit Report' 카테고리의 다른 글

[Audit Report] Particle Protocol  (1) 2024.02.12
[Audit Report] Lens Protocol V2  (0) 2024.01.26
[Audit Report] Axelar Network (2)  (1) 2024.01.21
[Audit Report] Trader Joe v2 (2)  (1) 2024.01.14
[Audit Report] Trader Joe v2 (1)  (0) 2024.01.10