How to do it...

  1. There are multiple ways a contract can interact with other contracts. Making validations around these interactions cannot be trusted, and the destination contract can manipulate them:
require(<external_contract_call>);
  1. It is recommended not to depend on untrusted third-party contracts for validations. The external call can make the contract useless.
  2. A place where this can be easily overlooked is while transferring Ether. If the destination address is an externally owned account, then the transfer will be successful. If it is a contract address, then the transfer will call the fallback function of that contract and execute the code written inside it.

  3. Write a fallback function that always cancels the transaction. This will make the calling contract's condition fail every time, making the contract useless.

  4. Consider the following example contract, which allows anyone to become king by sending a higher value than the previous king. Once the new king is seated, the old king receives the invested value back:
pragma solidity ^0.4.24; 

contract BecomeTheKing {

address currentKing;
uint highestBid;

function() payable {
// Verify the value sent
require(msg.value > highestBid);

// Transfer the previous bid back
require(currentKing.send(highestBid));

// Update the king and value
currentKing = msg.sender;
highestBid = msg.value;
}
}
  1. The contract looks very straightforward. For you to become king, place a bid that is greater than the previous bid. Once a new bid is validated, the old king gets his bid back.

  2. The send function, which is used to send the previous bid, assumes that the destination is an externally owned account.

  3. Create a contract to place a bid. Include a fallback function in the contract that always throws/reverts:
pragma solidity ^0.4.24;

// Attacker contract
contract AlwaysTheKing {

// Call the function to become the king
function becomeKing(address _address) payable {
_address.call.value(msg.value);
}

// Always revert when some value is sent
function() payable {
revert();
}
}
  1. When a new player places a higher bid in the BecomeTheKing contract, it will try to transfer the older bid. Since the current king is your contract, which never accepts this transfer, the condition will fail every time. This allows you to stay as king forever.

  2. To avoid such DoS attacks, it is recommended to use the Withdraw pattern. The pattern asks the recipient to withdraw the value rather than send it using the transfer function.

  3. This avoids the possibility of DoS in the contract flow. Even if the recipient tries such an attack, it can only affect the specific transaction and it has nothing to do with other users.

  1. Modify the BecomeTheKing contract as follows by using the withdraw pattern:
pragma solidity ^0.4.24; 

contract BecomeTheKing {

address currentKing;
uint highestBid;

mapping(address => uint) balances;

// Function to withdraw previous bids
function withdraw() public {
uint balance = balances[msg.sender];
require(balance > 0);
balances[msg.sender] = 0;
msg.sender.transfer(balance);
}

function() public payable {
require(msg.value > highestBid);

// Save the previous bid for withdrawal
balances[msg.sender] = highestBid;

currentKing = msg.sender;
highestBid = msg.value;
}
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.247.181