-
Notifications
You must be signed in to change notification settings - Fork 2
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
0xadrii - Recursive _lzCompose() call can be leveraged to steal all generated USDO fees #113
Comments
Dupe of #111 |
Escalate The vulnerability detailed in this issue is not related to the issue of passing a wrong parameter as the source chain sender when the The problem with the vulnerability reported in this issue is that // TapiocaOmnichainReceiver.sol
...
if (nextMsg_.length > 0) {
_lzCompose(address(this), _guid, nextMsg_); // <---- `address(this)` is wrong
} This will make the next compose call have Although this fix mitigates the possibility of draining any account that is passed as the As shown in my PoC, an attacker can then burn all USDO fees held in the USDO contract on chain B, and transfer them to an arbitrary address in chain A, effectively stealing all fees sitting in the USDO contract. |
You've created a valid escalation! To remove the escalation from consideration: Delete your comment. You may delete or edit your escalation comment anytime before the 48-hour escalation window closes. After that, the escalation becomes final. |
This seems like a duplicate of #135, will need to review further. They are all very similar to each other. |
I agree with the escalations and @nevillehuang comment. We can deduplicate from #111 and duplicate with #135. |
Result: |
Escalations have been resolved successfully! Escalation status:
|
0xadrii
high
Recursive _lzCompose() call can be leveraged to steal all generated USDO fees
Summary
It is possible to steal all generated USDO fees by leveraging the recursive _lzCompose() call triggered in compose calls.
Vulnerability Detail
The
USDOFlashloanHelper
contract allows users to take USDO flash loans. When a user takes a flash loan some fees will be enforced and transferred to the USDO contract:Such fees can be later retrieved by the owner of the USDO contract via the
extractFees()
function:However, such fees can be stolen by an attacker by leveraging a wrong parameter set when performing a compose call.
When a compose call is triggered, the internal
_lzCompose()
call will be triggered. This call will check themsgType_
and execute some logic according to the type of message requested. After executing the corresponding logic, it will be checked if there is an additional message by checking thenextMsg_.length
. If the compose call had a next message to be called, a recursive call will be triggered and_lzCompose()
will be called again:As we can see in the code snippet’s last line, if
nextMsg_.length > 0
an additional compose call can be triggered . The problem with this call is that the first parameter in the_lzCompose()
call is hardcoded to beaddress(this)
(address of USDO), making thesrcChainSender_
become the USDO address in the recursive compose call.An attacker can then leverage the remote transfer logic in order to steal all the USDO tokens held in the USDO contract (mainly fees generated by flash loans).
Forcing the recursive call to be a remote transfer,
_remoteTransferReceiver()
will be called. Because the source chain sender in the recursive call is the USDO contract, theowner
parameter in the remote transfer (the address from which the remote transfer tokens are burnt) can also be set to the USDO address, making the allowance check in the_internalTransferWithAllowance()
call be bypassed, and effectively burning a desired amount from USDO.After burning the tokens from USDO, the remote transfer will trigger a call to a destination chain to mint the burnt tokens in the origin chain. The receiver of the tokens can be different from the address whose tokens were burnt, so an attacker can obtain the minted tokens in the destination chain, effectively stealing all USDO balance from the USDO contract.
An example attack path would be:
nextMsg
), which is the actual compose message that will trigger the remote transfer and burn the tokens in chain B, and finally trigger a call back to chain A to mint he tokensProof of concept
The following proof of concept illustrates how the mentioned attack can take place. In order to execute the PoC, the following steps must be performed:
EnpointMock.sol
file inside thetest
folder insideTapioca-bar
and paste the following code (the current tests are too complex, this imitates LZ’s endpoint contracts and reduces the poc’s complexity):Usdo.t.sol
fileUsdo.sol
’s implementation so that the endpoint variable is not immutable and add asetEndpoint()
function so that the endpoint configured insetUp()
can be chainged to the newly deployed endpointsUsdo.t.sol
:Run the poc with the following command:
forge test --mt testVuln_USDOBorrowFeesCanBeDrained
The proof of concept shows how in the end, USDO’s
bUsdo
balance will become 0, while the same amount ofaUsdo
in chain A will be minted to the attacker.Impact
High, all fees generated by the USDO contract can be effectively stolen by the attacker
Code Snippet
https://github.com/sherlock-audit/2024-02-tapioca/blob/main/Tapioca-bar/gitmodule/tapioca-periph/contracts/tapiocaOmnichainEngine/TapiocaOmnichainReceiver.sol#L182
Tool used
Manual Review, foundry
Recommendation
Ensure that the
_lzCompose()
call triggered when a_nextMsg
exists keeps a consistent source chain sender address, instead of hardcoding it toaddress(this)
:The text was updated successfully, but these errors were encountered: