前言
利用刚过去的周末参加了 D^3CTF ,做了一道区块链的题目,在此记录一下:
- 以太坊
Kovan
测试链 - 合约地址动态生成,每个队伍合约地址分开(但是分发合约地址的账户是同一个账户,所以说还是可以查到其他人在链上的交易2333)
hash-reveal-commit
随机数- 出题人采用的是前后台交互的方法,所以题目没有复现,记录下解题的思想
- 题目要求在上面图里,官方exp入口
- 题目
exp
:https://github.com/hitcxy/blockchain-challenges/tree/master/2019/D3CTF/bet2loss_v2
- 合约代码主要函数为
placeBet
和settleBet
,placeBet
建立赌博,settleBet
开奖 出题人利用了
hash-reveal-commit
随机数的思想,随机数的实现放在了服务端- 用户首先在前台选好下注的方式
- 之后服务端生成随机数
reveal
、commit
、commitLastBlock
及对commit
和commitLastBlock
哈希后的签名signature
,signature
中包含r
、s
、v
,并返回commit
、commitLastBlock
、r
、s
、v
信息 - 回到前端,web3.js 配合返回的数据向 meta 发起交易,交易成功被打包之后向后台发起请求
settleBet
- 后端收到请求后对该
commit
做开奖
题目要求是
balanceOf(you) > 300000
( 题目描述多了一个0,可以看合约里面应该是300000
)
1 | function PayForFlag() external returns (bool success){ |
- 先来看下合约里的
placeBet
代码
1 | function placeBet(uint8 betnumber, uint8 modulo, uint40 wager, uint40 commitLastBlock, uint commit, bytes32 r, bytes32 s, uint8 v) external { |
- 这里有一个签名验证
1 | bytes32 signatureHash = keccak256(abi.encodePacked(commitLastBlock, commit)); |
- 根据上述合约里面的签名验证对应修改服务端后台签名验证方式,参考链接为 HCTF2018_bet2loss
1 | def random_num(start=2**20, end=2**30): |
- 这样的话,我们就能够模拟服务端生成随机数了,通过下面的
placeBet
交易便能够通过签名验证,然后通过settleBet
开奖,需要注意的是,开奖要使用croupier
对应的私钥签名交易 croupier
对应私钥可以在 HCTF2018_bet2loss 的settings.py
中找到private_key = b'o\x08\xd7A\x949\x90t#\x81\xe1"4FU:c\xb3\x8a:\xa8k\xee\xf1\xe9\xfc_\xcfa\xe6m\x12'
- 不过需要注意的是,这里的
blocknumber
是在交易上链之前获取的,所以这里并不是placeBet
最后上链的区号,这里我采用了爆破的方法,使用了五个用户账号,blocknumber分别等于w3.eth.blocknumber + 2(3、4、5、6)
的方式进行交易,如果哪个区号对应在链上开奖中奖的话,就用对应的用户账号继续开奖即可(因为每个账户可以开奖16次,这里中一次奖是100*1000=100000
,所以再用第一次获取的随机数开奖3次即可大于300000
)
1 | result,reveal,blocknumber = random() |
- 自己做题当中是第三个用户账户成功开奖,所以用第三个账户再重复三次开奖即可,这里的
reveal
是第一次placeBet
获取到的
1 | def settleBet3(): |