Summary
Users using account abstraction wallets or multisig wallets won't be credited on the destination chain.
Vulnerability Details
Users with account abstraction wallets have different addresses across different chains for the same account. Also, multisigs are contracts deployed on chains and that's why destination chain address, which corresponds to source chain address won't be owned by the same person.
In the send()
function of TempleGold contract:
function send(
SendParam calldata _sendParam,
MessagingFee calldata _fee,
address _refundAddress
) external payable virtual override(IOFT, OFTCore) returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {
if (_sendParam.composeMsg.length > 0) { revert CannotCompose(); }
address _to = _sendParam.to.bytes32ToAddress();
if (msg.sender != _to) { revert ITempleGold.NonTransferrable(msg.sender, _to); }
(uint256 amountSentLD, uint256 amountReceivedLD) = _debit(
msg.sender,
_sendParam.amountLD,
_sendParam.minAmountLD,
_sendParam.dstEid
);
(bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);
msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);
oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);
emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, amountSentLD, amountReceivedLD);
}
User can only cross-chain transfer to self address.
if (msg.sender != _to) { revert ITempleGold.NonTransferrable(msg.sender, _to); }
Now, when _lzReceive()
is called on the destination chain, the same self address is credited:
function _lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address ,
bytes calldata
) internal virtual override {
address toAddress = _message.sendTo().bytes32ToAddress();
uint256 amountReceivedLD = _credit(toAddress, _toLD(_message.amountSD()), _origin.srcEid);
if (_message.isComposed()) { revert CannotCompose(); }
emit OFTReceived(_guid, _origin.srcEid, toAddress, amountReceivedLD);
}
Impact
The address may not be same on the destination chain and thus incorrect address will be credited. This is loss of funds for the message sender.
Tools Used
Manual Analysis
Relevant Github Links
send()
: https://github.com/Cyfrin/2024-07-templegold/blob/57a3e597e9199f9e9e0c26aab2123332eb19cc28/protocol/contracts/templegold/TempleGold.sol#L290
_lzReceive()
: https://github.com/Cyfrin/2024-07-templegold/blob/57a3e597e9199f9e9e0c26aab2123332eb19cc28/protocol/contracts/templegold/TempleGold.sol#L333
Recommendations
Give the user the option to pass in the address the funds should be credited to on the l2 by removing
this check in the send()
function:
if (msg.sender != _to) { revert ITempleGold.NonTransferrable(msg.sender, _to); }