Solving Ethernaut 19 — Alien Codex

1 month ago 21
Photo by Kevin Ku on Unsplash

Ethernaut

The instrumentality to defeating this challenge, is knowing however static variables and dynamic arrays are stored.

The nonsubjective is elemental enough — claim the declaration by overwriting the “owner” address.

The declaration to crack

The archetypal happening you mightiness announcement is that the “AlienCodex” declaration inherits from different declaration called “Ownable”, but the codification for this declaration is not available. The “Ownable” declaration present is an aboriginal mentation of OpenZeppelin’s implementation, but for our purposes each we request to cognize is that this declaration volition store the “owner” code successful a storage slot.

Reminder connected retention mechanics successful astute contracts

All persisted authorities successful a contract — meaning each variables and each dynamic arrays — are stored successful a agelong database of 32 byte slots. Each slot has an scale starting from 0 and goes each the mode to scale 2²⁵⁶ -1. Each adaptable that is defined adjacent each different tin beryllium packed into a retention slot together, if they tin acceptable unneurotic into 32 bytes.

As successful erstwhile challenges you request to fig retired a mode to manipulate the retention slots successful an unintended way.

At archetypal glimpse this mightiness not look truthful straightforward, but let’s effort to fig retired however the variables are stored successful our contract. In the browser console you tin effort looking astatine the contract’s archetypal retention slot by writing:

await web3.eth.getStorageAt(contract.address, 0);

The effect you get backmost should look thing like this:

The worth is simply a 32 byte agelong hexadecimal string, with the past 40 characters being acceptable to an address. This is really the owners address, due to the fact that adjacent though we cannot spot the adaptable successful the codification provided by the ethernaut challenge, the adaptable is defined successful the “Ownable” contract. Variables are besides stored according to the inheritance hierarchy, truthful that a declaration inheriting from different declaration has it’s variables stored aft the declaration it inherits from. In our lawsuit it means that the proprietor code adaptable is stored successful slot 0, due to the fact that “AlienCodex” inherits from “Ownable”.

You tin effort checking retired the much retention slots by, but you volition find that each retention slots beyond slot 0 volition instrumentality 0 successful hexadecimal. Let’s look astatine the codification and fig retired a way forward.

bool nationalist contact;
bytes32[] nationalist codex;modifier contacted() {
assert(contact);
_;
}

The archetypal happening we request to bash is alteration the interaction adaptable to true, different we volition not beryllium capable to telephone the different methods. To bash truthful is straightforward. We conscionable request telephone this method:

function make_contact() nationalist { interaction = true; }

Again by utilizing the console successful the browser:

await contract.make_contact()

If you cheque retention slot 0 aft the telephone succeeded, you mightiness announcement thing peculiar:

There is present a 1 successful beforehand of the code successful our retention slot! This 1 is really the bool adaptable interaction that we conscionable acceptable to existent successful the past call. The crushed is that a bool lone takes up 1 byte, and it got packed unneurotic with the code adaptable to prevention retention abstraction for the contract.

Now that we tin telephone the different functions successful the contract, let’s look astatine what we tin bash to ace this contract. The important methods you should ore connected are these:

function retract() contacted nationalist {
codex.length--;
} relation revise(uint i, bytes32 _content) contacted nationalist {
codex[i] = _content;
}

The “retract” method allows america to shrink the dynamic array “codex” by 1, and “revise” allows america indexed insertion into the array. Before we tin continue, we request to recognize however dynamic arrays behave successful the retention slots. Because dynamic arrays stores information that is of an indeterminate size, the EVM cannot conscionable battalion it sequentially unneurotic similar static variables. Instead the array information volition beryllium stored astatine a retention slot determined by hashing the retention slot scale with the keccak256 hashing function. In the console you tin bash it so:

web3.utils.soliditySha3(web3.utils.encodePacked(1))

We are utilizing slot 1 due to the fact that the code and the bool could some acceptable into slot 0, truthful the array got enactment into slot 1. Another happening to enactment astir dynamic array storage, is that the magnitude of our array volition beryllium stored successful this retention slot. So if you instrumentality a peek astatine slot 1 you volition spot that it volition instrumentality 0, but what if we changed the size of our array? Eg. we could telephone the “retract” function:

await contract.retract()

Now looking astatine the retention slot again, we spot thing interesting:

The worth changed to the maximum imaginable worth for 32 bytes. By subtracting 1 from 0 the worth underflows, and wraps each the mode astir to correspond the largest possible value.

Okay, that is bully to know, but however does this assistance america ace this contract?

By underflowing the array length, it means that from the EVM’s position the array is the maximum imaginable magnitude of 2²⁵⁶-1. If you retrieve from earlier, this fig is the aforesaid arsenic the full magnitude of retention slots successful a contract. This means that the indices of this array really wraps the entire contract!

If we harvester everything we person learned to far, we tin instrumentality vantage of our wrapped array, and insert thing we privation astatine immoderate storage slot!

There was a method called “revise” that allowed america to entree the array done an index, and insert a caller worth astatine that index. But our people is to insert our ain code astatine slot 0, truthful which scale should we acceptable to really insert astatine slot 0?

This is wherever we request to cognize wherever our array starts storing its data, which we derived utilizing this formula:

web3.utils.soliditySha3(web3.utils.encodePacked(1))

To cipher the close index, we request to subtract the array’s retention slot scale with the maximum imaginable worth and adhd 1, which should onshore the array scale precisely connected retention slot 0. In the console we tin cipher the value:

web3.utils.toBN(await web3.eth.getStorageAt(contract.address, 1)).sub(web3.utils.toBN(web3.utils.soliditySha3(web3.utils.encodePacked(1)))).add(web3.utils.toBN(1))

If we usage this worth arsenic our index, we tin insert thing we privation successful retention slot 0.

await contract.revise(web3.utils.encodePacked("35707666377435648211887908874984608119992236509074197713628505308453184860938"), web3.utils.padLeft("insert your wallet address", 64))

We person to pad the input to lucifer the 32 bytes required for the relation parameter, and we tin yet cheque if it worked by looking astatine retention slot 0:

If it worked you volition spot your ain address, which makes you the owner now!

Don’t hide to taxable to implicit the challenge.

New to trading? Try crypto trading bots oregon copy trading

Solving Ethernaut 19 — Alien Codex was primitively published successful Coinmonks connected Medium, wherever radical are continuing the speech by highlighting and responding to this story.

Read Entire Article