TempleGold

TempleDAO
Foundry
25,000 USDC
View results
Submission Details
Severity: high
Valid

Transferring TGLD _cross-chain_ would end up exposing some users to attackers to steal their tokens

Summary

Transferring TGLD cross-chain exposes users to potential token theft due to the wrong assumption that they control the same address on multiple EVM-compatible chains. This vulnerability, similar to the Wintermute incident, can result in attackers controlling the destination address and stealing the TGLD tokens from honest users.

Vulnerability Details

First from the contest's readme we can see that protocol is to be deployed on any EVM compatible chain, i.e https://github.com/Cyfrin/2024-07-templegold/blob/a47602635bf55221404fc9ceb96a8bb6c0db36a2/README.md#L156-L158

Compatibilities:
Blockchains:
- Ethereum/Any EVM

Now, from the temple gold readMe we can see that the below code/road path has been hinted for the transfers of the TGLD token https://github.com/Cyfrin/2024-07-templegold/blob/a47602635bf55221404fc9ceb96a8bb6c0db36a2/protocol/contracts/templegold/README.md#L11-L12

The mint source chain of Temple Gold is `Arbitrum One`. With layer zero integration, Temple Gold can be transferred cross-chain. A holder can only transfer cross-chain to their own address.

This however would mean that some users would lose their tokens cause protocol have claimed to them that they own the same address on multiple EVM chains, which would not be the case for all users.

Note that this bug case is very similar to the Wintermute case from two summers back, where 20 million worth of OP tokens were lost ~$27.6M as at the time of the incident... source.

As explained in the rekt article tagged above, it is possible to gain control of the same address for contract accounts in a different chain, especially for those contract accounts that are deployed using the Gnosis Safe contracts:

So in the case the _address hardcoded (sender) _to receive the tokens on the other chain (any EVM compatible chain) is not controlled by the same sender of the tokens this means that the users tokens is effectively lost.

See how the send is being finalised https://github.com/Cyfrin/2024-07-templegold/blob/a47602635bf55221404fc9ceb96a8bb6c0db36a2/protocol/contracts/templegold/TempleGold.sol#L281-L311

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(); }
/// cast bytes32 to address
address _to = _sendParam.to.bytes32ToAddress();
/// @dev user can cross-chain transfer to self
//@audit
if (msg.sender != _to) { revert ITempleGold.NonTransferrable(msg.sender, _to); }
// @dev Applies the token transfers regarding this send() operation.
// - amountSentLD is the amount in local decimals that was ACTUALLY sent/debited from the sender.
// - amountReceivedLD is the amount in local decimals that will be received/credited to the recipient on the remote OFT instance.
(uint256 amountSentLD, uint256 amountReceivedLD) = _debit(
msg.sender,
_sendParam.amountLD,
_sendParam.minAmountLD,
_sendParam.dstEid
);
// @dev Builds the options and OFT message to quote in the endpoint.
(bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);
// @dev Sends the message to the LayerZero endpoint and returns the LayerZero msg receipt.
msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);
// @dev Formulate the OFT receipt.
oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);
emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, amountSentLD, amountReceivedLD);
}

As hinted by the @audit tag, users can only end up transferring their tokens cross-chain to the same address so to go into more details as to how the bug case would actualise, suppose that a user uses a MultiSigWallet contract to teleport their TGLD tokens, in the TempleGold contract, the address of the MultisigWallet will be used and all the TGLD that were bridged will be deposited to this MultiSigWallet on the receiving chain effectively having the real user lose out on their funds and the attacker stealing unearned TGLD tokens.

Now, the problem is that the address of the MultisigWallet, might not be controlled by the same user on a different chain, as has happened in the past and as explained by this article from rekt: https://rekt.news/wintermute-rekt. So for example, in Polygon, an attacker could gain control of the address of the same address of the MultisigWallet that was used to teleport the tokens from the original chain. So the attacker having gained control of this address, i.e the MultisigWallet that deposited tokens from the original chain in the Polygon Branch having them take control of the funds.

Impact

As hinted under Vulnerability Details, since users are given the assumption that they always own the same address on the receiving chain where they want to cross-chain their tokens to, then this could lead to them effectively losing their TGLD tokens, since they don't have access to this address on the receiving chain, and the likelihood of this bug case is quite higher than expected, considering Temple has plans in place to be deployed on any EVM compatible chain as hinted in the readMe/docs.

Tools Used

Recommendations

Consider integration a receiving address when the cross chain is to occur and remove the enforcal that the to is not exactly the same as the msg,sender calling the function, i.e
https://github.com/Cyfrin/2024-07-templegold/blob/a47602635bf55221404fc9ceb96a8bb6c0db36a2/protocol/contracts/templegold/TempleGold.sol#L281-L301

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(); }
/// cast bytes32 to address
address _to = _sendParam.to.bytes32ToAddress();
/// @dev user can cross-chain transfer to self
- if (msg.sender != _to) { revert ITempleGold.NonTransferrable(msg.sender, _to); }
// @dev Applies the token transfers regarding this send() operation.
// - amountSentLD is the amount in local decimals that was ACTUALLY sent/debited from the sender.
// - amountReceivedLD is the amount in local decimals that will be received/credited to the recipient on the remote OFT instance.
(uint256 amountSentLD, uint256 amountReceivedLD) = _debit(
msg.sender,
_sendParam.amountLD,
_sendParam.minAmountLD,
_sendParam.dstEid
);
Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Account abstraction, Multisig, Any other contract based solution that doesn't share the same address across chains will lose it's TGLD in teleport.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.