前言
- 复现
balsn2020 ctf
中的Election
区块链题目,当时没做出来😭 wtcl
,都2021
了,还在看2020
的题目- 这篇会写的很详细,大佬勿喷🙈
- 具体分析及官方
WP
如下:https://x9453.github.io/2021/02/27/Balsn-CTF-2020-Election/ - 源码 https://github.com/hitcxy/blockchain-challenges/tree/master/2020/balsn/Election
- 复现地址为:
ropsten@0xef31471E3004a78Ae403858BbcB27D6d1f37791C
- 关于
ABI
及ABI Encoding
相关知识可以查看我的另一片博客 👉 Function Selector and Argument Encoding 👈
Source
1 | pragma solidity =0.6.12; |
Analyse
- 考点是
ERC223
的customFallback
错误引入、ABI encoding
、Integer overflow
,关于ERC223
的官方实现可点击这里 👉 officially recommended implementation 👈 - 目标成功调用
giveMeFlag()
即可 题目大概是实现了一个投票系统,我们只需要让我们的票数最多即可,包括三个阶段:
propose
、vote
、reveal
propose
: 通过auth
验证便可添加新的candidates
vote
: 投票人提交ballot
投票的hash
值,投票的数量是任意的,这里没有限制reveal
: 计算所有candidates
的票数情况
第
56
行的这种调用可以通过auth
验证,如果控制合理,可以调用自身的其他函数,因为customFallback
可控,并且参数也都可控,只要我们控制的参数符合ABI encoding format
,便可调用任何函数,关于ABI encoding
可参考 官方文档,关于带有auth
限制的主要有propose
和_setStage
函数:- 当调用
customFallback
时,三个参数编码如下图左边所示 - 当调用
propose
时,编码如下图中间所示 - 当调用
_setStage
时,编码如下图右边所示 - 下图取自官方 WP ,如果有疑惑,可查看我的另一片博客 👉 Function Selector and Argument Encoding 👈
- 当调用
知道了不同函数调用参数编码,便可根据参数编码后的相应位置进行分析
- 通过
customFallback
调用propose
,需要满足下面条件:value = 0x40
(需要构造,要求attacker
拥有0x40
的token
) ,offset_to_data = 0x60
(已满足),length_of_data = 0xa0
(需要构造,要求data
长度是0xa0
,同时为了通过172
行和173
行的检查,我们需要对data
内容进行合理的构造) - 通过
customFallback
调用_setStage
,需要满足下面条件:stage
可以是0
、1
、2
、3
,所以相应只需msg.sender
以00
、01
、02
、03
结尾即可,多余的value
参数和data
参数不影响
- 通过
第
145
行存在一个integer overflow
,所以我们可以两个ballot
投票,票数分别为2^256-1
(投给attacker
) 和1
(投给Alice
或Bob
任意一个),这样利用145
行的integer overflow
便可通过148
行的限制,同时使attacker
票数为2^256-1
,可通过getWinner()
赢得选票
Solution
- 本题中的一些账户,下面为了简洁,就使用简称,下面是对应关系
- 0xef31471E3004a78Ae403858BbcB27D6d1f37791C
Election
- 0x52cC2403764380CCa583633d2523999FE5077113 attacker账户
- 0xe7F2D75e69a989fb834e302f7caf8e1A9CC34000 00账户
- 0x40F6dBA38C634C86Ae11D1a775FA2215b7669702 02账户
- 0x6CDFf92000246705EFc00B625E2e3d6b0C9e7603 03账户
- 0x0000000000000000000000000000000000009453
Alice
- 0x0000000000000000000000000000000000009487
Bob
- 0xef31471E3004a78Ae403858BbcB27D6d1f37791C
- 通过下列脚本,令
times = 64
,使_balances[attacker] = 0x40
1 | contract son { |
- 准备两个
ballot
,一个投给attacker
,票数为2^256-1
;一个投给Alice
,票数为1
- 先进行
ballotEncode
计算,调用形式如下
1 | ballotEncode([["0x52cC2403764380CCa583633d2523999FE5077113","0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"], ["0x0000000000000000000000000000000000009453","0x1"]]) |
- 结果如下图所示
- 调用
vote(0x741d83d533784642740e64dbea7fa658c082a96b14623cdb1866e58c252f7f23)
,然后查看voteHashes[0]
成功显示 (使用 attacker账户 )
- 切换到
stage 0
(使用 00账户 ),data
字段随意
- 将
attacker
增加到candidate
(使用 attacker账户 )
- 其中
data
字段拆分如下
- 查看
proposals
可以看到添加attacker
为candidate
成功
- 切换到
stage 2
(使用 02账户 ),data
字段随意
进行
reveal
调用,其中ballots
是step 2
中的ballots
(使用 attacker账户 ),调用完后查看voteCount[attacker]
已经是2^256-1
切换到
stage 3
(使用 03账户 ),data
字段随意
- 调用
giveMeFlag()
即可 (使用 attacker账户 ),撒花完结 🎉🎉🎉🎉🎉🎉