区块链平安—庞氏代币破绽剖析 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

区块链平安—庞氏代币破绽剖析

申博_安全预警 申博 136次浏览 已收录 0个评论

一、媒介

在剖析过浩瀚的基本破绽后,我将破绽的应用投向实在合约状况中。而近来“币圈”的点点滴滴吸引着我,以是我阅读了一些代币合约并剖析与代币体系相干的合约破绽。

本文针对一种布置在以太坊上的博彩合约举行剖析,包孕代码剖析和破绽测试,末了针对实在以太坊状况举行剖析。

二、庞氏代币引见

关于方才打仗区块链的同砚来讲,应当会经常听到人们议论“币圈”。而“币圈”也是推进区块链成为众所周知产物的一个主要的要素。而在五花八门的区块链金融产物中,我们或多或少会看到“庞氏”圈套的影子。

所谓“庞氏”圈套就是:他们向投资者许诺,若是你向某合约投资一笔以太坊,它就会以一个高回报率回赠你更多的以太币,然则高回报只能从后续的投资者那边络绎不绝地吸收资金以反馈给前面的投资者。

比方某些基于EOS开辟的博彩类产物,所接纳的的投注体式格局就是“玩家运用EOS代币与项目方刊行的代币MEV举行替代”,然后应用代币再举行博彩。除在智能合约中“埋雷”,刊行代币“血洗”玩家,开辟者实在另有个“杀手锏式”的要领让介入者的钱乖乖进入他的口袋,那就是开辟“庞氏圈套”智能合约。罕见的是一种“庞氏圈套合约”,该合约是如许设定的:若是你向某合约投资一笔以太币,它就会以高回报率回赠更多的以太币,高回报的背地是从后续投资者那边络绎不绝地吸收资金以反馈给前面的投资者。

关于区块链来讲,它的底层机制能够或许下降信托风险。区块链手艺具有开源、通明的特征,体系的介入者能够或许晓得体系的运转划定规矩,考证帐本内容和帐本组织汗青的实在性和完整性,确保生意业务汗青是牢靠的、没有被改动的,相称于提高了体系的可追责性,下降了体系的信托风险。 以是这也就是为何有云云多的用户挑选将资金投入到区块链平台中。

三、合约剖析

1 合约引见

在本文中,我们来引见ETHX代币合约。ETHX是一个典范的庞氏代币合约。该合约能够算作假造币生意业务所,但只要ETH和ETHX (ERC20 token)生意业务对,每次生意业务,都有局部的token分配给全部平台的已有的token持有者,因而token持有者在持币时期,将会直接赚取新购置者和旧兜售者的手续费。以是这也赓续鼓励着用户将本身以太币投入到合约中,以便本身占领更多的股分来猎取到更多的利润。

与我上次剖析的Fomo3D合约相似,此合约当你投入了一定以太币后,就须要等相称长的时候才能够或许把本金赚足。而在这个过程当中又赓续有新用户介入到合约中,而络绎不绝的本身末了的获益者也就是合约创建者自己了。

下面我们就针对这个合约举行细致的剖析,来看看合约创建者是以何思绪来设想这个合约的。

2 代码剖析

以太坊合约概况以下:https://etherscan.io/address/0x1c98eea5fe5e15d77feeabc0dfcfad32314fd481

代码以下:

pragma solidity ^0.4.19;

// If you wanna escape this contract REALLY FAST
// 1. open MEW/METAMASK
// 2. Put this as data: 0xb1e35242
// 3. send 150000+ gas
// That calls the getMeOutOfHere() method

// Wacky version, 0-1 tokens takes 10eth (should be avg 200% gains), 1-2 takes another 30eth (avg 100% gains), and beyond that who the fuck knows but it's 50% gains
// 10% fees, price goes up crazy fast ETHCONNNNNNNNNNNECT! www.ethconnectx.online
contract EthConnectPonzi {
    uint256 constant PRECISION = 0x10000000000000000;  // 2^64
    // CRR = 80 %
    int constant CRRN = 1;
    int constant CRRD = 2;
    // The price coefficient. Chosen such that at 1 token total supply
    // the reserve is 0.8 ether and price 1 ether/token.
    int constant LOGC = -0x296ABF784A358468C;

    string constant public name = "ETHCONNECTx";
    string constant public symbol = "ETHX";
    uint8 constant public decimals = 18;
    uint256 public totalSupply;
    // amount of shares for each address (scaled number)
    mapping(address => uint256) public balanceOfOld;
    // allowance map, see erc20
    mapping(address => mapping(address => uint256)) public allowance;
    // amount payed out for each address (scaled number)
    mapping(address => int256) payouts;
    // sum of all payouts (scaled number)
    int256 totalPayouts;
    // amount earned for each share (scaled number)
    uint256 earningsPerShare;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    //address owner;

    function EthConnectPonzi() public {
        //owner = msg.sender;
    }

    // These are functions solely created to appease the frontend
    function balanceOf(address _owner) public constant returns (uint256 balance) {
        return balanceOfOld[_owner];
    }

    function withdraw(uint tokenCount) // the parameter is ignored, yes
      public
      returns (bool)
    {
        var balance = dividends(msg.sender);
        payouts[msg.sender] += (int256) (balance * PRECISION);
        totalPayouts += (int256) (balance * PRECISION);
        msg.sender.transfer(balance);
        return true;
    }

    function sellMyTokensDaddy() public {
        var balance = balanceOf(msg.sender);
        transferTokens(msg.sender, address(this),  balance); // this triggers the internal sell function
    }

    function getMeOutOfHere() public {
        sellMyTokensDaddy();
        withdraw(1); // parameter is ignored
    }

    function fund()
      public
      payable 
      returns (bool)
    {
      if (msg.value > 0.000001 ether)
            buy();
        else
            return false;

      return true;
    }

    function buyPrice() public constant returns (uint) {
        return getTokensForEther(1 finney);
    }

    function sellPrice() public constant returns (uint) {
        return getEtherForTokens(1 finney);
    }

    // End of useless functions

    // Invariants
    // totalPayout/Supply correct:
    //   totalPayouts = \sum_{addr:address} payouts(addr)
    //   totalSupply  = \sum_{addr:address} balanceOfOld(addr)
    // dividends not negative:
    //   \forall addr:address. payouts[addr] <= earningsPerShare * balanceOfOld[addr]
    // supply/reserve correlation:
    //   totalSupply ~= exp(LOGC + CRRN/CRRD*log(reserve())
    //   i.e. totalSupply = C * reserve()**CRR
    // reserve equals balance minus payouts
    //   reserve() = this.balance - \sum_{addr:address} dividends(addr)

    function transferTokens(address _from, address _to, uint256 _value) internal {
        if (balanceOfOld[_from] < _value)
            revert();
        if (_to == address(this)) {
            sell(_value);
        } else {
            int256 payoutDiff = (int256) (earningsPerShare * _value);
            balanceOfOld[_from] -= _value;
            balanceOfOld[_to] += _value;
            payouts[_from] -= payoutDiff;
            payouts[_to] += payoutDiff;
        }
        Transfer(_from, _to, _value);
    }

    function transfer(address _to, uint256 _value) public {
        transferTokens(msg.sender, _to,  _value);
    }

    function transferFrom(address _from, address _to, uint256 _value) public {
        var _allowance = allowance[_from][msg.sender];
        if (_allowance < _value)
            revert();
        allowance[_from][msg.sender] = _allowance - _value;
        transferTokens(_from, _to, _value);
    }

    function approve(address _spender, uint256 _value) public {
        // To change the approve amount you first have to reduce the addresses`
        //  allowance to zero by calling `approve(_spender, 0)` if it is not
        //  already 0 to mitigate the race condition described here:
        //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        if ((_value != 0) && (allowance[msg.sender][_spender] != 0)) revert();
        allowance[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
    }

    function dividends(address _owner) public constant returns (uint256 amount) {
        return (uint256) ((int256)(earningsPerShare * balanceOfOld[_owner]) - payouts[_owner]) / PRECISION;
    }

    function withdrawOld(address to) public {
        var balance = dividends(msg.sender);
        payouts[msg.sender] += (int256) (balance * PRECISION);
        totalPayouts += (int256) (balance * PRECISION);
        to.transfer(balance);
    }

    function balance() internal constant returns (uint256 amount) {
        return this.balance - msg.value;
    }
    function reserve() public constant returns (uint256 amount) {
        return balance()
            - ((uint256) ((int256) (earningsPerShare * totalSupply) - totalPayouts) / PRECISION) - 1;
    }

    function buy() internal {
        if (msg.value < 0.000001 ether || msg.value > 1000000 ether)
            revert();
        var sender = msg.sender;
        // 5 % of the amount is used to pay holders.
        var fee = (uint)(msg.value / 10);

        // compute number of bought tokens
        var numEther = msg.value - fee;
        var numTokens = getTokensForEther(numEther);

        var buyerfee = fee * PRECISION;
        if (totalSupply > 0) {
            // compute how the fee distributed to previous holders and buyer.
            // The buyer already gets a part of the fee as if he would buy each token separately.
            var holderreward =
                (PRECISION - (reserve() + numEther) * numTokens * PRECISION / (totalSupply + numTokens) / numEther)
                * (uint)(CRRD) / (uint)(CRRD-CRRN);
            var holderfee = fee * holderreward;
            buyerfee -= holderfee;

            // Fee is distributed to all existing tokens before buying
            var feePerShare = holderfee / totalSupply;
            earningsPerShare += feePerShare;
        }
        // add numTokens to total supply
        totalSupply += numTokens;
        // add numTokens to balance
        balanceOfOld[sender] += numTokens;
        // fix payouts so that sender doesn't get old earnings for the new tokens.
        // also add its buyerfee
        var payoutDiff = (int256) ((earningsPerShare * numTokens) - buyerfee);
        payouts[sender] += payoutDiff;
        totalPayouts += payoutDiff;
    }

    function sell(uint256 amount) internal {
        var numEthers = getEtherForTokens(amount);
        // remove tokens
        totalSupply -= amount;
        balanceOfOld[msg.sender] -= amount;

        // fix payouts and put the ethers in payout
        var payoutDiff = (int256) (earningsPerShare * amount + (numEthers * PRECISION));
        payouts[msg.sender] -= payoutDiff;
        totalPayouts -= payoutDiff;
    }

    function getTokensForEther(uint256 ethervalue) public constant returns (uint256 tokens) {
        return fixedExp(fixedLog(reserve() + ethervalue)*CRRN/CRRD + LOGC) - totalSupply;
    }

    function getEtherForTokens(uint256 tokens) public constant returns (uint256 ethervalue) {
        if (tokens == totalSupply)
            return reserve();
        return reserve() - fixedExp((fixedLog(totalSupply - tokens) - LOGC) * CRRD/CRRN);
    }

    int256 constant one       = 0x10000000000000000;
    uint256 constant sqrt2    = 0x16a09e667f3bcc908;
    uint256 constant sqrtdot5 = 0x0b504f333f9de6484;
    int256 constant ln2       = 0x0b17217f7d1cf79ac;
    int256 constant ln2_64dot5= 0x2cb53f09f05cc627c8;
    int256 constant c1        = 0x1ffffffffff9dac9b;
    int256 constant c3        = 0x0aaaaaaac16877908;
    int256 constant c5        = 0x0666664e5e9fa0c99;
    int256 constant c7        = 0x049254026a7630acf;
    int256 constant c9        = 0x038bd75ed37753d68;
    int256 constant c11       = 0x03284a0c14610924f;

    function fixedLog(uint256 a) internal pure returns (int256 log) {
        int32 scale = 0;
        while (a > sqrt2) {
            a /= 2;
            scale++;
        }
        while (a <= sqrtdot5) {
            a *= 2;
            scale--;
        }
        int256 s = (((int256)(a) - one) * one) / ((int256)(a) + one);
        // The polynomial R = c1*x + c3*x^3 + ... + c11 * x^11
        // approximates the function log(1+x)-log(1-x)
        // Hence R(s) = log((1+s)/(1-s)) = log(a)
        var z = (s*s) / one;
        return scale * ln2 +
            (s*(c1 + (z*(c3 + (z*(c5 + (z*(c7 + (z*(c9 + (z*c11/one))
                /one))/one))/one))/one))/one);
    }

    int256 constant c2 =  0x02aaaaaaaaa015db0;
    int256 constant c4 = -0x000b60b60808399d1;
    int256 constant c6 =  0x0000455956bccdd06;
    int256 constant c8 = -0x000001b893ad04b3a;
    function fixedExp(int256 a) internal pure returns (uint256 exp) {
        int256 scale = (a + (ln2_64dot5)) / ln2 - 64;
        a -= scale*ln2;
        // The polynomial R = 2 + c2*x^2 + c4*x^4 + ...
        // approximates the function x*(exp(x)+1)/(exp(x)-1)
        // Hence exp(x) = (R(x)+x)/(R(x)-x)
        int256 z = (a*a) / one;
        int256 R = ((int256)(2) * one) +
            (z*(c2 + (z*(c4 + (z*(c6 + (z*c8/one))/one))/one))/one);
        exp = (uint256) (((R + a) * one) / (R - a));
        if (scale >= 0)
            exp <<= scale;
        else
            exp >>= -scale;
        return exp;
    }

    /*function destroy() external {
        selfdestruct(owner);
    }*/

    function () payable public {
        if (msg.value > 0)
            buy();
        else
            withdrawOld(msg.sender);
    }
}

下面我们针对这个合约举行症结函数的剖析。

关于代币合约,我们将从代币购置、股分嘉奖、利钱提取等举行顺次剖析。

起首看合约变量:

uint256 public totalSupply;
    // amount of shares for each address (scaled number)
    mapping(address => uint256) public balanceOfOld;
    // allowance map, see erc20
    mapping(address => mapping(address => uint256)) public allowance;
    // amount payed out for each address (scaled number)
    mapping(address => int256) payouts;
    // sum of all payouts (scaled number)
    int256 totalPayouts;
    // amount earned for each share (scaled number)
    uint256 earningsPerShare;

这些变量以此代表:合约中代币总和、用户的代币余额、补助数目(ERC20变量)、用户分红金额、总分红金额、每股取得的收益。

下面我们来看用户怎样购置代币:

function fund()
      public
      payable 
      returns (bool)
    {
      if (msg.value > 0.000001 ether)
            buy();
        else
            return false;

      return true;
    }

起首用户能够挪用fund()函数。在函数中请求用户传入msg.value > 0.000001,以后进入buy()函数。

function buy() internal {
        if (msg.value < 0.000001 ether || msg.value > 1000000 ether)
            revert();
        var sender = msg.sender;
        // 5 % of the amount is used to pay holders.
        var fee = (uint)(msg.value / 10);

        // compute number of bought tokens
        var numEther = msg.value - fee;

        // 盘算用户购置金额对应的股分
        var numTokens = getTokensForEther(numEther);

        var buyerfee = fee * PRECISION;
        if (totalSupply > 0) {
            // compute how the fee distributed to previous holders and buyer.
            // 盘算持有股分的人取得的金额
            // The buyer already gets a part of the fee as if he would buy each token separately.
            var holderreward =
                (PRECISION - (reserve() + numEther) * numTokens * PRECISION / (totalSupply + numTokens) / numEther)
                * (uint)(CRRD) / (uint)(CRRD-CRRN);
            var holderfee = fee * holderreward;
            buyerfee -= holderfee;

            // Fee is distributed to all existing tokens before buying
            var feePerShare = holderfee / totalSupply;
            earningsPerShare += feePerShare;
        }
        // add numTokens to total supply
        totalSupply += numTokens;
        // add numTokens to balance
        balanceOfOld[sender] += numTokens;
        // fix payouts so that sender doesn't get old earnings for the new tokens.
        // also add its buyerfee
        var payoutDiff = (int256) ((earningsPerShare * numTokens) - buyerfee);
        payouts[sender] += payoutDiff;
        totalPayouts += payoutDiff;
    }

在这个函数中,起首请求用户传入的金额有一定的局限。msg.value < 0.000001 ether || msg.value > 1000000 ether

以后合约提取了用户的百分之十的金额来作为手续费(这里的解释中写的是5%,然则这里的代码是 /10,而其他处所一样没有削减这个值,以是我以为应当照样百分之十)。

var fee = (uint)(msg.value / 10);

以后合约将剩下的钱传入getTokensForEther()举行处置惩罚(这里是合约自行设想的转换函数,将流动数目的以太币转换为相对应的股分),获得numTokens

var numEther = msg.value - fee;

        // 盘算用户购置金额对应的股分
        var numTokens = getTokensForEther(numEther);

if (totalSupply > 0)。若是这不是第一个用户介入合约,那末进入下面的处置惩罚。(若是是第一个用户,那末totalSupply += numTokens;直接在总代币上举行相加。)

if (totalSupply > 0) {
            // compute how the fee distributed to previous holders and buyer.
            // 盘算持有股分的人取得的金额
            // The buyer already gets a part of the fee as if he would buy each token separately.
            var holderreward =
                (PRECISION - (reserve() + numEther) * numTokens * PRECISION / (totalSupply + numTokens) / numEther)
                * (uint)(CRRD) / (uint)(CRRD-CRRN);
            var holderfee = fee * holderreward;
            buyerfee -= holderfee;

            // Fee is distributed to all existing tokens before buying
            var feePerShare = holderfee / totalSupply;
            earningsPerShare += feePerShare;
        }

上述函数中主如果针对earningsPerShare举行更新。每次有新用户到场合约并传入一定以太币均会挪用此函数,以后var feePerShare = holderfee / totalSupply; earningsPerShare += feePerShare;

而每次earningsPerShare变量更新后,老用户均能够提拔本身的利钱。

下面我们来看提取分红的函数。

function withdraw(uint tokenCount) // the parameter is ignored, yes
      public
      returns (bool)
    {
        var balance = dividends(msg.sender);
        payouts[msg.sender] += (int256) (balance * PRECISION);
        totalPayouts += (int256) (balance * PRECISION);
        msg.sender.transfer(balance);
        return true;
    }

函数中的balance赋值为dividends(msg.sender)。而这是分红函数:

function dividends(address _owner) public constant returns (uint256 amount) {
        return (uint256) ((int256)( earningsPerShare * balanceOfOld[_owner]) - payouts[_owner]) / PRECISION;
    }

个中详细的值不消我们穷究,其分工的详细的金额与股分的单价、用户的余额和已取得分红有关。

我们继承回到withdraw()函数。在盘算完成分红金额后,合约将分红转给用户msg.sender.transfer(balance)

而这里的transfer()函数一样是封装好的函数:

function transfer(address _to, uint256 _value) public {
        transferTokens(msg.sender, _to,  _value);
    }
function transferTokens(address _from, address _to, uint256 _value) internal {
        if (balanceOfOld[_from] < _value)
            revert();
        if (_to == address(this)) {
            sell(_value);
        } else {
            int256 payoutDiff = (int256) (earningsPerShare * _value);
            balanceOfOld[_from] -= _value;
            balanceOfOld[_to] += _value;
            payouts[_from] -= payoutDiff;
            payouts[_to] += payoutDiff;
        }
        Transfer(_from, _to, _value);
    }

这里起首推断用户的余额是不是充足,以后推断_to是不是是合约地点,若是不是则申明是转账给其他用户。这时候对转账两边均举行余额变量处置惩罚。

倘使_to是合约地点:则挪用sell(_value)函数。

function sell(uint256 amount) internal {
        var numEthers = getEtherForTokens(amount);
        // remove tokens
        totalSupply -= amount;
        balanceOfOld[msg.sender] -= amount;

        // fix payouts and put the ethers in payout
        var payoutDiff = (int256) (earningsPerShare * amount + (numEthers * PRECISION));
        payouts[msg.sender] -= payoutDiff;
        totalPayouts -= payoutDiff;
    }

函数最先盘算amount对应的股分numEthers。以后由于这时候用户举行的转账,以是转账方的余额纪录应当被修正。这里挪用了:

totalSupply -= amount;
balanceOfOld[msg.sender] -= amount;

然则我们实在能够发明这里的题目,当我们顺着函数思绪抵达这里后,我们应当能发明这里削减的是msg.sender的余额,然则我们此次的转账者仅仅只是msg.sender吗?这里我们要打一个问号。详细的状况我们鄙人一章详细来看。

掏出分红除上述函数外,另有下面这个函数。

function withdrawOld(address to) public {
        var balance = dividends(msg.sender);
        payouts[msg.sender] += (int256) (balance * PRECISION);
        totalPayouts += (int256) (balance * PRECISION);
        to.transfer(balance);
    }

这个函数能够或许传入参数 to,并资助其他用户猎取分红。

由于合约属于ERC20的延长合约,以是一样内里具有ERC20的影子。

function approve(address _spender, uint256 _value) public {
        // To change the approve amount you first have to reduce the addresses`
        //  allowance to zero by calling `approve(_spender, 0)` if it is not
        //  already 0 to mitigate the race condition described here:
        //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        if ((_value != 0) && (allowance[msg.sender][_spender] != 0)) revert();
        allowance[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
    }

这个函数能够或许受权其他用户资助本身举行股权让渡。个中对照典范的变量是:allowance

————————————-

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

以后用户能够挪用一下函数举行股权让渡。

function transferFrom(address _from, address _to, uint256 _value) public {
        var _allowance = allowance[_from][msg.sender];
        if (_allowance < _value)
            revert();
        allowance[_from][msg.sender] = _allowance - _value;
        transferTokens(_from, _to, _value);
    }

上述内容就是这个庞氏代币的症结流程。而在我们剖析以后,我发明我们在转入响应的代币后,独一赢利的要领就是赓续的从分红中取得利润,而这个分红的金额是非常有限的,以是想要经由过程这个要领回本那就须要赓续发展新的用户进入合约。

四、破绽应用

1 破绽剖析

而报告了这么多合约代码,下面我们要针对合约举行破绽剖析。

我们适才对合约破绽轻微有点引见,下面细致的剖析下。

我们晓得题目出在这里:

function transferTokens(address _from, address _to, uint256 _value) internal {
        if (balanceOfOld[_from] < _value)
            revert();
        if (_to == address(this)) {
            sell(_value);
        } else {
            int256 payoutDiff = (int256) (earningsPerShare * _value);
            balanceOfOld[_from] -= _value;
            balanceOfOld[_to] += _value;
            payouts[_from] -= payoutDiff;
            payouts[_to] += payoutDiff;
        }
        Transfer(_from, _to, _value);
    }

我们传入_from_to,当_to==address(this)时,进入了sell(_value)

而在这个函数中:

function sell(uint256 amount) internal {
        var numEthers = getEtherForTokens(amount);
        // remove tokens
        totalSupply -= amount;
        balanceOfOld[msg.sender] -= amount;

        // fix payouts and put the ethers in payout
        var payoutDiff = (int256) (earningsPerShare * amount + (numEthers * PRECISION));
        payouts[msg.sender] -= payoutDiff;
        totalPayouts -= payoutDiff;
    }

依照一般的头脑来斟酌,合约应当对_from举行余额的调解,然则这里倒是:balanceOfOld[msg.sender] -= amount(对msg.sender)。

假定我们这里具有A B 两个用户,A受权B举行转账1 个股分,然则B将A的股分转还给了合约地点,那末我们进入了sell()函数。此时然则B此时躺枪,明显是A将股分转给了合约,然则你为啥转我B的余额(由于B是msg.sender)???此时就致使了bug存在。

区块链平安—庞氏代币破绽剖析

2 破绽演示

下面我们演示破绽的应用。

我们在0xca35b7d915458ef540ade6068dfe2f44e8fa733c地点处布置合约。合约地点为:0xbbf289d846208c16edc8474705c748aff07732db

区块链平安—庞氏代币破绽剖析

以后A用户为:0x14723a09acff6d2a60dcdf7aa4aff308fddc160c
B用户为:0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db

A受权B 1股权的转账才能。

区块链平安—庞氏代币破绽剖析

以后我们领A到场合约的股东行列,向合约转账5 ether。

区块链平安—庞氏代币破绽剖析

区块链平安—庞氏代币破绽剖析

以后我们切换到B账户。

为了对照,我们先查找B的余额。为0,是一定的。

区块链平安—庞氏代币破绽剖析

以后挪用transferFrom()

区块链平安—庞氏代币破绽剖析

参数为:transferFrom(A的钱包地点,合约地点,1)。【”0x14723a09acff6d2a60dcdf7aa4aff308fddc160c”,”0xbbf289d846208c16edc8474705c748aff07732db”,1】

以后我再检察B的余额。

区块链平安—庞氏代币破绽剖析

发明果真溢出了emmmm。

详细的理论为:

B的余额正本为0,而挪用了sell后-1,以是溢出抵达最大值。从而涌现了严峻隐患。

而由于合约涌现了严峻破绽,以是这个博彩类合约也就没有人玩了。正如图中的合约余额,只剩下了1 wei

区块链平安—庞氏代币破绽剖析


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明区块链平安—庞氏代币破绽剖析
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址