The sendProfit()
function in the MembershipERC1155
contract contains a reentrancy vulnerability that allows an attacker to recursively exploit the contract by draining its balance. This vulnerability arises from the contract's use of an external call (safeTransferFrom
) before updating the contract's internal state. The attacker can trigger a recursive call to sendProfit()
while the state of the contract is still in an inconsistent state, leading to unintended transfers of funds.
The vulnerability is located in the sendProfit()
function of the contract, which is responsible for distributing profits to token holders. Specifically, the vulnerability exists because the external call to transfer the profit (IERC20(currency).safeTransferFrom(msg.sender, address(this), amount)
) occurs before the internal state of the contract is updated.
sendProfit()
In the sendProfit()
function, the contract performs an external transfer using the safeTransferFrom
function of an ERC20 token. However, it does this before updating the internal state of the contract (i.e., the totalProfit
state variable). This opens the contract to a reentrancy attack, where an attacker can recursively call sendProfit()
before the state is updated, thus allowing them to withdraw more funds than intended.
The key part of the vulnerable code is:
In this scenario, an attacker can exploit this ordering to recursively call sendProfit()
while the totalProfit
variable is still in its previous state, draining the contract of all available funds.
An attacker can:
Exploit the vulnerability by deploying a malicious contract that recursively calls sendProfit()
before the state is updated.
Drain the contract’s balance by making multiple recursive calls, allowing the attacker to withdraw more funds than they should have been entitled to.
Interrupt normal operations by draining the contract’s funds and preventing legitimate users from claiming profits.
If the attacker’s malicious contract is designed correctly, they can recursively exploit the vulnerability until the contract's balance is depleted, leaving the contract with no funds to distribute.
Hardhat: A popular development environment for Ethereum, used to write and deploy the smart contracts.
Foundry: A smart contract testing framework that allows for fast testing of Solidity code and includes built-in assertions and utilities.
Ethers.js: A JavaScript library to interact with Ethereum smart contracts, used in conjunction with Hardhat for testing.
The following is a proof of concept (PoC) demonstrating the reentrancy vulnerability:
Deployment:
The test first deploys the MembershipERC1155
contract, which is the vulnerable contract under examination.
Then, it deploys the MaliciousToken
contract, which is designed to exploit the reentrancy vulnerability by recursively calling sendProfit()
.
Attack Simulation:
The malicious contract's attack()
function triggers a recursive call to sendProfit()
before the state of the contract is updated, draining the contract's balance.
Assertions:
After the attack, we assert that the contract's balance is drained to zero.
We also check that after the funds are drained, further attempts to send profits should fail because there are no funds left.
When the tests are run with Hardhat, the expected output should indicate that the vulnerability has been successfully exploited, and the contract’s funds are drained:
To fix the reentrancy vulnerability, state changes must be made before external calls. Specifically, the sendProfit()
function should update the contract's state before making any transfers. This prevents recursive calls from being made before the state has been properly updated.
This fix ensures that the state is updated before making the external call, preventing reentrancy attacks.
The sendProfit()
function in the MembershipERC1155
contract contains a critical reentrancy vulnerability that can be exploited by an attacker to drain the contract's balance. By following the Checks-Effects-Interactions pattern, where state changes are made before external calls, this vulnerability can be mitigated. It is important to apply this pattern universally in contracts that interact with external addresses to prevent potential exploits.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.