Code Snippet Serie - 03 - Cross-Function-Reentrancy
Challenge Description
This challenge, authored by @KLM, involves exploiting a vulnerability in a vyper smart contract that utilize a Cross-Function-Reentrancy due to a problem in the vyper version. This smart contract is made for a company to sell shares on the blockchain to help and ensure everything is secure, tracked and transparent.
Vulnerability Overview
🛑 Vulnerability: The vulnerability lies in the insecure version of Vyper that desynchronise the values of the @nonreentrant("lock")
between function in a contract and the bad execution flow management.
Exploitation Process
-
⚙️ Discovery:
- At first, this contract seems really secure and well done, but if you look at the sell and transfer functions, you can see a reentrancy vulnerability. However, they are protected with a nonreentrant lock. If you look deeper, searching for the the vyper compiler version, you can find a huge vulnerability in the nonreentrant locks.
-
🧠 Understanding the Cross-Function-Reentrancy:
- The basic Reentrancy is a programming concept where a function or subroutine can be interrupted and then resumed before it finishes executing. This means that the function can be called again before it completes its previous execution.
- Cross Function just means you have to call another function before the first one finishes.
-
🎭 Exploitation Steps:
-
🛠️ Creating a malicious contract to exploit the Cross-Function-Reentrancy:
# @version 0.2.15 """ @author KLM @title Solve for The Cross Function Reentrancy """ # External Interfaces interface SS: def buyStock(): payable def sellStock(sell_order: uint256): nonpayable def transferStock(receiver: address, transfer_order: uint256): nonpayable def company() -> address: view def totalShares() -> uint256: view def price() -> uint256: view snaketarget: SS me: address @external def setparams(_target: address, _me: address): self.snaketarget = SS(_target) self.me = _me @external @payable def step1(): self.snaketarget.buyStock(value=msg.value)
-
📤 Submitting the Malicious Payload:
- Set your target with the
setparams()
function. - Trigger the
step1()
function by sending a tx to the attack contract. - Trigger the reentrancy by sending a tx to the
step2()
function.
- Set your target with the
-
Mitigation
🔒 To mitigate this vulnerability, the following measures can be taken:
- 🚧 Fix the order of the execution flow, you need to update storage variables BEFORE interacting with another actor.
- 🚧 Change the vyper version to a more up-to-date one.
Conclusion
🔑 Sometimes, when everything seems fine, you will find deeper flaws in the things you thought were out of scope.