DeFiHardhatOracleProxyUpdates
100,000 USDC
View results
Submission Details
Severity: low
Invalid

Reentrancy Vulnerability in Silo Contract's `_claimPlenty` Function

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

  1. Deploy the Silo contract on a test network.

  2. Deploy a malicious contract that can interact with the Silo contract.

  3. Execute the _claimPlenty function of the Silo contract from the malicious contract.

  4. 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 () {
// Deploy the Silo contract
Silo = await ethers.getContractFactory("Silo");
[deployer, ...accounts] = await ethers.getSigners();
silo = await Silo.deploy();
await silo.deployed();
// Deploy a malicious contract that will attempt to exploit the reentrancy vulnerability
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 () {
// Simulate a scenario where the malicious contract attempts to exploit the reentrancy vulnerability
// This test assumes that the Silo contract does not use the nonReentrant modifier
// and that the malicious contract can somehow trigger a reentrant call to the Silo contract
// This is a simplified example and may not directly apply to your contract's logic
await expect(maliciousContract.attemptExploit()).to.be.revertedWith("ReentrancyGuard: reentrant call");
});
it("should be protected against reentrancy with nonReentrant modifier", async function () {
// Apply the nonReentrant modifier to the _claimPlenty function in the Silo contract
// This test assumes that the Silo contract uses the nonReentrant modifier
// and that the malicious contract cannot trigger a reentrant call to the Silo contract
// This is a simplified example and may not directly apply to your contract's logic
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 {
// Other contract code...
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);
}
}
Updates

Lead Judging Commences

giovannidisiena Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Too generic
Assigned finding tags:

Informational/Invalid

pisces Submitter
about 1 year ago
giovannidisiena Lead Judge
about 1 year ago
pisces Submitter
about 1 year ago
giovannidisiena Lead Judge
about 1 year ago
pisces Submitter
about 1 year ago
pisces Submitter
about 1 year ago
giovannidisiena Lead Judge
about 1 year ago
giovannidisiena Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Too generic
Assigned finding tags:

Informational/Invalid

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.