Closed sherlock-admin4 closed 4 months ago
Escalate I believe this issue should clearly be a high and not a medium. Both this report as well as issue 69 clearly show how a substantial loss of funds is possible. As per Sherlock's criteria to identify a high issue, issues must be considered of high impact if a "Definite loss of funds without (extensive) limitations of external conditions." takes place. For this specific issue, no external conditions or limitations exist, the issue will always be present in the contract and all the ETH can effectively be stolen when a rebalance is triggered for mtOFTs.
Escalate I believe this issue should clearly be a high and not a medium. Both this report as well as issue 69 clearly show how a substantial loss of funds is possible. As per Sherlock's criteria to identify a high issue, issues must be considered of high impact if a "Definite loss of funds without (extensive) limitations of external conditions." takes place. For this specific issue, no external conditions or limitations exist, the issue will always be present in the contract and all the ETH can effectively be stolen when a rebalance is triggered for mtOFTs.
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.
@0xadrii From discussion in #69, for the planned stargateComposer to be used, wouldn't it simply cause an revert?
As windhustler mentions in this comment, the StargateComposer contract does not have a swapETH()
function (it rather has a swapETHAndCall()
function), so the stargate composer contract can't be the one that was expected to be used.
The tests also clearly show that the intention was to use the ETH router (forking with mainnet address 0x150f94b44927f078737562f0fcf3c95c01cc2376) rather than using the Stargate composer, so it is clear that the intention to handle native ETH was to use the ETH router.
Setting the ETH router as the contract, the functions won't revert, and the cross-chain call will actually take place. However, as mentioned in my report and windhustler's, because sgReceive()
is never called on the destination chain the transferred ETH will remain stuck in the wrong contract (mtOFT
instead of the vault), and users can drain the bridged ETH by leveraging the fact that the wrap()
function does not check that the _amount
passed as parameter is equal to msg.value
, so users can wrap the stuck ETH as if it was theirs.
@0xadrii Fair enough, I think high severity is warranted. Thanks for the detailed escalation
I agree with the escalation. It meets the criteria of High severity.
Planning to accept the escalation and make this issue a valid High.
Result: High Duplicate of #69
0xadrii
high
Wrong usage of Stargate’s ETH router in balancer enables attackers to steal all bridged native funds
Summary
Using Stargate's ETH router will break rebalancing interactions when bridging native assets, making it possible for attackers to steal the native bridged funds.
Vulnerability Detail
Tapioca employs a rebalancer contract that constantly reweights the backing of mtOFT's across the mtOFT's supported networks using Stargate. When a
rebalance()
is triggered, themtOFT
underlying token will be router via Stargate to the destination chainmtOFT
.This vulnerability describes an issue that arises when bridging native tokens via Stargate’s ETH router.
Currently, underlying assets wrapped via
mtOFTs
are not stored in themtOFT
contract itself. Instead, they are deposited into an externalTOFTVault
contract. When assets are wrapped, they are transferred directly to the vault. Similarly, in order to unwrap assets they first need to be retrieved from the vault:When a rebalance takes place in
Balancer.sol
, the following steps take place:ITOFT(_srcOft).extractUnderlying(_amount);
mtOFT
contract, not directly to the vault.Because the second step does not directly swap and transfer assets into the vault, a last step is required in the destination chain so that the bridged assets can be actually sent to the vault. This is done via
sgReceive()
, which is a function that should be automatically called by Stargate’s router during the swap flow:The problem with the current implementation is that Stargate’s ETH router will NOT call
sgReceive()
during a swap call. This is because Stargate’s ETH router always assumes that the receiver in the destination chain is an EOA.Proof of concept
Let’s examine how a Stargate swap flow works:
Tapioca's
Balancer
contract calls the router/ETH router’s swap()/swapETH() function. IfswapETH()
is called, the ETH router contract will internally call the regular router’sswap()
function. It is crucial to note how the last field when callingswap()
insideswapETH()
is left with empty bytes (””
), and a comment is left that reads: “empty payload, since sending to an EOA”.Stargate’s bridge
swap()
function will be called. The bridge appends some extra data to the call, and forces the message to be of typeTYPE_SWAP_REMOTE
. Then, LayerZero’s endpoint will be triggered to deliver the message to the destination chain.In the destination chain, Stargate’s bridge
lzReceive()
function will be triggered. Because the message sent via the swap is of typeTYPE_SWAP_REMOTE
, the regular Stargate’s routerswapRemote()
function will be called. Note how a lastpayload
parameter is passed. This is the parameter that was left empty in the origin chain when ETH router called the regular router’sswap()
:Finally, the regular router’s
swapRemote()
is called, which will call the internal_swapRemote()
function. Inside this function, two important steps will take place:_to
( in Tapioca’s flow it will be themtOFT
contract)._payload.length > 0
, then destination address_to
sgReceive()
function will be called. This is the step that should be called in order to deposit the assets from themtOFT
to theTOFTVault
. However, this step will never be triggered when routing native assets, because as we saw in the first step, Stargate’s ETH router will hardcode the payload to be empty, making the_payload.length
be 0, and preventing themtOFT’s
sgReceive()
function from being called.Because
sgReceive()
is never called, bridged nativeassets will remain stuck in themtOFT
contract when received in the destination chain. Then, an attacker can use thewrap()
function in order to steal all the idle native assets stuck in the contract.Because inside the
wrap()
mtOFT
function themsg.value
is not checked against the_amount
that is actually passed as parameter, an attacker can pass a smallmsg.value
amount enough to cover the wrapping fees but pass an_amount
parameter equal to the amount of idle native assets, effectively wrapping and getting minted all the stuck bridged amount of native assets.Impact
High. All native funds bridged via the Balancer contract wan be stolen by an attacker.
Code Snippet
https://github.com/sherlock-audit/2024-02-tapioca/blob/main/TapiocaZ/contracts/Balancer.sol#L272-L278
Tool used
Manual Review
Recommendation
One possible solution is to use the regular router to bridge native assets, and wrap them to their wrapped version prior to interacting with the router.
On the other hand, there are some chains where Stargate has deployed ETH routers that allow custom payloads These routers incorporate a
swapETHAndCall()
function that allows to directly interact with the native asset and does not hardcode the payload to be an empty string. Although limited to some specific chains, using these routers could be another solution.Duplicate of #69