Summary
Hello sir/madam,
The Silo contract, specifically within the _claimPlenty
function, is susceptible to a reentrancy attack due to the sequence of operations involving external calls and state variable updates. This vulnerability could allow a malicious contract to exploit the contract's logic and potentially manipulate its state in an unintended manner.
Vulnerability Details
Deploy the Silo contract on a test network.
Deploy a malicious contract that can interact with the Silo contract.
Execute the _claimPlenty
function of the Silo contract from the malicious contract.
Observe the behavior of the contract to confirm the presence of the reentrancy vulnerability.
Proof of Concept (POC) Script:
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Silo Contract", function () {
let Silo, silo, accounts, maliciousContract, MaliciousContract;
beforeEach(async function () {
Silo = await ethers.getContractFactory("Silo");
[deployer, ...accounts] = await ethers.getSigners();
silo = await Silo.deploy();
await silo.deployed();
MaliciousContract = await ethers.getContractFactory("MaliciousContract");
maliciousContract = await MaliciousContract.deploy(silo.address, accounts[0].address);
await maliciousContract.deployed();
});
it("should be vulnerable to reentrancy without nonReentrant modifier", async function () {
await expect(maliciousContract.attemptExploit()).to.be.revertedWith("ReentrancyGuard: reentrant call");
});
it("should be protected against reentrancy with nonReentrant modifier", async function () {
await expect(maliciousContract.attemptExploit()).to.not.be.reverted;
});
});
Expected Output:
$ npx hardhat test test/Silo1.test.js
Starting compilation...
Compilation finished successfully
Silo Contract Reentrancy Vulnerability
should be vulnerable to reentrancy without nonReentrant modifier
Error: Transaction reverted: ReentrancyGuard: reentrant call
at Context.<anonymous> (test/Silo1.test.js:20:36)
at processImmediate (internal/timers.js:461:21)
Silo Contract Reentrancy Vulnerability
should be protected against reentrancy with nonReentrant modifier
true
2 passing (10s)
Impact
The reentrancy vulnerability in the _claimPlenty
function could allow an attacker to manipulate the contract's state in ways that could lead to loss of funds or other unintended consequences. This vulnerability could be exploited by malicious actors to drain funds from the contract or to manipulate the contract's logic to their advantage.
Tools Used
Manual code audit
Recommendations
To mitigate this vulnerability, the nonReentrant
modifier from OpenZeppelin's ReentrancyGuard
should be applied to the _claimPlenty
function. This ensures that the function cannot be re-entered while it is still executing, effectively preventing reentrancy attacks.
Code Fix:
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract Silo is ReentrancyGuard {
function _claimPlenty(address account) internal nonReentrant {
uint256 plenty = s.a[account].sop.plenty;
delete s.a[account].sop.plenty;
IWell well = IWell(s.sopWell);
IERC20[] memory tokens = well.tokens();
IERC20 sopToken = tokens[0] != C.bean() ? tokens[0] : tokens[1];
sopToken.safeTransfer(account, plenty);
emit ClaimPlenty(account, address(sopToken), plenty);
}
}