Inconsistent AccountDepositSol struct in ILedger.sol and msd_codec.rs
Summary
The protocol uses LayerZero to do cross network communication. Users can deposit on the Solana vault, and it will be forwarded through LZ, eventually landing Ethereum's SolConnector. However, during message decoding process, there is a discrepancy between sent and received struct, which can cause unexpected value to be parsed or DoS.
Root Cause
In _lzReceive, the deposit message will be decoded and forwarded to ledger contract:
function decodeLzMsg(bytes calldata _msg) internal pure returns (uint8 msgType, bytes memory payload) {
msgType = uint8(bytes1(_msg[:MSG_TYPE_OFFSET]));
payload = _msg[MSG_TYPE_OFFSET:];
}
Which takes the first byte of message, checking if it's deposit type, and unmarshal the rest into AccountDepositSol. On the other hand, programs on Solana offers a method deposit, and it does similar job as SolConnector::withdraw:
Which the deposit params, will be first encoded in VaultDepositParams::encode, then combined with message type, altogether encoded again by LzMessage::encode, and sent to LZ endpoint. And in VaultDepositParams::encode:
From the above function, we see from the struct, it should be encoded into a (32 * 7) = 224 bytes of data blob. For reference, here is the struct for AccountDepositSol, which will be decoded into:
There is a discrepancy between the struct being encoded and decoded, and more likely, when such message is received by SolConnector, it will revert, as the unmarshal will fail.
Internal pre-conditions
No response
External pre-conditions
No response
Attack Path
No response
Impact
deposit function from LZ will fail due to discrepancy on structs, since for such message to send, tokens will be transferred first, but if the LZ message fails, those tokens will be lost.
Dizzy Green Mantis
High
Inconsistent
AccountDepositSol
struct inILedger.sol
andmsd_codec.rs
Summary
The protocol uses LayerZero to do cross network communication. Users can deposit on the Solana vault, and it will be forwarded through LZ, eventually landing Ethereum's
SolConnector
. However, during message decoding process, there is a discrepancy between sent and received struct, which can cause unexpected value to be parsed or DoS.Root Cause
In
_lzReceive
, the deposit message will be decoded and forwarded to ledger contract:and here is the decode function:
Which takes the first byte of message, checking if it's deposit type, and unmarshal the rest into
AccountDepositSol
. On the other hand, programs on Solana offers a methoddeposit
, and it does similar job asSolConnector::withdraw
:Which the deposit params, will be first encoded in
VaultDepositParams::encode
, then combined with message type, altogether encoded again byLzMessage::encode
, and sent to LZ endpoint. And inVaultDepositParams::encode
:From the above function, we see from the struct, it should be encoded into a (32 * 7) = 224 bytes of data blob. For reference, here is the struct for
AccountDepositSol
, which will be decoded into:There is a discrepancy between the struct being encoded and decoded, and more likely, when such message is received by
SolConnector
, it will revert, as the unmarshal will fail.Internal pre-conditions
No response
External pre-conditions
No response
Attack Path
No response
Impact
deposit
function from LZ will fail due to discrepancy on structs, since for such message to send, tokens will be transferred first, but if the LZ message fails, those tokens will be lost.PoC
No response
Mitigation
Adjust the struct, so that they align.