Open code423n4 opened 2 years ago
If the callback would revert, normally it won't be called.
However, the attacker (griefer) could potentially frontrun the relayer's callback transaction (which is already submitted / in the mempool) and update the state of their callback contract in such a way to cause this subsequent callback to fail. Why would they do that? Nothing is gained, only losses are incurred on both sides.
This seems like it should be invalid, but it also seems like we should be doing a try/catch on principle though. Perhaps the issue is misrepresented here - it's not so much an attack vector as it's 'best practice' / QA issue.
Let's de-escalate to QA issue. Confirming, but would be great to get a second look from @LayneHaber on this.
EDIT: Changed my mind, think the risk level is appropriate.
If you change the message, the following check(s) would fail (since the hash is put onchain when the message is propagated by nomad):
bytes32 messageHash = messageHashes[transferId];
if (messageHash == bytes32(0)) revert PromiseRouter__process_invalidTransferId();
bytes29 _msg = _message.ref(0).mustBePromiseCallback();
if (messageHash != _msg.keccak()) revert PromiseRouter__process_invalidMessage();
An attacker cannot falsify this message because sending messages on the router is restricted via the onlyConnext
modifier, so I don't think changing the callbackAddress
is a valid attack strategy.
If we extend this concern to any failing callback
, then any revert would not be executed. In most cases, this would be caught in offchain simulations meaning the relayer would not submit the transaction (and then nobody could process the callback). This means that integrators must handle failures within their code. This is a common practice when writing crosschain handlers (i.e. nomad handle
should not revert as the message cannot be reprocessed, functions called via execute
should handle errors unless sending funds to a recovery address is okay, etc.).
Don't think this qualifies as a value leak, but because it is potentially unprocessable will leave the severity at 2 and move the label to "acknowledged"
I guess this issue has the same concerns as #220. Even if the onus is on the caller of process
to simulate the call beforehand, it seems likely that the transaction could be front-run and prove to be poor user experience for relayers.
I don't think its realistic that relayers would call this function without first simulating the transaction to see if the callback fails, but I guess the bridge user could set the callback address up as a honey pot such that simulated transactions are successful.
It probably makes sense to remove the main culprit of the issue by using a try/catch statement for the ICallback(callbackAddress).callback(transferId, _msg.returnSuccess(), _msg.returnData());
call.
Lines of code
https://github.com/code-423n4/2022-06-connext/blob/b4532655071566b33c41eac46e75be29b4a381ed/contracts/contracts/core/promise/PromiseRouter.sol#L226-L262
Vulnerability details
Impact
process()
inPromiseRouter
is used for process stored callback function and anyone calls it getscallbackFee
and it callscallback()
function ofcallbackAddress
. but attacker set acallbackAddress
that reverts oncallback()
and causeprocess()
caller griefing. attacker can perform this buy front running or complex logic.Proof of Concept
This is
process()
code:As you can see it calls
ICallback(callbackAddress).callback(transferId, _msg.returnSuccess(), _msg.returnData());
and if that call reverts then whole transaction would revert. so attacker can setupcallbackAddress
that revert and the caller ofprocess()
wouldn't get any fee and lose gas.Tools Used
VIM
Recommended Mitigation Steps
change the code so it won't revert if call to
callbackAddress
reverts.