_Follow along with this video:_ --- ### Recap At it's most minimalistic, a re-entrancy attack looks like this: ::image{src='/security-section-4/18-exploit-reentrancy/exploit-reentrancy3.png' style='width: 75%; height: auto;'} A reentrancy attack occurs when an attacker takes advantage of the recursive calling capability of a contract. By repeatedly calling a function within a contract, the attacker can withdraw funds or manipulate contract state before the initial function call is resolved, often leading to the theft of funds or other unintended consequences. As a more indepth reference: ::image{src='/security-section-4/18-exploit-reentrancy/exploit-reentrancy2.png' style='width: 75%; height: auto;'} We learnt that re-entrancy is a _very_ common attack vector and walked through how to identify and reproduce the vulnerability both in [**Remix**](https://remix.ethereum.org/#url=https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/reentrancy/Reentrancy.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.20+commit.a1b79de6.js) and locally as well as how to test for them. <details> <summary>Re-entrancy Test Example</summary> ```js // SPDX-License-Identifier: MIT pragma solidity 0.8.20; import {Test, console2} from "forge-std/Test.sol"; import {ReentrancyVictim, ReentrancyAttacker} from "../../src/reentrancy/Reentrancy.sol"; contract ReentrancyTest is Test { ReentrancyVictim public victimContract; ReentrancyAttacker public attackerContract; address victimUser = makeAddr("victimUser"); address attackerUser = makeAddr("attackerUser"); uint256 amountToDeposited = 5 ether; uint256 attackerCapital = 1 ether; function setUp() public { victimContract = new ReentrancyVictim(); attackerContract = new ReentrancyAttacker(victimContract); vm.deal(victimUser, amountToDeposited); vm.deal(attackerUser, attackerCapital); } function test_reenter() public { // User deposits 5 ETH vm.prank(victimUser); victimContract.deposit{value: amountToDeposited}(); // We assert the user has their balance assertEq(victimContract.userBalance(victimUser), amountToDeposited); // // Normally, the user could now withdraw their money if they like // vm.prank(victimUser); // victimContract.withdrawBalance(); // But... we get attacked! vm.prank(attackerUser); attackerContract.attack{value: 1 ether}(); assertEq(victimContract.userBalance(victimUser), amountToDeposited); assertEq(address(victimContract).balance, 0); vm.prank(victimUser); vm.expectRevert(); victimContract.withdrawBalance(); } } ``` </details> :br Additionally, we learnt that `static analysis` tools like `Slither` can even catch this vulnerability (though not always)! We also covered how to safeguard against this attack in at least two ways. - Adhering to the CEI (Checks, Effects, Interactions) pattern, assuring we perform state changes _before_ making external calls. - Implementing a nonReentrant modifier like one offered by [**OpenZeppelin's ReentrancyGuard**](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/ReentrancyGuard.sol). - Applying a mutex lock to our function ourselves. <details> <summary>Mutex Lock Example</summary> ```js bool locked = false; function withdrawBalance() public { if(locked){ revert; } locked = true; // Checks // Effects uint256 balance = userBalance[msg.sender]; userBalance[msg.sender] = 0; // Interactions (bool success,) = msg.sender.call{value: balance}(""); if (!success) { revert(); } locked = false; } ``` </details> :br Lastly, we learnt how this problem still plagues us today. Through this [**repo**](https://github.com/pcaversaccio/reentrancy-attacks) managed by Pascal et al, we can see a horrifying list, 7 years long, of just this single attack vector. We also uncovered a case study in [**The DAO hack**](https://medium.com/@zhongqiangc/smart-contract-reentrancy-thedao-f2da1d25180c) and saw just how severe this issue can be. Armed with all of this knowledge, surely you will _never_ miss a re-entrancy attack again. Let's move onto the PoC.
Patrick recaps reentrancy vulnerabilities and discusses prevention such as checks-effects-interactions (CEI) and non-reentrant modifiers.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Solidity Developer
Smart Contract SecurityDuration: 25min
Duration: 1h 18min
Duration: 35min
Duration: 2h 28min
Duration: 5h 03min
Duration: 5h 22min
Duration: 4h 33min
Duration: 2h 01min
Duration: 1h 40min
Testimonials
Read what our students have to say about this course.
Chainlink
Chainlink
Gustavo Gonzalez
Solutions Engineer at OpenZeppelin
Francesco Andreoli
Lead Devrel at Metamask
Albert Hu
DeForm Founding Engineer
Radek
Senior Developer Advocate at Ceramic
Boidushya
WalletConnect
Idris
Developer Relations Engineer at Axelar