2022 | 中国科学技术大学第九届信息安全大赛 | General
链上记忆大师
题目
听说你在区块链上部署的智能合约有过目不忘的能力。
main.py
| from web3 import Web3
from web3.middleware import geth_poa_middleware
import os
import json
import time
import shutil
challenge_id = int(input('The challenge you want to play (1 or 2 or 3): '))
assert challenge_id == 1 or challenge_id == 2 or challenge_id == 3
player_bytecode = bytes.fromhex(input('Player bytecode: '))
print('Launching geth...')
shutil.copytree('/data', '/dev/shm/geth')
os.system('geth --datadir /dev/shm/geth --nodiscover --mine --unlock 0x2022af4DCbb9dA7F41cBD3dD8CdB4134D4e6DDe6 --password password.txt --verbosity 0 --datadir.minfreedisk 0 &')
time.sleep(2)
w3 = Web3(Web3.IPCProvider('/dev/shm/geth/geth.ipc'))
w3.middleware_onion.inject(geth_poa_middleware, layer=0)
w3.eth.default_account = w3.eth.accounts[0]
w3.geth.personal.unlock_account(w3.eth.default_account, open('password.txt').read().strip())
print('Deploying challenge contract...')
bytecode, abi = json.load(open(f'contract{challenge_id}.json'))
Challenge = w3.eth.contract(abi=abi, bytecode=bytecode)
tx_hash = Challenge.constructor().transact()
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print('Challenge contract address:', tx_receipt.contractAddress)
challenge = w3.eth.contract(address=tx_receipt.contractAddress, abi=abi)
print('Deploying player contract...')
tx_hash = w3.eth.send_transaction({'to': None, 'data': player_bytecode})
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print('Player contract address:', tx_receipt.contractAddress)
for i in range(10):
print(f'Testing {i + 1}/10...')
if challenge_id == 2:
n = int.from_bytes(os.urandom(2), 'big')
else:
n = int.from_bytes(os.urandom(32), 'big')
print(f'n = {n}')
if challenge.functions.test(tx_receipt.contractAddress, n).call():
print('Test passed!')
else:
print('Test failed!')
exit(-1)
print(open(f'flag{challenge_id}').read())
|
compile.py
| from solcx import compile_source
import json
for i in 1, 2, 3:
compiled_sol = compile_source(open(f'challenge{i}.sol').read(), output_values=['abi', 'bin'])
contract_interface = compiled_sol['<stdin>:Challenge']
bytecode = contract_interface['bin']
abi = contract_interface['abi']
json.dump((bytecode, abi), open(f'contract{i}.json', 'w'))
|
genesis.json
| {
"config": {
"chainId": 2022,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"arrowGlacierBlock": 0,
"grayGlacierBlock": 0,
"clique": {
"period": 0,
"epoch": 30000
}
},
"alloc": {
"0x2022af4DCbb9dA7F41cBD3dD8CdB4134D4e6DDe6": {"balance": "0x56bc75e2d63100000"}
},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x1",
"gasLimit": "0x1c9c380",
"extraData": "0x00000000000000000000000000000000000000000000000000000000000000002022af4dcbb9da7f41cbd3dd8cdb4134d4e6dde60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000042",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00"
}
|
Dockerfile
| FROM ubuntu:22.04
RUN apt update && apt install -y software-properties-common && add-apt-repository -y ppa:ethereum/ethereum && apt update && apt install -y ethereum python3-pip
RUN python3 -m pip install web3 py-solc-x
RUN python3 -c "from solcx import install_solc; install_solc(version='latest')"
COPY genesis.json privatekey.txt password.txt main.py challenge1.sol challenge2.sol challenge3.sol compile.py /
RUN geth init --datadir data genesis.json
RUN geth --datadir data account import --password password.txt privatekey.txt
RUN python3 compile.py
CMD ["/usr/bin/python3", "-u", "/main.py"]
|
记忆练习
| pragma solidity =0.8.17;
interface MemoryMaster {
function memorize(uint256 n) external;
function recall() external view returns (uint256);
}
contract Challenge {
function test(MemoryMaster m, uint256 n) external returns (bool) {
m.memorize(n);
uint256 recalled = m.recall();
return recalled == n;
}
}
|
牛刀小试
| pragma solidity =0.8.17;
interface MemoryMaster {
function memorize(uint16 n) external;
function recall() external view returns (uint16);
}
contract Challenge {
function test(MemoryMaster m, uint16 n) external returns (bool) {
try this.memorize_revert(m, n) {
} catch (bytes memory) {
}
uint16 recalled = m.recall();
return recalled == n;
}
function memorize_revert(MemoryMaster m, uint16 n) external {
m.memorize(n);
revert();
}
}
|
终极挑战
| pragma solidity =0.8.17;
interface MemoryMaster {
function memorize(uint256 n) external view;
function recall() external view returns (uint256);
}
contract Challenge {
function test(MemoryMaster m, uint256 n) external returns (bool) {
m.memorize(n);
uint256 recalled = m.recall();
return recalled == n;
}
}
|
解题思路
需要编写包含函数 memorize
和函数 recall
的合约 MemoryMaster
,合约 Challenge
将首先调用函数 memorize
并传入参数 n
,随后调用函数 recall
并期望返回 n
。
记忆练习
本题没有对函数 memorize
和函数 recall
进行任何限制,因而可以直接借助状态变量。
| pragma solidity 0.8.17;
contract MemoryMaster {
uint256 n;
function memorize(uint256 _n) public {
n = _n;
}
function recall() public view returns (uint256) {
return n;
}
}
|
Flag
flag{Y0u_Ar3_n0w_f4M1l1ar_W1th_S0l1dity_st0rage_dd0d6977ef}
牛刀小试
Exploit
| pragma solidity 0.8.17;
contract MemoryMaster {
function memorize(uint16 n) public {
uint256 g = gasleft();
while (gasleft() > g - 720 * uint256(n)) gasleft();
}
function recall() public view returns (uint16) {
return uint16((50000000 - 30040 - gasleft() * 64 / 63) / 720);
}
}
|
Flag
flag{Gas_gAs_gaS_c4n_b3_us3d_aS_s1de_ChaNNel_5a01148fd5}
终极挑战
Flag
flag{EVM_1s_c0mPl1c4ted_bUt_Rea11y_FuN_T0_d1g_Deeper_9d3b7f6932}
参考资料
最后更新:
2022年12月19日 00:22:49