http://www.7klian.com

CTF技术宝典之智能合约#重入裂痕

连年来,各个大型CTF(Capture The Flag,中文一般译作夺旗赛,在网络安详规模中指的是网络安详技能人员之间举办技能竞技的一种角逐形式)角逐中都有了区块链攻防的身影,并且呈现的题目绝大大都都是区块链智能合约攻防。此系列文章我们主要以智能合约攻防为中心,来分解智能合约攻防的要点,前两篇我们分享了合约反编译,反汇编的基本内容。后续的文章中,我们会继承分享CTF角逐中智能合约常见题型(重入,整数溢出,空投,随机数可控等)及解题思路,相信会给读者带来纷歧样的收获。

本篇我们先来分享CTF角逐中的重入题型,也是较量常见的一类题型,虽然大都CTF智能合约题目并不只仅考查单个裂痕的攻防,合约中的判定条件有时也很是棘手。好比2018年WCTF上BelluminarBank题目,需要用到整数绕过条件限制,还需用到存储溢出,会见权限配置等多个进攻能力。

本篇分享的重入题型我们选择2019强网杯babybank题目。

题目地点:https://ropsten.etherscan.io/address/0x93466d15A8706264Aa70edBCb69B7e13394D049f#code

题目阐明

题目提示:

function?payforflag(string?md5ofteamtoken,string?b64email)?public{ ????????require(balance[msg.sender]?>=?10000000000); ????????balance[msg.sender]=0; ????????owner.transfer(address(this).balance); ????????emit?sendflag(md5ofteamtoken,b64email); ????}

合约源码:

查察合约题目,发明并没有ether,也没有给出合约源码,如下图:

CTF技能宝典之智能合约#重入裂缝

由于拿到题目后只有合约的opcode,所以需要举办逆向,这里我们推荐Online Solidity Decompiler在线网站(https://ethervm.io/decompile),详细逆向时的源码还原我们不再赘述,需要进修的同学可移步系列文章反编译篇,反汇编篇

以下为逆向后的合约代码:

pragma?solidity?^0.4.23;

contract?babybank?{ ????mapping(address?=>?uint)?public?balance; ????mapping(address?=>?uint)?public?level; ????address?owner; ????uint?secret; ????event?sendflag(string?md5ofteamtoken,string?b64email);

constructor()public{ ????????owner?=?msg.sender; ????}

function?payforflag(string?md5ofteamtoken,string?b64email)?public{ ????????require(balance[msg.sender]?>=?10000000000); ????????balance[msg.sender]=0; ????????owner.transfer(address(this).balance); ????????emit?sendflag(md5ofteamtoken,b64email); ????}

modifier?onlyOwner(){ ????????require(msg.sender?==?owner); ????????_; ????}

function?withdraw(uint256?amount)?public?{ ????????require(amount?==?2); ????????require(amount?<=?balance[msg.sender]); ????????address(msg.sender).call.value(amount?*?0x5af3107a4000)();??//重入裂痕点 ????????balance[msg.sender]?-=?amount; ????}

function?profit()?public?{ ????????require(level[msg.sender]?==?0); ????????balance[msg.sender]?+=?1; ????????level[msg.sender]?+=?1; ????}

function?xxx(uint256?number)?public?onlyOwner?{ ????????secret?=?number; ????}

function?guess(uint256?number)?public?{ ????????require(number?==?secret); ????????require(level[msg.sender]?==?1); ????????balance[msg.sender]?+=?1; ????????level[msg.sender]?+=?1; ????}

function?transfer(address?to,?uint256?amount)?public?{ ????????require(balance[msg.sender]?>=?amount); ????????require(amount?==?2); ????????require(level[msg.sender]?==?2); ????????balance[msg.sender]?=?0; ????????balance[to]?=?amount; ????} }

合约阐明:

我们先来看题目提示:

function?payforflag(string?md5ofteamtoken,string?b64email)?public{ ????require(balance[msg.sender]?>=?10000000000);????//挪用者余额需大于便是10000000000 ????balance[msg.sender]=0; ????owner.transfer(address(this).balance); ????emit?sendflag(md5ofteamtoken,b64email); }

从该段代码的payforflag函数可以看出,该函数传入两个参数(md5ofteamtoken,b64email),函数中第一行代码require(balance[msg.sender] >= 10000000000);会判定挪用者地点余额是否大于便是10000000000,假如满意该条件,则继承执行之儿女码,不然遏制执行该函数并回滚状态;第二行和第三行对换用者地点举办了赋值和转账;最后一行emit sendflag(md5ofteamtoken,b64email);意义是通过event事件输出该函数传入的两个参数。

也就是说只要通过该事件输出这两个参数,就意味着拿到了flag,那么如何让挪用者地点余额到达10000000000就是我们接下来需要做的事情。

通过阐明合约,我们发此刻withdraw函数中,存在一个经典的重入裂痕。

function?withdraw(uint256?amount)?public?{ ????require(amount?==?2);????????? ????require(amount?<=?balance[msg.sender]);???? ????address(msg.sender).call.value(amount?*?0x5af3107a4000)();?//?重入裂痕点 ????balance[msg.sender]?-=?amount; }

该withdraw函数中,第一行代码require(amount == 2);限制该函数传入的amount值为2,不然遏制执行该函数并回滚状态;第二行代码require(amount <= balance[msg.sender]); 会判定挪用者地点是否大于便是2,假如满意该条件,则继承执行之儿女码,,不然遏制执行该函数并回滚状态;第三行代码寄义是举办转账,由于这里利用call.value()的转账要领,所以存在重入裂痕;之后操作第四行减掉已经转出的数值,由于这里balance[msg.sender]值已经大于便是2,故不存在整数下溢出。

这里的重入裂痕点为:

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。