# AnySwap permit exploit

By [Arsen](https://paragraph.com/@arsen-5) · 2024-11-14

---

Let’s take a look on very interesting vulnerability that actually happened quite a while, but the quality is still astonishing till our days.

### Part #1 - Vulnerable function

![](https://storage.googleapis.com/papyrus_images/3e4a895ea99854cb579d75e9bbac85d3f7b2376463dc34017cba0b80d4e09bab.png)

First and foremost goal of the “AnySwap“ was to allow users to swap between any two chains freely. It reduces fees and makes it easier to move between chains. It worked in such way:

_The router accomplishes this by wrapping the original token with an "_**_anyToken_**_." For instance, DAI is wrapped as anyDAI, making DAI the underlying asset of anyDAI. This wrapped token is utilized for internal accounting within Multichain. When a user "transfers" DAI from Ethereum to BSC, anyDAI is added to the Multichain anyDAI contract on BSC and burned (subtracted) from the anyDAI contract on Ethereum._

The previously mentioned exploited function, `anySwapOutUnderlyingWithPermit`, facilitates the swapping of an underlying token using the `ERC20 permit()` function. This function allows users to authorize a contract to spend their funds by providing a signed approval transaction without needing to submit it directly to the blockchain, thereby reducing the user's gas costs. The signed transaction is represented using the parameters (v, r, s).

![](https://storage.googleapis.com/papyrus_images/7c90a2673256610826b6585a31f63eac870c50232951b930eaa7f450af7f53d1.png)

So, if we divide the logic by peaces, we would have the following:

1.  First line extracts the address of the underlying token (e.g., "DAI") from its wrapped version (e.g., "anyDAI")
    
2.  Call the `permit` on the underlying token to `approve` to the router
    
3.  Router `safeTransferFrom` the token from the user.
    
4.  The remainder of the function handles the accounting processes associated with the wrapped version of the token and manages its transfer across different chains
    

### Part #2 - Actual exploit

It worth to mention, that this function was used only once - during actual exploit. It means that contract had some other entry-point for the users to interact with the protocol.

Now, let’s take a look on the inputs provided by the attacker and try to analyse what has happened exactly.

![](https://storage.googleapis.com/papyrus_images/b57549152f50a24f23eed128ca87e7573c0b0da2ecc6a171e83e032cb4b02ecd.png)

*   **from** is victim address
    
*   **token** is attackers’ deployed contract
    
*   **to** is destination address (attacker wallet)
    
*   **amount** is the value to drain from the respective user
    
*   **deadline** is time till the permit is valid
    
*   **v,r,s** are all zeros
    

Eventually, how did attacker managed to steal funds? Here is the most interesting part.

Attacker provided the **token**, as an attackers deployed contract. It was necessary because the AnySwap `.underlying()` method was called on it. Due to the lack of validation attacker could return any `_underlying` value. **He returned WETH address**, let’s see why.

![](https://storage.googleapis.com/papyrus_images/3c5dceccb2067c404b88b9e99cd5616d5de893e0425e4383172ffa09d6f0baf7.png)

The intended behaviour should be: user calls permit on WETH contract with `v,r,s` to approve the router the ability of withdrawing tokens. **But, the WETH token has no permit method!**

However, the WETH has the fallback function. And once the WETH can’t distinguish what function to call, since there is no `permit` method, the fallback would be called

![solidity fallback function in the old versions](https://storage.googleapis.com/papyrus_images/67d29c8839138d916835c8f543bc9d2c809abe0b92e9c9a6c7c3ba266c48024b.png)

solidity fallback function in the old versions

The function `TransferHelper.safeTransferFrom(_underlying, from, token, amount);` was originally expected to operate under the assumption that the signature verification in the preceding step was **successful**. This would imply that the approval granted by the signature could be used to transfer the specified amount from the user's account to the router. However, as observed, the signature validation did not occur as intended.

![](https://storage.googleapis.com/papyrus_images/eeacb3a15a987127fbedea2d10b2c052e33b392f7ee0f21bc6e246d5c6247c08.png)

In theory, this should not have been an issue because, although the attacker’s input should not have passed the signature check, the router would not have been approved to transfer the funds on behalf of the victim. However, the AnySwap had requested an almost infinite approval limit from all its users to save on gas fees.

Eventually, the problems were because of the following:

1.  Not useful function, which wasn’t used by the user at all (no code simplicity)
    
2.  Vulnerable function didn’t validate that the token inputed is indeed a valid AnySwap token.
    
3.  Vulnerable function didn’t validate that the permit call was actually called

---

*Originally published on [Arsen](https://paragraph.com/@arsen-5/anyswap-permit-exploit)*
