Hacking Smart Contracts — Token

Ethernaut Token

Getting Away With Debt

What if you could add tokens to your wallet, without spending any real money? This challenge will describe two vulnerabilities that will let you do that, refer below for the code:

As we can see above, the transfer function will deduct the value of the users balance, as long as, the difference is positive, meaning, the user should not be able to deduct more money than he has…

Understanding Overflows & Underflows

Lets say we have only 2 bits to represent our money, which means that the lowest value is 0 (bits: 00) and the highest value is 3 (bits: 11). For a capacity of 2 bits, summing or subtracting an amount which results in a value greater than 3 or lower than 0 will effectively cycle backward/forward just like a clock, or mathematically, the result will be mod 3 (res % 3):

  • Overflow: 3 + 1 = 0
  • Underflow: 0 – 1 = 3

Another thing to account for is the variable type: int vs unsigned int. This won’t change the Underflow or Overflow behaviour, but the result will change, because if the value is of type unsigned int , then the result will cycle through positive numbers only, but if the value is of type int, then it will also cycle through the negative set of numbers. Check the table below:

The above table shows what numbers are possible to represent, with 3 bits, for unsigned and signed values. This means the following for the Underflow and Overflow vulnerabilities:

  • Signed Overflow: 3 + 2 = -3
  • Unsigned Overflow: 7 + 2 = 1
  • Signed Underflow: 0 – 2 = -2
  • Unsigned Underflow: 0 – 3 = 5

If a program does not account for this behaviour, a whole logic or condition based on this premise might become faulty.

The Underflow vulnerability

Reading the code carefully, we can see that the smart contract checks whether the user withdraws more money than he has in the transfer() function using the uint (unsigned integer) types :

require(balances[msg.sender] - _value >= 0);

The uint type is an alias for uint256 which means it can store up to 2²⁵⁶ = 115792089237316195423570985008687907853269984665640564039457584007913129639935. So if a user has balance of 3 and tries to withdraw 4 what do you think will happen?

require(3 - 4 >= 0);
require(115792089237316195423570985008687907853269984665640564039457584007913129639935 >= 0) ;This evaluates to True

Following up the code, the smart contract will update the user’s balance, doing the same subtraction, which means the user will end with more balance than he ever had before.

Exploitation

First we check the token supply and how much balance the user has, then we just need to transfer to an arbitrary account 21 tokens which should result in 2²⁵⁶ tokens in our account:

root@Web3 ❯ python -i ethernaut_token.py
[+] Loaded wallet addr: 0xC43D69354685c5718EC4549b7590808dc3f2b533 with balance: 0.09999894189702654
[+] Loaded target contract addr: 0x52b5899362d9a6f6cf58011BFe15BB0c0FF71B8C
[+] Token supply: 21000000
[+] Account balance: 20
Transfering 21 tokens elsewhere...
[+] Final balance: 115792089237316195423570985008687907853269984665640564039457584007913129639935

For reference, the final code can be seen below:

References

Big props to @the_ethernaut for creating the content.


Hacking Smart Contracts — Token was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.

Share: Twitter LinkedIn