# Tag Info

37

A Simpler Explanation The attacker creates a wallet contract (0xc0ee9db1a9e07ca63e4ff0d5fb6f86bf68d47b89 in the 17/06/2016 attack) with a default (or fallback) function () to call The DAO's splitDAO(...) function a number of times. Following is a simple default function (): function () { // Note that the following statement can only be called ...

19

The fallback function can do as much computation as how much gas it gets. There are 2 cases (basically recipient.send and recipient.call.value()) and case 2 has security implications and is a key part of how theDAO was attacked and exploited on June 17 2016. Case 1: 2300 gas A recipient contract's fallback function only gets a 2300 gas stipend if it was ...

14

If your code looks like this in pseudo code: function do: if (pool has mymoney = true) split(mymoney) pool has mymoney = false By repeatedly calling that function, you have sort of a race condition where you are allowed to spend your money twice, trice, ... ad infitum. Fix is simple, reverse two operations: function do: if (pool has ...

13

transfer() and send() should be avoided (because they take a hard dependency on gas costs by forwarding a fixed amount of gas: 2300). Gas specific code (call.gas().value()()) should also be avoided. call.value()() should be used, for example: contractB.call.value(1000)() It is also critical that you make sure to guard against reentrancy by making all state ...

12

What happened? 3,641,694 ETH where splitted out of theDAO. The attacker found a loophole in the regular splitDAO function so that they could reuse the same DAO tokens over and over again. How did the attack worked exactly? The attacker managed to combine 2 exploits. The first exploit was to call the split DAO function recursively. That means the ...

10

Call depth attack A. No. If the call depth is at 1024, employeeAddress.send will fail. The depth remains at 1024 and bossAddress.send will also fail. The depth will only decrease to 1023 when payout (or its caller, depending on how payout is invoked) is finished. B. Yes, that all sounds correct and no need for any call depth manipulation. C. There is ...

6

.send will only provide 2300 gas to the fallback function and is safe. But you should carefully check and handle the return value of .send, because of the "call depth attack": How does the stack depth attack make a send() silently fail? and another example. Note: "recursive call attack" is ambiguous because I think you mean a "reentrant attack" (but the "...

6

A very highlevel, very simplified answer. If you send a split request to the DAO, it checks your token account, creates a new copy of the DAO with your ethers in it and then reduces your account by the amount of tokens you split out. If the address from which you send the split request however reacts to the answer by sending a new split request, your token ...

6

In case the danger and the pattern isn't clear, a contract is vulnerable if it calls, sends or transfers to an untrusted address (contract with fallback function?) and then updates the state afterward. This is because the state is implicitly incomplete when flow control is transferred to a (potentially) hostile contract. Flow control may not return as the ...

6

5

A "recursive calling vulnerability" is an ambiguous term that should be avoided because it is imprecise and can mean 2 things. Reentrant attack You probably mean "reentrancy vulnerability" or "reentrant attack", which is what @Roland's answer describes. Note: not all reentrant attacks have to be recursive (in the sense that malicious code does not have to ...

5

This is the problematic line of code in the withdrawRewardFor function: if (!rewardAccount.payOut(_account, reward)) <-- reentrant exploit throw; paidOut[_account] += reward; The payOut will call the recipient payout function: function payOut(address _recipient, uint _amount) returns (bool) { .. if (_recipient.call.value(...

5

No, there are no contract invocations with a selfdestruct (suicide), so there is no possibility of reentrancy. There are no CALLs as you've noted and the Geth code you've quoted is correct.

5

EDIT Dec 2019: call.value()() should now be used for transferring ether. See: Is transfer() still safe after the Istanbul update (2300 gas limitation)? The following is the original answer. contract.call.gas(...).value(...)(...) is a way to add Ether and limit gas when invoking a contract. Basically, TheDAO used call.value to move Ether around in a ...

4

It's not possible because the negative gas is accumulated in a separate refund counter and only provided at the very end of the transaction (up to a maximum of half the gas used). See What are the limits to gas refunds?

4

Before June 2016 August 2014: Reentrant Contracts gives an example of "a contract can be tricked into calling itself". July 2015: LeastAuthority Ethereum Analyses mentioned: reentrancy hazards if the callback itself executes publish(), subscribe(), or unsubscribe(): repeated actions, missing actions, and inconsistent delivery of messages November ...

4

If you are a developer or interested in recreating the attack, I've created two contracts for the purpose of demonstrating exactly how it worked. https://github.com/joeb000/mock-dao-hack There are also some articles linked in the README file you might find helpful.

4

Both send() and transfer() are subject to the 2300 gas stipend. The main difference between them is that transfer throws on error and that transfer() supports the .gas() modifier, allowing you to override the gas being passed. Look here for further info: https://github.com/ethereum/solidity/issues/610 Nevertheless, if you wanted to be completely ...

4

My understanding is that: The counter is incremented A copy of the value of counter is kept Control is returned (https://solidity.readthedocs.io/en/v0.6.8/contracts.html#function-modifiers) There could be reentrancy where the counter would be incremented A check is made that the copy of the counter value still equals the counter otherwise it reverts You ...

3

I setup a minimal test example that you can find on https://gist.github.com/anonymous/07d4714c27dbf1af0e5cb16c9f833353 pragma solidity ^0.4.11; contract Test { Receiver myR; function setReceiver (address a) { myR = Receiver(a); } function callR() { myR.call(this.balance); } function sendCash() payable { } } ...

3

A reasonable analogy I've heard is: You go to an ATM and withdraw the money. After the ATM gives you the money, you unplug the ATM before it updates your account. You plug the ATM back, withdraw money and repeat the process. One of the things that can help mitigate this reentrant attack is for the ATM update to your account first, before giving you the ...

3

Ethcore blogpost does a good job at describing the history of the hack. Basically they knew about the vulnerability two days before the attack and fixed the function that they though was affected. Read more here slock.it blog. From the guy who wrote about the vulnerability before the attack: I wrote up this vulnerability last week: you can read more about ...

3

The simple high level explanation would be like this; When you (as a DAO token holder) split from DAO, it creates a new child DAO and pays you back (in a transaction) your share by sending it to the new splitted DAO, (your DAO). The attacker created a loop (by recursively calling the splitDAO function) caused the DAO paying several times instead of once. ...

3

Update Before Sep 2016 send and transfer pass all available gas to CALL op by default. This was changed in the Solidity compiler in this commit https://github.com/ethereum/solidity/commit/9ca7472089a9f4d8bfec20e9e55c4f7ed2fb502e. // Provide the gas stipend manually at first because we may send zero ether. // Will be zeroed if we send more than zero ...

3

You should use transfer() (it will throw if something bad happens and revert all previous operations) and you don´t need to add again value to the balance. Your code would be like this: balances[msg.sender] -= winnings msg.sender.transfer(winnings); If transfer has a problem, it will throw and balances[msg.sender] will get the previous value. ps: Don´t ...

3

It calls the anonymous fallback function on msg.sender. In a typical reentrancy attack, it would be something like a withdraw function doing msg.sender.call.value(1 ether)(). The caller (a smart contract), would then call the function again, hence the "reentrancy" attack. In this snippet, the call doesn't seem to be doing anything useful, but it's just ...

3

msg.sender.call() calls the fallback-function on msg.sender. Here is an example that is extended with a canBeAttacked function. contract Mutex { bool locked; modifier noReentrancy() { require(!locked); locked = true; _; locked = false; } function canBeAttacked() public returns (uint) { require(msg.sender.call.value(1 ether)()); ...

3

In your code, the line: target.call(bytes4(keccak256("withdraw()")),amount); Should be: target.call(bytes4(keccak256("withdraw(uint256)")),amount); Hope this helps you.

3

The code: modifier modi() { prolog(); _; epilog(); } function func() modi() { stuff(); } Is equivalent to: function func() { prolog(); stuff(); epilog(); } If func is re-entered (i.e., called from within the execution of stuff), then prolog is executed a second time before epilog was executed for the first time. So by ...

3

You can add it to the original constructor: contract MyContract is ERC721, ReentrancyGuard { constructor() ERC721("MyToken", "TOK") ReentrancyGuard() public { } } With that said, I do not believe OpenZeppelin's ReentrancyGuard has a constructor. You may want to verify that.

Only top voted, non community-wiki answers of a minimum length are eligible