Teacher Xiao Zhen from Peking University's "Blockchain Technology and Application" series of course study notes [25] Ethereum-Smart Contract-5

Smart Contract-1

Smart Contract-2

Smart Contract-3

Smart Contract-4

Resolving reentrancy attacks

Online Auctions Version 2: Retrieval of bids by bidders themselves

//使用withdraw模式
//由投标者自己取回出价,返回是否成功
function withdraw( ) public returns (bool) {
    //拍卖已截止
    require(now > auctionEnd);
    //竞拍成功者需要把钱给受益人,不可取回出价,如果不是最高出价者
    require(msg.sender != highestBidder);
    //当前地址有钱可取
    require(bids[msg.sender] > 0);//账户余额是否为正

    uint amount = bids[msg.sender];//账户余额
    if(msg.sender.call.value(amount)()) {//把账户余额转给msg.sender
        bids[msg.sender] = 0;//把账户余额清成0
        return true;
    }
    return false;
}

//把最高出价给这个受益人,也是判断一下拍卖已经结束了
//最高出价的金额大于零,下面再把它转过去
event Pay2Beneficiary( address winner,uint amount);
//结束拍卖,把最高的出价发送给受益人

function pay2Beneficiary ()public returns(bool) {
    //拍卖已截止
    require(now > auctionEnd);
    //有钱可以支付
    require(bids[highestBidder] > 0);//最高出价的金额大于零
    
    uint amount = bids[highestBidder];
    bids[highestBidder] = 0;
    emit Pay2Beneficiary(highestBidder,bids[highestBidder]);
    if(!beneficiary.call.value(amount)()){
        bids[highestBidder] = amount;
        return false;
    }
    return true;
}

(1) Existing problems

        Reentrancy attack, what if a hacker writes a program like the following?

pragma solidity ^0.4.21;

import "./simpleAuctionv2.sol";

contract HackV2 {
    uint stack = 0;
    function hack_bid( address addr) payable public {
        simpleAuctionv2 sa = simpleAuctionv2(addr);
        sa.bid.value(msg.value)();
    }
    
    function hack_withdraw(address addr) public payable{
        SimpleAuctionv2(addr).withdraw();
    }

    function() public payable{
        stack += 2;
        //当前调用的剩余汽油,msg.gas还有6000个单位以上,调用栈的深度不超过500
        if (msg.sender.balance >= msg.value && msg.gas > 6000 && stack < 500{
            SimpleAuctionV2(msg.sender).withdraw();
        }
    }
}

        This hack_bid is the same as the previous hacker contract hack_bid contract. By calling the auction bid function to participate in the auction, hack_withdraw calls the withdraw function at the end of the auction to get the money back. These two seem to be no problem. The problem is that fallback function, he withdrew the money again.

        When hack_withdraw calls the withdraw function, the "if(msg.sender.call.value(amount)())" will be executed to transfer money to the hacker's contract. This msg.sender is the hacker's contract, and the original bid amount will be transferred to him , and in this contract, the withdraw function "SimpleAuctionV2(msg.sender).withdraw();" of the auction function is called to withdraw money again. The msg.sender here in the fallback function is the auction contract, because it is an auction contract To transfer the money to this contract, the auction contract on the left is executed to if, and then transfer the money to him again. Pay attention to the clearing operation. The operation of clearing the hacker’s contract account is only done after the transfer transaction is completed. It will be carried out, and the previous statement of the if transfer has fallen into a recursive call with the hacker's contract, and the following clearing operation cannot be performed at all, so the final result is that the hacker gave a price when he first bid. After the auction is over, he will keep withdrawing money from this smart contract according to this price. The first time he gets it is his own bid, and the subsequent ones are other people's money.

       When will the recursive and repeated withdrawal of money last? There are three situations. One is that the balance on the auction contract is not enough to support the transfer statement. The second situation is that the gas fee is not enough, because the gas fee is still consumed every time it is called, and there is not enough in the end. In the third case, the call stack overflows, so the fallback function of the hacker contract judges that the balance of the auction contract is enough to support the transfer. The remaining gas currently called, msg.gas, still has more than 6,000 units. The depth of the call stack does not exceed 500. Then launch another round of attacks, so what should we do?

(2) How to deal with it

        The easiest way is to clear first and then transfer, which is Pay2Beneficiary’s way of writing, clear the account balance of the highestBidder to zero (the balance in the bids hash table has been cleared to 0), and then transfer, if the transfer is unsuccessful If so, restore the balance. This is actually a classic programming model for situations that may interact with other contracts. It is necessary to judge the conditions first, then change the conditions, and finally interact with other contracts. On the blockchain, any unknown contract may be malicious, so every time you transfer money to the other party or call a function of the other party, you must remind yourself that this contract, this function may be called in reverse With your current contract, and modifying the state, it is always good to be careful.

Another modification:

Figure 2-2

        In addition to this modification method, there is another method, as shown in Figure 2-2, which does not need to transfer money in the form of call.value. Compare the two pieces of code before and after modification (the part in the green box), and advance the clearing position ( Clear first and then transfer); and when transferring money, sender is used, and transfer can also be used. A common feature of sender and transfer is that the gas fee sent during the transfer is only 2300 units, which is not enough for the receiving contract Initiating a new call is only enough to write a log.

        This time there is no problem.

        In addition, as shown in Figure 2-3 below, the hacker contract does not write the fallback function. If this is not a hacker contract, it is an ordinary account, and he forgot to write the fallback function. What should I do? There is no way, even if this account is willing to change, it cannot be changed. He has no way to make up the fallback function, because it is published on the blockchain, this account can create a new contract, but this contract has already Participating in this auction has already been recorded in this cycle, and there is no way.

Figure 2-3

Guess you like

Origin blog.csdn.net/YSL_Lsy_/article/details/126601281