连年来,各个大型CTF(Capture The Flag,中文一般译作夺旗赛,在网络安详规模中指的是网络安详技能人员之间举办技能竞技的一种角逐形式)角逐中都有了区块链攻防的身影,并且呈现的题目绝大大都都是区块链智能合约攻防。此系列文章我们主要以智能合约攻防为中心,来分解智能合约攻防的要点,前两篇我们分享了合约反编译,反汇编的基本内容。后续的文章中,我们会继承分享CTF角逐中智能合约常见题型(重入,整数溢出,空投,随机数可控等)及解题思路,相信会给读者带来纷歧样的收获。
上篇文章中我们分享了CTF角逐中常考的重入裂痕题型,本篇继承来分享CTF角逐中的整数溢出题型,也是较量常见的一类题型,虽然大都CTF智能合约题目并不只仅考查单个裂痕的攻防,大概涉及多个裂痕的组合。
本篇我们以2018年WCTF上BelluminarBank题目为例,给各人分享智能合约整数溢出的题型。解出这道题不只需要整数溢出进攻,也需用到变量包围,权限配置等多个进攻能力。
题目地点:
由于WCTF智能合约角逐没有在以太坊测试网(ropsten)举办,没有在线的攻防场景,合约详细题目先容及合约源码已在GitHub给出:https://github.com/beched/ctf/tree/master/2018/wctf-belluminar
题目阐明
题目提示
团队需要对字节码举办反向工程,并利用以下进攻:
整数溢出绕过存款期限限制;
存储溢出以包围银行所有者;
存储会见权限以泄露私有属性;
陈设自杀条约以强制将eth发送到方针条约(以办理余额差别)
不必然需要意外的以太进攻,假如利用withdraw()和invest()挪用,则可以适当均衡。大概是由于导致错误办理方案的庞大错误所致:withdraw()函数不会变动balances数组。可是仍然需要事先操作整数溢出。
合约说明
Belluminar Bank很是小而出格。其事情方法如下:
任何人都可以投资任何金额,并应指定存款期限(在此之前存款将被锁定);
存款期限必需比先前客户的存款期限至少长1年;
每个存款分派一个账号;
帐户0包括31337 wei,由银行所有者(条约建设者)锁定多年;
存款期限满一年(假如您不提款),银行所有者可以充公您的存款。
方针是破解这家银行并清空其余额。假如乐成,该呆板人将向您发送生意业务数据中的符号。
合约源码
pragma?solidity?^0.4.23;
contract?BelluminarBank?{ ????struct?Investment?{ ????????uint256?amount; ????????uint256?deposit_term; ????????address?owner; ????} ????//全局变量 ????Investment[]?balances; ????uint256?head; ????address?private?owner; ????bytes16?private?secret;??//secret可读取
function?BelluminarBank(bytes16?_secret,?uint256?deposit_term)?public?{ ????????secret?=?_secret; ????????owner?=?msg.sender; ????????if(msg.value?>?0)?{ ????????????balances.push(Investment(msg.value,?deposit_term,?msg.sender)); ????????} ????}
function?bankBalance()?public?view?returns?(uint256)?{ ????????return?address(this).balance; ????}
//局部变量包围全局变量 ????function?invest(uint256?account,?uint256?deposit_term)?public?payable?{ ????????if?(account?>=?head?&&?account?<?balances.length)?{ ????????????Investment?storage?investment?=?balances[account]; ????????????investment.amount?+=?msg.value; ????????}?else?{ ????????????if(balances.length?>?0)?{ ????????????//存在整数溢出 ????????????????require(deposit_term?>=?balances[balances.length?-?1].deposit_term?+?1?years); ????????????} ????????????//局部变量 ????????????investment.amount?=?msg.value; ????????????investment.deposit_term?=?deposit_term; ????????????investment.owner?=?msg.sender; ????????????balances.push(investment); ????????} ????}
function?withdraw(uint256?account)?public?{ ????????require(now?>=?balances[account].deposit_term); ????????require(msg.sender?==?balances[account].owner); ???????? ????????msg.sender.transfer(balances[account].amount); ????}
function?confiscate(uint256?account,?bytes16?_secret)?public?{ ????????require(msg.sender?==?owner); ????????require(secret?==?_secret); ????????require(now?>=?balances[account].deposit_term?+?1?years); ???????? ????????uint256?total?=?0; ????????for?(uint256?i?=?head;?i?<=?account;?i++)?{ ????????????total?+=?balances[i].amount; ????????????delete?balances[i]; ????????} ????????head?=?account?+?1; ????????msg.sender.transfer(total); ????} }
合约阐明
从题目提示可以得出,本次进攻的目标是拿到合约中的所有余额。而且需要多个裂痕进攻手法。
先来阐明合约中的存在转账成果函数withdraw()和confiscate():
function?withdraw(uint256?account)?public?{ ????require(now?>=?balances[account].deposit_term); ????require(msg.sender?==?balances[account].owner); ????msg.sender.transfer(balances[account].amount); }
withdraw()函数中,会判定此刻的时间是否大于存款的期限,第二句判定挪用者地点是否是存款者地点,假如条件满意,就会转出当前合约挪用者的存款余额,可以得出该函数并不能转出合约所有余额。
继承来看第二个函数confiscate():
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。