Solidity Attack— Array Underflow

Chen-I Chang
3 min readMay 5, 2019

--

In this article, we are going to reproduce the array underflow and change the variable value in a smart contract.

What is underflow?

An underflow error occurs when one attempts to access the example array using an index value less than 0. Either of these errors results in accessing memory not allocated to the array. The result of accessing memory outside the memory allocated to the array is undefined in many languages.

Before we jump into the attack approach, we have to understand how Solitidy stores variable in the memory.

How does data store in solidity?

Reference: Understanding Ethereum Smart Contract

Each smart contract running in the Ethereum Virtual Machine (EVM) maintains state in its own permanent storage. This storage can be thought of as a very large array, initially full of zeros. Each value in the array is 32-bytes wide, and there are 2256 such values. A smart contract can read from or write to value at any location. That’s the extent of the storage interface.

However, storage is extremely sparsely populated, and there’s no need to store all the zeros. A key/value store mapping 32-byte keys to 32-byte values will do the job nicely. An absent key is simply defined as mapping to the value zero.

For example:

a => in slot 0

b => in slot 1

c => in slot 2

Ok! Now you know the basic concept of how solidity handles constant size variable.

But what if it’s dynamic? Like array or mapping?

Here is the example

Slot 5 stores the length value of d. The array value is actually stored consecutively starting from the hash value of the address of slot 5.

Therefore, in the previous example. Slot 6 stores variable e

To calculate the address via slot number, we can use the solidity syntax keccak256

keccak256(1)or web3.sha3('0x0000000000000000000000000000000000000000000000000000000000000001', {encoding: 'hex'})

Perform Array Underflow

If we execute the popLength() for three times, it will cause array underflow.

First, the value you see 115792089237316195423570985008687907853269984665640564039457584007913129639935 equals to 2²⁵⁶-1

In hexadecimal format, it’s 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

This is how solidity handle with unsigned integer. If someone store the the negative value inside it, it turns the value into 2²⁵⁶-1. If you keep subtracting it, it won’t change the value but stay 2²⁵⁶-1.

Secondly, we click the debugger and see how the data store.

As you can see, our first length variable now is underflow.

We can compute the hash of the key value to get the starting address of the consecutive array.

web3.sha3('0x0000000000000000000000000000000000000000000000000000000000000001', {encoding: 'hex'})// => 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6

Ok, then we have to know the

Cool, now we use the way we always use to inference the address of the target variable. Since the target is the first variable, we can simply use 2²⁵⁶-(the address of starting array to get it).

perl -Mbigint -E 'say ((2**256–0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6 + 0)->as_hex)'//=> target's address
0x4ef1d2ad89edf8c4d91132028e8195cdf30bb4b5053d4f8cd260341d4805f30a

Last, use its underflowed array to modify the target variable.

(Don’t forget to add double quotes)

Then you can access the target variable and see it become 0

--

--