

前言
- 复现
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、revealpropose: 通过auth验证便可添加新的candidatesvote: 投票人提交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账户 ),撒花完结 🎉🎉🎉🎉🎉🎉

