Ethernaut Solutions: 26-DoubleEntryPoint

So this is a Forta demo in the guise of a security challenge. We are presented with two ERC20 tokens: LegacyToken and DoubleEntryPoint. Apparently LegacyToken is an old token that is forwarding all transfer() requests to DoubleEntryPoint. There is another contract CryptoVault that holds DET (DoubleEntryPoint) tokens.

The challenge has 2 objectives:

  • Figure out a way to drain CryptoVault of all DET tokens
  • Write a Forta bot that can prevent this attack

CryptoVault has a sweepToken() function that can be used to transfer out all the tokens it holds except for DET which is its “underlying” token. CryptoVault even has a check in sweepToken() to prevent a transfer of DET:

require(token != underlying, "Can't transfer underlying token");

However it will still be possible to transfer DET via LegacyToken’s transfer() function if we pass LegacyToken’s address to CryptoVault’s sweepToken() function. LegacyToken’s transfer() function will invoke the DoubleEntryPoint’s delegateTransfer() function that has a modifier fortaNotify. What this modifier does is call a DetectionBot saved in the mapping “usersDetectionBots” inside the Forta contract. This DetectionBot receives msg.data from the caller (DoubleEntryPoint) and provides the bot an ability to raise an alarm (see botRaisedAlerts mapping inside Forta contract) should it sense any security issue. DoubleEntryPoint’s delegateTransfer() function then checks and reverts the transaction if a new alarm has been raised before the end of execution.

Our mission is to write such a DetectionBot that can catch DET transfer on time and prevent a sweep. Doing this should be straightforward – we just need to see if the DoubleEntryPoint’s delegateTransfer() was called from the CryptoVault contract. We will have to figure out the msg.sender from the msg.data that was sent to the DetectionBot. I have presented the code below. Please note I have not tested this bot but believe it should work.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IDetectionBot {
    function handleTransaction(address user, bytes calldata msgData) external;
}

interface IForta {
    function setDetectionBot(address detectionBotAddress) external;
    function notify(address user, bytes calldata msgData) external;
    function raiseAlert(address user) external;
}

contract MyDetectionBot is IDetectionBot
{
  address cryptoVault;
  
  constructor(address _cryptoVault )
  {
    cryptoVault = _cryptoVault;
  }
  
  function handleTransaction(address user, bytes calldata msgData) override external
  {   
    ( , , address sender) = abi.decode(msgData[4:], (address, uint256, address));

    if(sender == cryptoVault)
    {
      IForta forta = IForta(msg.sender);
      forta.raiseAlert(user);
    }
  }
}

Leave a Comment