DEV Community

Cover image for Mythril an easy way to audit your smart contracts.
Vasiliy Gualoto
Vasiliy Gualoto

Posted on

Mythril an easy way to audit your smart contracts.

What the heck is Mythril?

Image description

Is not a rare ore from a fantasy novel neither a video game armor set, but one of the most powerful smart contract security tools, allowing to detect vulnerabilities on the bytcode for a lot of EVM-compatible Blockchains, such as Ethereum, Vechain, Hedara, Roostock and Tron.

Mythril is part of the core tools of Consensys Mythx one of the biggest Smart Contract security services for Ethereum, which main goal is to ensure development teams avoid costly errors and make Ethereum more secure and trustworthy… or at least that is what their page says.

This information is great, but…

Why should you care about about this?

Well, according to Chainalysis a trusted blockchain analytics firm, security breaches led nearly 1.4 billion dollars being stolen on the first half of 2022, which is crazy if your think about it. There are a bunch of examples, like Wormhole and Poly Network with more than $320 million in loss or the biggest one was being the hack to Ronin Network with more than $600 million in compromised assets, and the last one on August to Nomad Protocol with around two hundred million dollars in losses.

Image description

So, As you can see, protecting our smart contracts is really important, moreover most of this hacks happened due to human errors and lack of security measures on the smart contracts, therefore prevention is key, but is not that simple, and is really easy to get confused because there a lot of problems, vulnerabilities and attacks like:

  • Reentrancy attacks
  • Arithmetic over/under flows
  • Delegate call Problems
  • Short Address Attacks
  • Denial Of Service attacks (DOS)
  • Attack Attacks!

Ok, may the the las one not, but you get the point. Anyways, with time you’ll become a more experienced developer and you’ll have better understanding on all of these problems and you will be able to choose the correct approach for each one. However, if you are not that experienced the problems are not that obvious.

Let’s see an example, here we have a contract like this on which you can deposit and and withdraw some funds, may be for a crowdfunding campaign.

//SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract ReentrancyIssue {
    mapping(address => uint) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

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

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}
Enter fullscreen mode Exit fullscreen mode

If we run this code with our framework it seems everything runs well, so that’s it right? We have a functional and good smart contract. Well, not really, this contract is vulnerable to Reentrancy attacks which broadly allows a potential hacker to execute a Fallback function to withdraw the funds over and over again without updating the state of the contract.

Image description

So basically the attacker sends over an address which is not a wallet but another contract, and the contract executes some arbitrary code stealing all the funds on the main contract. This is not obvious at all! Specially if you are starting your journey as smart contract developer so, how can we be aware about this problems?

First of all, you should always keep looking for more information and educational resources to learn about this issues so you will become a better dev with time, meanwhile? We can use some tools so let’s give a look into Mythril.

Installing Mythril

To use this tool you need some prerequisites already installed such as python, brownie, NPM and a Unix Terminal (WSL) in this case. If you already have them you can install Mythril, but if you don’t have any or some of them, don’t forget to check out the Github repo related article on which you can find more instructions on how to install that dependencies and also the commands to get Mythril running.

The process for any Unix based terminal is really straight forward, you just need some commands on your terminal and you will be ready to go.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
pip install mythril
Enter fullscreen mode Exit fullscreen mode

Using Mythril

Once the installation process is done we can continue, so let’s analyze our smart contract in order to find vulnerabilities, so using the following command we can order Mythril to start analyzing our contract.

myth analyze contracts/VulnerableContracts/ReentrancyIssue.sol
Enter fullscreen mode Exit fullscreen mode

This process could take from some seconds to several minutes, and it give us the following output:

Image description

Image description

  • The Issue ID.
  • Which Function is causing the problem.
  • The amount of gas used.
  • A detailed explanation of the vulnerability, in this case we are allowing to send a external address to the contract as parameter, and that address could be another contract which is the one who executes the attack.
  • The exact line on which the problem occurs.
  • The whole execution sequence.

This is great, because if we didn’t know about this issue before, we can now fix our smart contract to be more robust and secure.

So easiest way to do this is adding a modifier which will lock the function until it finishes processing, so even if the attacker wants to use that fallback function over and over again, as the function is locked until this line is completed we are now reentrancy attack safe, so the fixed contract with look like this:

//SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract ReentrancyGuarded {
    mapping(address => uint) public balances;
    bool internal _inCall;

   // THIS LINE!!
    modifier nonReentrant() {
        require(!_inCall, "ReentrancyGuard: reentrant call");
        _inCall = true;
        _;
        _inCall = false;
    }

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

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

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}
Enter fullscreen mode Exit fullscreen mode

Let’s see another example, here we have a contract with a old solidity version meant to be a place to buy and sell tokens.

//SPDX-License-Identifier: MIT

pragma solidity ^0.4.21;

contract IntegerVulnerability {
    mapping(address => uint256) public balanceOf;
    uint256 constant PRICE_PER_TOKEN = 1 ether;

    function sale(address _player) public payable {
        require(msg.value == 1 ether);
    }

    function isComplete() public view returns (bool) {
        return address(this).balance < 1 ether;
    }

    function buy(uint256 numTokens) public payable {
        require(msg.value == numTokens * PRICE_PER_TOKEN);
        balanceOf[msg.sender] += numTokens;
    }

    function sell(uint256 numTokens) public {
        require(balanceOf[msg.sender] >= numTokens);
        balanceOf[msg.sender] -= numTokens;
        msg.sender.transfer(numTokens * PRICE_PER_TOKEN);
    }
}
Enter fullscreen mode Exit fullscreen mode

After running the myth analyze tool we discover we can have problems when our uint variables due to overflows so if a variable reach its maximum capacity it will default again to 0 (including other potential problems).

Image description

The easiest solution for this is just, use newer solidity version which manages that overflows automatically and you don’t need something like Safe Math.

pragma solidity ^0.8.0;
Enter fullscreen mode Exit fullscreen mode

What about other kind of vulnerabilities which are not that common and maybe we have never heard about before? This contract has an auto destruct functionality, which only the owner of the contract should be able to execute.

//SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract SelfDestruct {
    address owner;
    mapping(address => uint256) balances;

    bytes11 private backdoorpwd = "cryptocrome";
    bool private passwordentered = false;

    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can call this function.");
        _;
    }

    constructor() {
        owner = msg.sender;
    }

    function kill() public onlyOwner {
        selfdestruct(payable(owner));
    }

    function activateBackdoor(bytes11 _password) public {
        require(_password == backdoorpwd, "Wrong password");
        passwordentered = true;
    }

    function pwnContract() public {
        require(passwordentered == true, "Backdoor not activated");
        owner = msg.sender;
    }
}
Enter fullscreen mode Exit fullscreen mode

So we run the analyzer tool, this time with 3 transactions because that’s the minimum amount in order to kill a contract.

myth analyze contracts/VulnerableContracts/SelfDestruct.sol -t3
Enter fullscreen mode Exit fullscreen mode

And we will receive a information about our self destruct is not protected, and you know what I don’t know what that means, even with the description over here.

Image description

That’s why the ID is important, because we can just Google it. And there are useful pages like this one with real helpful information on the vulnerability and its possible remediation, I also recommend to read the other vulnerabilities on this page to improve your understanding of Solidity.

After understanding what the vulnerability was, we discover the problem is because we have a easily crackable password, so anyone can use the self destruct function without permission. So we must not use something like this on our contracts.

Another problem solved, nice… isn’t it?

Contracts with imports

What about more contract with imports, like this on which we use OpenZeppelin.

//SDPX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/proxy/utils/Initializable.sol";

contract MetamorphicVulnerability is Initializable {
    address payable owner;

    function kill() external {
        require(msg.sender == owner);
        selfdestruct(owner);
    }
}
Enter fullscreen mode Exit fullscreen mode

We can still analyze it, but we some extra steps, Mythril analyze the bytecode of the compiled contracts and, as this is an external import if we execute this just like that is going to throw us an error. The solution is to create a json file with a remapping pointing the package on node modules, and of course we will need to add that package using NPM.

{
  "remappings": ["@openzeppelin=node_modules/@openzeppelin"]
}
Enter fullscreen mode Exit fullscreen mode

Installing OpenZeppelin.

npm install @openzeppelin/contracts
Enter fullscreen mode Exit fullscreen mode

Nice and simple, so now we can run the analyzer to just pointing to the remappings.

myth analyze contracts/VulnerableContracts/MetamorphicVulnerability.sol –solc-json mythril-remappings.json
Enter fullscreen mode Exit fullscreen mode

A tool for rule them all?

Therefore, is Mythril the ultimate tool for smart contract auditing? Well, not really, there are some scenarios on which Mythril is not good enough to detect some vulnerabilities, for example on the last contract with imports it has a vulnerability on which the contract might not be initialized, and as we can see on the on the analyzer output, Mythril thinks that everything is ok.

Image description

Summary

In conclusion, Mythril is not perfect, but is a really powerful tool all smart contract developer should know about, is easy to use and give us detailed outputs. Not mentioning it’s open source and EVM-compatible. My final recommendation is even if you did your diligences and self audited your contracts, if you are going to deploy a protocol which is going to handle real assets. I Still recommend you to hire a external auditory service, remember we are all humans and there could be problems we didn’t notice and having an external point of view is always important.

With that said, That’s all for reading this article till the end and don’t forget to checkout the Github repo… happy testing.

Top comments (0)