-
Notifications
You must be signed in to change notification settings - Fork 4
19. AlienCodex
Claim ownership of the contract
codex
is stored as a dynamic array. retract()
reduces codex
length without checking against underflow. So it is actually possible to set the codex
array length to 2²⁵⁶ -1, which gives power to modify all storage slots.
Storage Layout of dynamically sized variables
Each smart contract running on the Ethereum Virtual Machine maintains its own state using a key:value storage mapping. The number possible of keys is so huge that most keys actually contain empty values. Each key is called a slot. They are 2²⁵⁶ - 1 slots. Each slot can contain 32 bytes of data.
In the Level 8 -Vault, I listed the basic storage layout rules. Each statically sized variable gets a reserved slot which is defined at compilation time.
But what about dynamically sized variables? As their size is not fixed beforehand, how to know which slots to reserve?
With regular hard drive space or RAM an allocation step to find free space to use exists, which is followed by a release step to put that space back into the pool of available storage. The number of storage locations of a smart contract is so huge that it manages its storage differently. It just needs to figure a way to define a storage location to start from. Indeed the likelihood of having location clashes is (not rigorously) 0.
Due to their unpredictable size, mappings and dynamically-sized array types cannot be stored “in between” the state variables preceding and following them. Instead, they are considered to occupy only 32 bytes with regards to the rules above and the elements they contain are stored starting at a different storage slot that is computed using a Keccak-256 hash.
Assume the storage location of the mapping or array ends up being a slot p after applying the storage layout rules. For dynamic arrays, this slot stores the number of elements in the array (byte arrays and strings are an exception, see below).
Array data is located starting at keccak256(p) and it is laid out in the same way as statically-sized array data would
-
Analyze storage layout
Slot # Variable 0 contact bool (1 bytes) & owner address (20 bytes), both fit on one slot 1 codex.length keccak256(1) codex[0] keccak256(1) + 1 codex[1] ... 2²⁵⁶ - 1 codex[2²⁵⁶ - 1 - uint(keccak256(1))] 0 codex[2²⁵⁶ - 1 - uint(keccak256(1)) + 1] --> can write slot 0! -
call
make_contact
to be able to pass thecontacted
modifer -
call
retract
: this provokes and underflow which leads tocode.length = 2²⁵⁶ - 1
-
Compute codex
index
corresponding to slot 0:2²⁵⁶ - 1 - uint(keccak256(1)) + 1 = 2²⁵⁶ - uint(keccak256(1))
-
Call
reverse
passing itindex
and your address left padded with 0 to total 32 bytes ascontent
Modifying a dynamic array length without checking for over/underflow is very dangerous as it can expand the array's bounds to the entire storage area of 2^256 - 1. This can possibly enable modifying the whole contract storage.