You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When submitting a proof to the batcher users can specify the max_fee they are willing to pay for the proof to be included in the next batch, the issue is that an attacker can specify 0 as max_fee and submit an arbitrary number of large VerificationData (limited by tungstenite-rs default config of 64mb per message and 16mb per frame) that will occupy memory in the batcher's queue indefinitely while the attacker only needs to lock 1 wei in the BatcherPaymentService contract, the attacker risks almost nothing because proofs with 0 max_fee will always be discarded in try_build_batch() and will never be submitted.
The root cause of the issue is that when a proof is submitted the check for the user's balance is done in verify_user_has_enough_balance() by comparing current user balance in the BatcherPaymentService contract to the max_fee specified by the user in the websocket message + the accumulated fee which is the sum of max_fee in previous submitted proofs (which could be 0) instead of comparing it to the minimum fee required for a proof to be submitted on-chain as calculated in calculate_fee_per_proof:
This allows a malicious user to submit an infinite amount of large proofs (max is 64mb) to the batcher while depositing 1 wei in the contract, those proofs will never be submitted on-chain but are never discarded from the batcher queue and will eventually exhaust the batcher's memory. The attacker only has to keep sending a max sized proof with 0 max_fee in an infinite loop through websocket to the batcher.
Proof of Concept
To demonstrate the issue we will be using the same 16mb payload used in #59 by inserting junk data in vm_program, also we need to generate a new keystore because the tests in Makefile use a non-paying address, the issue with non-paying addresses is that max_fee is hardcoded to 0.0013 eth in the batcher, but that should only be for testing purposes as mentioned in this comment.
Make sure to deposit 1 wei in batcher contract to the newly created address as described in 0_submitting_proofs.md to pass the locking check.
add the following to Makefile, make sure to replace $(KEYSTORE_PATH) with the path the keystore created in previous step or supply at as an env variable:
this will send 99 proofs that are 16mb, to avoid OOM in aligned cli this should be run multiple times. run make batcher_dos_fee multiple times while observing aligned-batcher memory usage increasing with top (ideally htop).
End of transcript
The takeaway is we need to define a reasonable minimum for the max_fee (and other maximum fees in other places) and enforce it across the system.
The text was updated successfully, but these errors were encountered:
Reported by cantina#66.
Transcript from the report:
Description
When submitting a proof to the batcher users can specify the
max_fee
they are willing to pay for the proof to be included in the next batch, the issue is that an attacker can specify 0 asmax_fee
and submit an arbitrary number of large VerificationData (limited by tungstenite-rs default config of 64mb per message and 16mb per frame) that will occupy memory in the batcher's queue indefinitely while the attacker only needs to lock 1 wei in theBatcherPaymentService
contract, the attacker risks almost nothing because proofs with 0max_fee
will always be discarded intry_build_batch()
and will never be submitted.The root cause of the issue is that when a proof is submitted the check for the user's balance is done in
verify_user_has_enough_balance()
by comparing current user balance in the BatcherPaymentService contract to themax_fee
specified by the user in the websocket message + the accumulated fee which is the sum ofmax_fee
in previous submitted proofs (which could be 0) instead of comparing it to the minimum fee required for a proof to be submitted on-chain as calculated incalculate_fee_per_proof
:This allows a malicious user to submit an infinite amount of large proofs (max is 64mb) to the batcher while depositing 1 wei in the contract, those proofs will never be submitted on-chain but are never discarded from the batcher queue and will eventually exhaust the batcher's memory. The attacker only has to keep sending a max sized proof with 0
max_fee
in an infinite loop through websocket to the batcher.Proof of Concept
To demonstrate the issue we will be using the same 16mb payload used in #59 by inserting junk data in
vm_program
, also we need to generate a new keystore because the tests in Makefile use a non-paying address, the issue with non-paying addresses is thatmax_fee
is hardcoded to 0.0013 eth in the batcher, but that should only be for testing purposes as mentioned in this comment.Make sure to deposit 1 wei in batcher contract to the newly created address as described in 0_submitting_proofs.md to pass the locking check.
add the following to Makefile, make sure to replace
$(KEYSTORE_PATH)
with the path the keystore created in previous step or supply at as an env variable:this will send 99 proofs that are 16mb, to avoid OOM in aligned cli this should be run multiple times. run
make batcher_dos_fee
multiple times while observing aligned-batcher memory usage increasing with top (ideally htop).End of transcript
The takeaway is we need to define a reasonable minimum for the
max_fee
(and other maximum fees in other places) and enforce it across the system.The text was updated successfully, but these errors were encountered: