Open hats-bug-reporter[bot] opened 3 months ago
Hey, hope it's okay to comment, believe me or not I actually explored this possibility before submitting #67. The problem is it does not pass the validClient(address(client))
check since it enforces that the client
passed in is the msg.sender
as well, so in this case the voter contract, which would not have payments since it's not a client.
My bad, you are right
@0xfuje Hi, I just noticed another pattern, would like to hear an opinion:
executeTxFromModule
which is inherited from ModuleManagerBase_v1
PP_Streaming_v1.sol
and cancels the payments through removeAllPaymentReceiverPayments
@0xfuje Hi, I just noticed another pattern, would like to hear an opinion:
- Such an edge-case vote passes
- The Voter module calls the Orchestrator's
executeTxFromModule
which is inherited fromModuleManagerBase_v1
- The Orchestrator calls inside
PP_Streaming_v1.sol
and cancels the payments throughremoveAllPaymentReceiverPayments
Hi, of course, I think I submitted the executeTxFromModule
issue in #50.
In this case specially the removeAllPaymentReceiverPayments()
is restricted by an onlyOrchestratorAdmin()
which is the orchestrator's admin (or Authorizer
's DEFAULT_ADMIN_ROLE
) which is not the same as the orchestrator itself so the tx will fail.
I also think this may be intended functionality, but not too sure, since if a stream has to be cancelled, voters have a way to cancel it.
Custom modules that are not in the repo, aka the logic modules, are OOS, thus an owner who adds his own external module should be aware of the risks of code. The only plausible scenario is if an edge-case 0 threshold vote passes, which I highlight.
I am down for letting this be a duplicate, but it is more of an extension to #50.
https://github.com/hats-finance/Inverter-Network-0xe47e52c4fea05e555920f1dcdcc6fb8eca103eeb/issues/50 is reported on the assumption that a future module would be able to exploit it, but does not provide a scenario. Custom modules that are not in the repo, aka the logic modules, are OOS, thus an owner who adds his own external module should be aware of the risks of code.
Respectfully, I provided realistic scenarios when you asked me in: https://github.com/hats-finance/Inverter-Network-0xe47e52c4fea05e555920f1dcdcc6fb8eca103eeb/issues/50#issuecomment-2155869736, and we already discussed external modules in #50.
The only plausible scenario is if an edge-case 0 threshold vote passes, which I highlight. I am down for letting this be a duplicate, but it is more of an extension to https://github.com/hats-finance/Inverter-Network-0xe47e52c4fea05e555920f1dcdcc6fb8eca103eeb/issues/50.
As I said in my first comment above: https://github.com/hats-finance/Inverter-Network-0xe47e52c4fea05e555920f1dcdcc6fb8eca103eeb/issues/71#issuecomment-2157718762, this exploit does not work, because it does not pass the validClient()
check.
If you are talking about the executeTxFromModule()
scenario: https://github.com/hats-finance/Inverter-Network-0xe47e52c4fea05e555920f1dcdcc6fb8eca103eeb/issues/71#issuecomment-2158623911. It will revert because of the access control.
validClient()
also forces the function to only cancel payments from the msg.sender
aka the client
that calls
My bad for not clarifying.
The valid attack path here is a combination:
executeTxFromModule
executeTxFromModule
goes inside transferOrchestratorToken
and transfers any amount out of the funding manager.My bad for not clarifying.
The valid attack path here is a combination:
- If a 0 threshold voter passes a vote he can execute a transaction from the Voter module
- From here he goes inside the Orchestrator and does
executeTxFromModule
executeTxFromModule
goes insidetransferOrchestratorToken
and transfers any amount out of the funding manager.50 is valid, my bad and sorry for the confusion. The only thing this report adds is the above attack path.
Don't take offence, but isn't this a different vulnerability: the scenario I told you in: https://github.com/hats-finance/Inverter-Network-0xe47e52c4fea05e555920f1dcdcc6fb8eca103eeb/issues/50#issuecomment-2155869736? only adds https://github.com/hats-finance/Inverter-Network-0xe47e52c4fea05e555920f1dcdcc6fb8eca103eeb/issues/67 as a first step? I'm not saying it's a duplicate just to clarify.
My bad for not clarifying. The valid attack path here is a combination:
- If a 0 threshold voter passes a vote he can execute a transaction from the Voter module
- From here he goes inside the Orchestrator and does
executeTxFromModule
executeTxFromModule
goes insidetransferOrchestratorToken
and transfers any amount out of the funding manager.50 is valid, my bad and sorry for the confusion. The only thing this report adds is the above attack path.
Don't take offence, but isn't this a different vulnerability: the scenario I told you in: #50 (comment)? only adds #67 as a first step? I'm not saying it's a duplicate just to clarify.
It's the combined steps in both. #50 mentions the scenario where the Voters do it, which is not plausible since it is a dedicated attack and the amount of voters needed is an assumtpion. However #67 shows an edge-case, which opens up the possibility of a single party draining the FM. It's the 2 steps combined yes.
Github username: @PlamenTSV Twitter username: @p_tsanev Submission hash (on-chain): 0x9778ef5c9a8ff46dfc942aa22d00101fe3d9d3fa68198e10713ca05f363141ae Severity: medium
Description: Description\ The
AUT_EXT_VotingRoles_v1.sol
implements a voting system which allows for adding, removing voters and executing arbitrary operations when the action's votes passes a given threshold. Said threshold is uncapped at the bottom, meaning that is such a edge-case any malicious voter would be able to execute arbitrary transactions from theAUT_EXT_VotingRoles_v1.sol
and bypass anyonlyModule
modifier inside other contracts.Attack Scenario\ This is a very specific edge-case requiring the threshold to be either 1 or 0, which are both possible valid states. This means that any proposed action will immediately pass and we enter:
This external call would be made from the voter contract, not from the user who executes the motion. Since the voter contract is a module inside of the Orchestrator system, we can execute any
onlyModule
function. The most impacted would have been the Logic Modules, but they include Module + specific role checks.However, the Payment Processor does not and the
PP_Streaming_v1.sol
could be impacted by a malicious voter.Inside
PP_Streaming_v1.sol
we haveremoveAllPaymentReceiverPayments
which invokes_removePayment
to remove a user's payment for the rest of the stream. This function isonlyOrchestratorAdmin
. However the functioncancelRunningPayments
isonlyModule
, which means our voted motion can call it directly._cancelRunningOrders
internally goes over all active payments and_removePayment
's them.This entire edge-case scenario would allow any single voter to cancel all payments at any time, unless the threshold of the voting contract gets changed. This can directly impact any Logic Module that implements a Streaming PP instead of a Simple PP, since the orders can be unexpectedly removed. For e.g the
LM_PC_RecurringPayments_v1
implements anonlyOrchestratorAdmin
function for removing payments, which we can bypass with the above scenario.Attachments
Proof of Concept (PoC) File
Revised Code File (Optional)
Recommendation:\ The options currently seen are 2:
cancelRunningPayments
fromonlyModule
toonlyOrchestratorAdmin
onlyModule
modifier toonlyModuleRole
and add a specific role able to cancel payments. The same logic is applied throughout the LMs with roles likePAYMENT_PUSHER_ROLE
,ASSERTER_ROLE
, etc.