Skip to content

Latest commit

 

History

History
424 lines (346 loc) · 14.5 KB

FP in LE.md

File metadata and controls

424 lines (346 loc) · 14.5 KB

There are false positives in the detection of Leaking Ether vulnerabilities by ConFuzzius.

Take the following code as an example: according to the transaction sequence information in the test cases, the contract owner first called the setBeneficiary function to set the attacker (which address is 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef) as the beneficiary, which complies with the syntax rules and contract logic (case the contract owner has super permission). Subsequently, the attacker called the getFees function to transfer the Ether stored in the contract, which also conforms to the business logic.

pragma solidity ^0.4.24;

// File: openzeppelin-solidity/contracts/ownership/Ownable.sol

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
    address public owner;


    event OwnershipRenounced(address indexed previousOwner);
    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    /**
     * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
    constructor() public {
        owner = msg.sender;
    }

    /**
     * @dev Throws if called by any account other than the owner.
   */
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    /**
     * @dev Allows the current owner to relinquish control of the contract.
   * @notice Renouncing to ownership will leave the contract without an owner.
   * It will not be possible to call the functions with the `onlyOwner`
   * modifier anymore.
   */
    function renounceOwnership() public onlyOwner {
        emit OwnershipRenounced(owner);
        owner = address(0);
    }

    /**
     * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param _newOwner The address to transfer ownership to.
   */
    function transferOwnership(address _newOwner) public onlyOwner {
        _transferOwnership(_newOwner);
    }

    /**
     * @dev Transfers control of the contract to a newOwner.
   * @param _newOwner The address to transfer ownership to.
   */
    function _transferOwnership(address _newOwner) internal {
        require(_newOwner != address(0));
        emit OwnershipTransferred(owner, _newOwner);
        owner = _newOwner;
    }
}

// File: contracts/Beneficiary.sol

// solhint-disable-next-line
pragma solidity ^0.4.24;

/** @title Beneficiary */
contract Beneficiary is Ownable {
    address public beneficiary;

    constructor() public {
        beneficiary = msg.sender;
    }

    /**
     * @dev Change the beneficiary address
     * @param _beneficiary Address of the new beneficiary
     */
    function setBeneficiary(address _beneficiary) public onlyOwner {
        beneficiary = _beneficiary;
    }
}

// File: contracts/Affiliate.sol

// solhint-disable-next-line
pragma solidity ^0.4.25;

/** @title Affiliate */
contract Affiliate is Ownable {
    mapping(address => bool) public canSetAffiliate;
    mapping(address => address) public userToAffiliate;

    /** @dev Allows an address to set the affiliate address for a user
      * @param _setter The address that should be allowed
      */
    function setAffiliateSetter(address _setter) public onlyOwner {
        canSetAffiliate[_setter] = true;
    }

    /**
     * @dev Set the affiliate of a user
     * @param _user user to set affiliate for
     * @param _affiliate address to set
     */
    function setAffiliate(address _user, address _affiliate) public {
        require(canSetAffiliate[msg.sender]);
        if (userToAffiliate[_user] == address(0)) {
            userToAffiliate[_user] = _affiliate;
        }
    }

}

// File: contracts/interfaces/ERC721.sol

contract ERC721 {
    function implementsERC721() public pure returns (bool);

    function totalSupply() public view returns (uint256 total);

    function balanceOf(address _owner) public view returns (uint256 balance);

    function ownerOf(uint256 _tokenId) public view returns (address owner);

    function approve(address _to, uint256 _tokenId) public;

    function transferFrom(address _from, address _to, uint256 _tokenId) public returns (bool);

    function transfer(address _to, uint256 _tokenId) public returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    // Optional
    // function name() public view returns (string name);
    // function symbol() public view returns (string symbol);
    // function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 tokenId);
    // function tokenMetadata(uint256 _tokenId) public view returns (string infoUrl);
}

// File: contracts/interfaces/PepeInterface.sol

contract PepeInterface is ERC721 {
    function cozyTime(uint256 _mother, uint256 _father, address _pepeReceiver) public returns (bool);

    function getCozyAgain(uint256 _pepeId) public view returns (uint64);
}

// File: contracts/AuctionBase.sol

// solhint-disable-next-line
pragma solidity ^0.4.24;

/** @title AuctionBase */
contract AuctionBase is Beneficiary {
    mapping(uint256 => PepeAuction) public auctions;//maps pepes to auctions
    PepeInterface public pepeContract;
    Affiliate public affiliateContract;
    uint256 public fee = 37500; //in 1 10000th of a percent so 3.75% at the start
    uint256 public constant FEE_DIVIDER = 1000000; //Perhaps needs better name?

    struct PepeAuction {
        address seller;
        uint256 pepeId;
        uint64 auctionBegin;
        uint64 auctionEnd;
        uint256 beginPrice;
        uint256 endPrice;
    }

    event AuctionWon(uint256 indexed pepe, address indexed winner, address indexed seller);
    event AuctionStarted(uint256 indexed pepe, address indexed seller);
    event AuctionFinalized(uint256 indexed pepe, address indexed seller);

    constructor(address _pepeContract, address _affiliateContract) public {
        pepeContract = PepeInterface(_pepeContract);
        affiliateContract = Affiliate(_affiliateContract);
    }

    /**
     * @dev Return a pepe from a auction that has passed
     * @param  _pepeId the id of the pepe to save
     */
    function savePepe(uint256 _pepeId) external {
        // solhint-disable-next-line not-rely-on-time
        require(auctions[_pepeId].auctionEnd < now);//auction must have ended
        require(pepeContract.transfer(auctions[_pepeId].seller, _pepeId));//transfer pepe back to seller

        emit AuctionFinalized(_pepeId, auctions[_pepeId].seller);

        delete auctions[_pepeId];//delete auction
    }

    /**
     * @dev change the fee on pepe sales. Can only be lowerred
     * @param _fee The new fee to set. Must be lower than current fee
     */
    function changeFee(uint256 _fee) external onlyOwner {
        require(_fee < fee);//fee can not be raised
        fee = _fee;
    }

    /**
     * @dev Start a auction
     * @param  _pepeId Pepe to sell
     * @param  _beginPrice Price at which the auction starts
     * @param  _endPrice Ending price of the auction
     * @param  _duration How long the auction should take
     */
    function startAuction(uint256 _pepeId, uint256 _beginPrice, uint256 _endPrice, uint64 _duration) public {
        require(pepeContract.transferFrom(msg.sender, address(this), _pepeId));
        // solhint-disable-next-line not-rely-on-time
        require(now > auctions[_pepeId].auctionEnd);//can only start new auction if no other is active

        PepeAuction memory auction;

        auction.seller = msg.sender;
        auction.pepeId = _pepeId;
        // solhint-disable-next-line not-rely-on-time
        auction.auctionBegin = uint64(now);
        // solhint-disable-next-line not-rely-on-time
        auction.auctionEnd = uint64(now) + _duration;
        require(auction.auctionEnd > auction.auctionBegin);
        auction.beginPrice = _beginPrice;
        auction.endPrice = _endPrice;

        auctions[_pepeId] = auction;

        emit AuctionStarted(_pepeId, msg.sender);
    }

    /**
     * @dev directly start a auction from the PepeBase contract
     * @param  _pepeId Pepe to put on auction
     * @param  _beginPrice Price at which the auction starts
     * @param  _endPrice Ending price of the auction
     * @param  _duration How long the auction should take
     * @param  _seller The address selling the pepe
     */
    // solhint-disable-next-line max-line-length
    function startAuctionDirect(uint256 _pepeId, uint256 _beginPrice, uint256 _endPrice, uint64 _duration, address _seller) public {
        require(msg.sender == address(pepeContract)); //can only be called by pepeContract
        //solhint-disable-next-line not-rely-on-time
        require(now > auctions[_pepeId].auctionEnd);//can only start new auction if no other is active

        PepeAuction memory auction;

        auction.seller = _seller;
        auction.pepeId = _pepeId;
        // solhint-disable-next-line not-rely-on-time
        auction.auctionBegin = uint64(now);
        // solhint-disable-next-line not-rely-on-time
        auction.auctionEnd = uint64(now) + _duration;
        require(auction.auctionEnd > auction.auctionBegin);
        auction.beginPrice = _beginPrice;
        auction.endPrice = _endPrice;

        auctions[_pepeId] = auction;

        emit AuctionStarted(_pepeId, _seller);
    }

    /**
     * @dev Calculate the current price of a auction
   * @param  _pepeId the pepeID to calculate the current price for
   * @return currentBid the current price for the auction
   */
    function calculateBid(uint256 _pepeId) public view returns (uint256 currentBid) {
        PepeAuction storage auction = auctions[_pepeId];
        // solhint-disable-next-line not-rely-on-time
        uint256 timePassed = now - auctions[_pepeId].auctionBegin;

        // If auction ended return auction end price.
        // solhint-disable-next-line not-rely-on-time
        if (now >= auction.auctionEnd) {
            return auction.endPrice;
        } else {
            // Can be negative
            int256 priceDifference = int256(auction.endPrice) - int256(auction.beginPrice);
            // Always positive
            int256 duration = int256(auction.auctionEnd) - int256(auction.auctionBegin);

            // As already proven in practice by CryptoKitties:
            //  timePassed -> 64 bits at most
            //  priceDifference -> 128 bits at most
            //  timePassed * priceDifference -> 64 + 128 bits at most
            int256 priceChange = priceDifference * int256(timePassed) / duration;

            // Will be positive, both operands are less than 256 bits
            int256 price = int256(auction.beginPrice) + priceChange;

            return uint256(price);
        }
    }

    /**
     * @dev collect the fees from the auction
   */
    function getFees() public {
        beneficiary.transfer(address(this).balance); //Leaking Ether
    }


}

// File: contracts/PepeAuctionSale.sol

// solhint-disable-next-line
pragma solidity ^0.4.19;

//Most functionality is in the AuctionBase contract.
//This contract is to buy pepes on the auction.
contract PepeAuctionSale is AuctionBase {
    // solhint-disable-next-line
    constructor(address _pepeContract, address _affiliateContract) AuctionBase(_pepeContract, _affiliateContract) public {

    }

    /**
     * @dev Buy a pepe from the auction
     * @param  _pepeId The id of the pepe to buy
     */
    function buyPepe(uint256 _pepeId) public payable {
        PepeAuction storage auction = auctions[_pepeId];

        // solhint-disable-next-line not-rely-on-time
        require(now < auction.auctionEnd);// auction must be still going

        uint256 price = calculateBid(_pepeId);
        require(msg.value >= price); //must send enough ether
        uint256 totalFee = price * fee / FEE_DIVIDER; //safe math needed?

        //Send ETH to seller
        auction.seller.transfer(price - totalFee);
        //send ETH to beneficiary

        // solhint-disable-next-line
        if (affiliateContract.userToAffiliate(msg.sender) != address(0) && affiliateContract.userToAffiliate(msg.sender).send(totalFee / 2)) { //if user has affiliate
            //nothing to do here. Just to suppress warning
        }
        //Send pepe to buyer
        if (!pepeContract.transfer(msg.sender, _pepeId)) {
            revert(); //can't complete transfer if this fails
        }

        emit AuctionWon(_pepeId, msg.sender, auction.seller);

        if (msg.value > price) { //return ether send to much
            msg.sender.transfer(msg.value - price);
        }

        delete auctions[_pepeId];//deletes auction
    }

    /**
     * @dev Buy a pepe and send along affiliate address
     * @param  _pepeId The id of the pepe to buy
     * @param  _affiliate address of the affiliate to set
     */
    // solhint-disable-next-line func-order
    function buyPepeAffiliated(uint256 _pepeId, address _affiliate) external payable {
        affiliateContract.setAffiliate(msg.sender, _affiliate);
        buyPepe(_pepeId);
    }

}
'6360': [
      {
        'type': 'Leaking Ether',
        'individual': [
          {
            'transaction': {
              'from': '0xcafebabecafebabecafebabecafebabecafebabe', //the owner of the contract
              'to': '0x2c5e8a3b3aad9df32339409534e64dfcabcd3a65',  //the address of the contract
              'value': 0,
              'gaslimit': 4500000,
              'data': '0x1c31f710000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef'  //invoke the setBeneficiary function, the parameter is the address of the attacker
            },
          },
          {
            'transaction': {
              'from': '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',  //the address of the attacker
              'to': '0x2c5e8a3b3aad9df32339409534e64dfcabcd3a65',
              'value': 0,
              'gaslimit': 4500000,
              'data': '0xdb8d55f1'  //invoke the getFees function
            },
          },
          {
            'transaction': {
              'from': '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
              'to': '0x2c5e8a3b3aad9df32339409534e64dfcabcd3a65',
              'value': 0,
              'gaslimit': 4500000,
              'data': '0x1c31f7100000000000000000000000003501454135014541350145413501454135014541'
            },
          }
        ],
        'time': 338.38306188583374,
        'line': 306,
        'column': 1,
        'source_code': 'beneficiary.transfer(address(this).balance)'
      }
    ]