Open c4-bot-2 opened 6 months ago
raymondfam marked the issue as sufficient quality report
raymondfam marked the issue as primary issue
This would happen only when the entrypoint batches the transactions. Additionally, this is kind of related to the known issue from readme: When acting as a paymaster, EntryPoint will debit MagicSpend slightly more than actualGasCost, meaning what is withheld on a gas-paying withdraw will not cover 100% of MagicSpend's balance decrease in the EntryPoint.
Ah right I feel like we discussed this @xenoliss and then perhaps forgot in later conversation. The only way to avoid would be to have validation keep some tally of all the expected coming withdraws.
wilsoncusack (sponsor) confirmed
To be fair, one could argue that the bundlers should simulate the execution of their bundles before submission to avoid reverts; this is however a valid way of grieving the reputation of the MagicSpend contract
3docSec marked the issue as satisfactory
To be fair, one could argue that the bundlers should simulate the execution of their bundles before submission to avoid reverts; this is however a valid way of grieving the reputation of the MagicSpend contract
Re, it is correct that the bundler would be expected to see this and revert the whole bundle. However the point is valid that the guard is not fully satisfactory as it is written. It should probably be removed or fixed.
3docSec marked the issue as selected for report
Bundler always check such cases - https://github.com/etherspot/skandha/blob/master/packages/executor/src/services/BundlingService/service.ts#L263-L268
Also, there is eip - https://github.com/eth-infinitism/ERCs/blob/0a3011040045ea36b551dd287d895a1187204b96/ERCS/erc-7562.md#entity-specific-rules
So, such bundle will never be even created.
While I agree (and already stated before) that bundlers should be defensive wrt bundles reverting as a whole, it is true that this finding proves that the MagicSpend can transactions it won't later pay for, which is against the promise that is implicit in a successful validatePaymasterUserOp
Bundlers make validatios offline, so the paymaster(magicSpender) should not write anything to storage - this makes no sense
@SovaSlava comments in PJQA must come with factual evidence, so please refrain from making any statement about what makes sense for you.
The factual evidence is that SSTORE
and SLOAD
are not among the opcodes a paymaster can't use in the validation phase.
step 3 in POC mentions that userop1 and userop2 are packed together in a bundle. Such scenario is impossible as long as the bundler follows the rules of EIP-4337. For EIP-4337 validation rules pls refer to the ERC-7562. Sources:
The rule EREP-010 specifically mentions the case described in POC: https://docs.stackup.sh/docs/entity-specific-rules#erep-010-paymaster-gas-limits
And you can check yourself how bundlers implement this rule: Etherspot's Skandha: https://github.com/etherspot/skandha/blob/master/packages/executor/src/services/BundlingService/service.ts#L255-L277
Alchemy's Rundler: https://github.com/alchemyplatform/rundler/blob/main/crates/pool/src/mempool/uo_pool.rs#L371-L375
Stackup's bundler: https://github.com/stackup-wallet/stackup-bundler/blob/main/pkg/modules/checks/standalone.go#L176-L209
What I'm trying to say is that the current implementation of the paymaster is perfectly in line with EIP-4337 and is not a bug. Paymaster can't and shouldn't validate the whole bundle.
You can argue that some bundler may not follow EREP-010 and submit a bundle that will revert, but we can make this case for any arbitrary validation rule.
step 3 in POC mentions that userop1 and userop2 are packed together in a bundle. Such scenario is impossible as long as the bundler follows the rules of EIP-4337
I have no proof of this statement, no part of EIP-4337 requires a paymaster native balance check.
Such precautionary check is therefore arbitrary, and to confirm this, you can observe that the checks you linked are on the paymaster's deposit on the EntryPoint, while this finding reports insufficient native token balance to cover postOp
:
paymasterDeposit[paymaster] = await entryPointContract.balanceOf(
paymaster
);
Lines of code
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L130-L135
Vulnerability details
Impact
Balance check during MagicSpend validation cannot ensure that MagicSpend has enough balance to cover the requested fund.
Proof of Concept
MagicSpend
withdrawAmount
100withdrawAmount
150UserOperation[]
ops and ops will be executed by entryPoint through thehandleOps
methodvalidatePaymasterUserOp
will be called during userOp1 validation and pass for address(this).balance (=200) is larger than withdrawAmount (=100)validatePaymasterUserOp
will be called during userOp1 validation and pass for address(this).balance (=200) is larger than withdrawAmount (=150)postOp
of userOp1, the balance of MagicSpend is 100.postOp
of userOp2, MagicSpend does not has enough balance to cover the requested fund (=150) of userOp2 and the execution will fail.Recommended Mitigation Steps
Use a state instead of
address(this).balance
to record the remain balance.Assessed type
Access Control