send()
and lzReceive()
dont check if addresses they are sending/minting tokens to are authorized so it is possible use them to do cross chain transfers to/from unauthorized addresses.
_update() is meant to stop transfer to or from unwhitelisted/unauthorized addreess but the check is skipped on _mint()
and _burn()
. TempleGod.send()
and TempleGold._lzReceive()
call internal OFT functions _debit()
and _credit()
to burn and mint tokens during a cross chain transfer. The _debit()
and _credit()
functions use _mint()
and _burn()
to do this. _mint()
and _burn()
call _update()
as seen here.
Because update()
skips check for if address to be minted to is authorized, TempleGold._lzReceive()
function will sucessfully mint to an unauthorized address. Also because TempleGod.send()
does not check that the _sendParam.to
address is authorized, a sucucessful cross chain message can be sent to deliver tokens to an unauthorized address on the other chain.
address A has 100 temple Gold tokens
address A gets marked unauthorized by admin and cant make any transfers via transfer()/transferFrom() on the chain.
address A decides to beat this restriction by transferring out to the second chain via cross chain transfer via send(). address A makes a cross chain message to transfers to address B (which may be authorized/unauthorized). 100 tokens is burnt from address A on local chain.
lzReceive() is called on remote chain and because address B is not checked to be authorized/unauthorized, 100 tokens is minted to address B on the remote chain.
This shows that the 100 tokens frozen have been "unfrozen". Also shows that an address marked as unauthorized to receive tokens, can still receive tokens via cross chain transfers. The fact that function send()
is open and callable by anyone makes it easy for any token holder to bypass the restriction.
In the templeGold.send()
function below we can see that the _sendParam.to
value is never checked to validate if it is authorized or not before the cross chain message is sucessfully construsted and sent.
In the _lzReceive()
function snippet below we can see there is no check for the address toAddress = _message.sendTo().bytes32ToAddress();
to confirm if it is an authorized address or not. This allows for the sucessfull mint of tokens to an unauthorized address and ensures the sucess of a token transfer, even though it is cross chain.
manual review
check the sendParam.to
and message.to
addresses in send()
and _lzReceive()
functions respectively. send()
should revert if sendParam.to
is marked unauthorized. and _lzReceive()
should not mint/fail silently if message.to
address is marked unauthorized.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.