Platform: Code4rena
Start Date: 04/05/2022
Pot Size: $50,000 DAI
Total HM: 24
Participants: 71
Period: 5 days
Judge: Justin Goro
Total Solo HM: 14
Id: 119
League: ETH
Rank: 8/71
Findings: 2
Award: $1,823.44
π Selected for report: 1
π Solo Findings: 1
π Selected for report: cccz
Also found by: 0x52, 0xYamiDancho, GimelSec, IllIllI, PPrieditis, WatchPug, csanuragjain, danb, gzeon, hickuphh3, horsefacts, hyh, kenzo, leastwood, reassor, unforgiven
63.9296 DAI - $63.93
In FixedPricePassThruGate.sol#passThruGate()
, at L48 the msg.value
is checked to be >= gate.ethCost
instead of == gate.ethCost
, which makes it possible for the caller to send more than gate.ethCost
.
However, at L53 only the amount of gate.ethCost
is passed thru to gate.beneficiary
instead of all the msg.value
.
function passThruGate(uint index, address) override external payable { Gate memory gate = gates[index]; require(msg.value >= gate.ethCost, 'Please send more ETH'); // pass thru ether if (msg.value > 0) { // use .call so we can send to contracts, for example gnosis safe, re-entrance is not a threat here (bool sent, bytes memory data) = gate.beneficiary.call{value: gate.ethCost}(""); require(sent, 'ETH transfer failed'); } }
As a result, any surplus funds sent by the caller will be stuck in the contract forever, and there is no way for anyone to retrieve them.
Change to:
(bool sent, bytes memory data) = gate.beneficiary.call{value: msg.value}("");
See also: the implementation of SpeedBumpPriceGate.sol
correctly forwarded all the msg.value
.
function passThruGate(uint index, address) override external payable { uint price = getCost(index); require(msg.value >= price, 'Please send more ETH'); // bump up the price Gate storage gate = gates[index]; // multiply by the price increase factor gate.lastPrice = (price * gate.priceIncreaseFactor) / gate.priceIncreaseDenominator; // move up the reference gate.lastPurchaseBlock = block.number; // pass thru the ether if (msg.value > 0) { // use .call so we can send to contracts, for example gnosis safe, re-entrance is not a threat here (bool sent, bytes memory data) = gate.beneficiary.call{value: msg.value}(""); require(sent, 'ETH transfer failed'); } }
#0 - illuzen
2022-05-11T09:47:16Z
Duplicate #49
#1 - gititGoro
2022-06-14T02:52:48Z
Upgrated severity: lost funds
#2 - gititGoro
2022-06-14T02:53:05Z
Duplicate of #48
π Selected for report: WatchPug
1759.5116 DAI - $1,759.51
gate.priceIncreaseDenominator = priceIncreaseDenominator;
If priceIncreaseDenominator
is set to 0
when addGate()
, in passThruGate()
the tx will revert at L72 because of div by 0.
// multiply by the price increase factor gate.lastPrice = (price * gate.priceIncreaseFactor) / gate.priceIncreaseDenominator;
Consider adding a check in addGate()
to require priceIncreaseDenominator > 0
.
#0 - illuzen
2022-05-11T09:45:40Z
This is fine, we just add another gate, redeploy the tree and only harm done is we lost some gas.
#1 - gititGoro
2022-06-17T02:49:09Z
While value is not leaked in this instance, this can cause functionality to be interrupted until fixed. Severity of issue will be maintained.