-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Attacker can block LayerZero channel #244
Comments
I don't think this is valid, since all LayerZero does here is to assign the job (the actual execution of the beaming) to an operator to be executed later, and the assigning part can't be failed easily. |
Setting gasLimit/gasPrice to very low can only block the channel temporarily since one can donate fund to unblock it. |
Warden is trying to fail |
#445 might cause lzreceive to revert due to insufficient gas |
LayerZero functionality either 100% succeeds or 100% fails. It is non-blocking. We are not using their helper libraries which would have created a blocking event in certain situations. And yes LayerZero is not in any way responsible for any aspects of the protocols except to deliver some bytes from one chain to another. |
So to clarify, if a LayerZero tx fails, it will not block the entire channel. Everything else will work as expected and other LayerZero tx's can continue to be sent. |
Lines of code
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L582-L652
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/module/LayerZeroModule.sol#L180-L222
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographBridge.sol#L245-L283
https://github.com/code-423n4/2022-10-holograph/blob/main/contracts/HolographOperator.sol#L484-L539
Vulnerability details
Impact
According to the LayerZero docs, the default behavior is that when a transaction on the destination application fails, the channel between the src and dst app is blocked. Before any new transactions can be executed, the failed transaction has to be retried until it succeeds.
See https://layerzero.gitbook.io/docs/faq/messaging-properties#message-ordering & https://layerzero.gitbook.io/docs/guides/advanced/nonblockinglzapp
The retry logic has to be handled by the app itself. The current implementation of the
LayerZeroModule
contract doesn't have that. Thus, if the channel gets blocked, it will be blocked forever. There's no logic to retry any failed tx.I've submitted the same bug in a previous contest: code-423n4/2022-05-velodrome-findings#83
Proof of Concept
To initiate a message from
src
todst
chain you callHolographBridge.bridgeOutRequest()
. The function is permissionless:It calls the operator's
send()
function. There's not too much happening there. Just some fee logic after which it sends the request over to theLayerZeroModule
contract:The
LayerZeroModule
then sends the request to the LayerZero endpoint:In the payload, it specifies that the target contract for LayerZero should be
LayerZeroModule
located on thedst
chain. They all share the same address. You can find the endpoint's interface here: https://layerzero.gitbook.io/docs/guides/master/how-to-send-a-messageLayerZero then calls the
lzReceive()
function. This is the place where the NonBlocking approach has to be implemented. But,LayerZeroModule
just does some input checks and routes the request to the operator:The operator doesn't put the logic in a try/catch block nor does it implement logic to retry failed requests:
As you can see, anybody can create a message through the LayerZero endpoint from
src
todst
chain. The remaining piece is to create a message that will for sure fail on the dst chain so that you can block it indefinitely. The easiest solution is to provide no tokens for gas when sending the message. The amount of gas is specified by the user when callingHolographBridge.bridgeOutRequest()
. You specify both thegasPrice
andgasLimit
. Using those parameters, theHolographOperator.send()
function determines the fees. If you setgasLimit
to0
you can initiate a tx with1 wei
for gas.It's a little difficult to provide a full PoC here because of the LayerZero dependency. I tried to make it as clear as possible what the process would look like.
Tools Used
Recommended Mitigation Steps
Implement LayerZero's non blocking approach, see https://layerzero.gitbook.io/docs/faq/messaging-properties#message-ordering & https://layerzero.gitbook.io/docs/guides/advanced/nonblockinglzapp
The text was updated successfully, but these errors were encountered: