Detecting Critical Smart Contract Vulnerabilities with MythX

In my previous article, I explained the various analysis techniques MythX uses to detect security flaws in smart contracts. But how do you unleash this security analysis powerhouse on your own code? In this article I’ll show the use of Sabre, a MythX command line client, to detect critical smart contract weaknesses.

While Turing-complete smart contracts are awesome, the added flexibility also allows programmers to introduce many types of security vulnerabilities. With the right tooling however, many critical flaws can be caught early in the development lifecycle. MythX makes this process simple.

Currently, the quickest way is running one of the MythX command line clients, such as Mythos, Sabre and the PythX CLI. In this article I’ll show the use of Sabre to detect the some of the most prevalent and critical smart contract weaknesses:

In case you want try the examples yourself, install Sabre by running:

$ npm install -g sabre-mythx

Head to mythx.io and sign in with MetaMask to create your free MythX account. For even greater convenience, you can add your credentials to ~/.bashrc if on a UNIX-based system:

export MYTHX_ETH_ADDRESS=0x[...]
export 'MYTHX_PASSWORD=[your_password]'

You can now run Sabre on Solidity files as follows:

$ sabre <filename.sol>

This will trigger the “quick” analysis mode which runs static analysis, input fuzzing and symbolic analysis on your smart contract for about a minute — enough to detect low-hanging fruits such as misnamed constructors, accidental killability and re-entrancy bugs. Let’s walk through some examples.

Weak Randomness

Our first vulnerable contract is “borrowed” from Steve Marx’s Capture the Ether CTF. It’s a lottery smart contract that pays out Ether to lucky players who correctly guess a random number. Take a long hard look at the code and see if you can spot the bug. Spoilers ahead!

Source: SWC Registry

To analyze this contract with Sabre, save it to a file named random.sol and run sabre random.sol. This should return a list of security issues:

$ sabre random.sol
✔ Downloaded solc v0.4.21 successfully
[== ] Analyzing GuessTheRandomNumberChallenge
random.sol
25:12  warning  Sending of Ether depends on a predictable variable  https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-120
25:12  error    Anyone can withdraw ETH from the contract account   https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-105
7:0   warning  A floating pragma is set                            https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-103
10:10  warning  The state variable visibility is not set            https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-108
14:33  warning  Use of disallowed function "block.blockhash()"      https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-111
14:33  warning  Potential use of a weak source of randomness        https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-120
14:49  warning  Potential use of a weak source of randomness        https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-120
✖ 7 problems (1 error, 6 warnings)

Each line contains the following elements:

  • Line and column number: This shows the position of the issue in the source code (for example: 25:12 = line 25, column 12).
  • Severity level: Sabre uses the ESLint-style error levels: “info”, “warning” and “error”. (Other MythX tools may follow different schemes.)
  • Heading: A brief one-sentence description of the bug.
  • SWC URL: A link to a detailed description of the bug in the Smart Contract Weakness Registry. The link is especially useful if you can’t make sense of the short description. Open the URL in a web browser to learn more about the weakness and possible fixes.

In our first example, there are several warnings related to weak randomness (SWC-120). The first result is particularly interesting: “Sending of Ether depends on a predictable variable ”. Here, MythX has detected that Ether can be withdrawn by guessing a “random” variable that isn’t really all that random (see below).

Indeed, an attacker can exploit the contract by calculating keccac256(block.blockhash(block.number — 1)) themselves and submitting the right answer. You can try exploiting this for yourself.

Integer Overflows and Underflows

Integer arithmetic bugs are another popular smart contract security flaw. Have a look at TokenSale, another vulnerable contract snagged Capture the Ether. Spoiler: It contains at least one exploitable integer overflow.

Here is what Sabre reports:

$ sabre Tokensale.sol
✔ Downloaded solc v0.5.0 successfully
[== ] Analyzing TokenSale
Tokensale.sol
1:0   warning  A floating pragma is set                           ttps://smartcontractsecurity.github.io/SWC-registry/docs/SWC-103
25:8   error    Anyone can withdraw ETH from the contract account  https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-105
16:29  error    The binary multiplication can overflow             https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-101
18:8   error    The binary addition can overflow                    https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-101
✖ 4 problems (3 errors, 1 warning)

This tells us that both the multiplication in line 16 and the addition in line 18 can overflow:

More specifically, MythX is telling you that there are no appropriate checks on the variables used in thise arithmetic operations, and the overflows aren’t caught by asserts either. By overflowing numTokens * PRICE_PER_TOKEN such that the result is zero, an attacker can add a large amount of tokens to their balance without transferring any Ether (can you figure out the exact value?).

Re-Entrancy

Ever since the infamous DAO hack, re-entrancy has taken the Ethereum security community by storm. If one would to have to pick the ultimate smart contract security bug, it would certainly be re-entrancy.

The SWC Registry has a couple of examples for re-entrancy. SimpleDAO by Josselin Feist more or less replicates the DAO flaw in a simplified way:

Here is what Sabre reports:

$ sabre simple_dao.sol
✔ Downloaded solc v0.4.24 successfully
[== ] Analyzing SimpleDAO
simple_dao.sol
17:14  warning  A call to a user-supplied address is executed. https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-107
12:4   error    The binary addition can overflow https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-101
18:6   error    persistent state read after call  https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-107
18:6   error    persistent state write after call https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-107
✖ 4 problems (3 errors, 1 warning

There are two parts to this problem. First, a call to msg.sender is executed (line 17). Following the call there is a write to the persistent state variable credit[msg.sender] in line 18:

Persistent account state should never be updated after calls to untrusted addresses. In the example above, msg.sender can point to a smart contract account that contains code to re-enter the withdraw() function. By recursively calling back into withdraw(), an attacker can withdraw Ether multiple times before the amount withdrawn is subtracted from the attacker’s balance. Check out SWC–107 for more information.

Broken Access Control

Insufficient access controls on critical functions such as Ether transfers and self-destructs are another classic. First, here is a vulnerable wallet example from the SWC Registry:

Source: SWC Registry

Sabre has the following to say:

$ sabre wallet_03_wrong_constructor.sol
✔ Downloaded solc v0.4.24 successfully
[== ] Analyzing Wallet
/wallet_03_wrong_constructor.sol
1:0   warning  A floating pragma is set                           https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-103
8:12  warning  The state variable visibility is not set           https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-108
10:32  warning  The state variable visibility is not set           https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-108
31:8   error    Anyone can withdraw ETH from the contract account  https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-105
17:5   warning  A reachable exception has been detected            https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-110
17:12  error    The binary addition can overflow                   https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-101
24:8   error    persistent state read after call                   https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-107
24:8   error    persistent state write after call                  https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-107
7:0   warning  precondition violation                             https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-123
✖ 9 problems (4 errors, 5 warnings)

The problem here is that there’s an initialization function that allows anyone to become the owner (line 12). Once the attacker has taken control of the contract, they can withdraw all Ether from the contract (line 31):

A current limitation of MythX is that we don’t yet provide call traces or test cases that explain exactly how the vulnerability is triggered. In this case, it’s a bit difficult to deduce what exactly must happen so Ether can be withdrawn (in this case, a call initWallet() followed by migrateTo(address to)). We’re working on a call trace feature which will become available later during the beta — I’ll update this article once it is available.

A similar infamous bug is the “anybody can kill this contract” vulnerability. Here is an example from the SWC Registry:

Source: SWC Registry

Here is what Sabre reports:

$ sabre suicide_multitx_feasible.sol
✔ Downloaded solc v0.4.23 successfully
[== ] Analyzing SuicideMultiTxFeasible
suicide_multitx_feasible.sol
16:8  error    The contract can be killed by anyone  https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-106
1:0  warning  A floating pragma is set              https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-103
11:4  warning  The function visibility is not set    https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-100
✖ 3 problems (1 error, 2 warnings)

MythX has correctly determined that it is possible for anyone to kill the contract. The vulnerability is almost the same as in the previous example: Anyone can kill the contract by calling the init() function followed by run(uint256 input).

TL;DR

MythX is a security analysis API that detects smart contract security weaknesses. Specifically, MythX detects weakness listed in the Smart Contract Weakness Classification Registry.

Current MythX tools are basic, but the MythX community is building a whole ecosystem of smart contract security tools that integrates into IDEs, code editors and CI services. You can build your own tools or use existing ones to analyze Ethereum smart contract for security vulnerabilities.

Join our Discord community to get support or share ideas with the MythX team. And please join our mailing list (sign up at bottom) to stay informed of updates and new releases.