Skip to content

Instantly share code, notes, and snippets.

@jstarry
Created November 26, 2021 22:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jstarry/ab03017fa05b7352261115c9bd5cfc4e to your computer and use it in GitHub Desktop.
Save jstarry/ab03017fa05b7352261115c9bd5cfc4e to your computer and use it in GitHub Desktop.
Mango v3 signer bug

Mango Markets V3 Program - Spoof open orders account

The mango v3 program has an instruction which allows users to setup new “spot open orders” (SOO) accounts which will be used by the mango v3 program to place orders with the serum dex program on behalf of the user. However, there was no restriction that the initialized open orders account is actually “owned” by a mango v3 PDA (program derived address), so a user could initialize an SOO account with their own signer as the owner. This bug was not obvious because the Solana program sdk's invoke_signed method silently ignores signer slices which are not used by any accounts passed to the invocation. This usability issue is now raised in an open Github issue here: solana-labs/solana#21409.

Let's now examine why the ability to create a user-owned SOO account is dangerous. In addition to being used spot trading on Serum, spot open order (SOO) accounts are also used by mango to calculate the total amount of assets a user can use as collateral when borrowing funds from the protocol. The idea here is that if a user has placed an order to buy 1 BTC for 56,000 USDC, they are good for either 56,000 USDC or for 1 BTC if the order is filled. Since SOO accounts should always be controlled by the mango protocol, they can safely be used as collateral because mango liquidators could either cancel the order or settle the funds during liquidation. So to recap, SOO accounts are used to determine how much a user can borrow, if those accounts are not owned by the mango protocol or if they are spoofed in a malicious way, users could potentially borrow funds that they're not good for. In this exploit, we will first create a user-owned SOO account and then trick the protocol into thinking that SOO account has a lot of collateral which can be borrowed against for withdraws.

Tricking the mango protocol was not easy. When SOO accounts are used during withdraws, the protocol first checks if the corresponding market for the SOO (e.g. BTC / USDC) is in the user’s "margin basket." A market will only be added to the user's margin basket if they have an active SOO for that market. This means that the mango program must have detected at some point that the user’s SOO for a given market has been either used to place an order or has a non-zero value of orders in it. Most mango instructions won’t allow a SOO to be passed to the serum dex program without first checking that the owner of the account is a mango program PDA. However, the mango program’s “settle funds” instruction did not have this restriction. So the next step for the exploit is to first use the SOO account directly with the serum dex program to place a non-zero order, and then call the mango "settle funds" instruction to update the user account's margin basket.

The last step is for the user to cancel their order, close the SOO account, and then recreate it with spoofed data. If they create a spoofed account at the same address, the mango program will blindly trust the data in that account after checking a few constraints. If the spoofed account has a valid open orders account layout with the owner field set to the mango program PDA, the mango program will go ahead and use the values in the open orders account as truth. To maximize effectiveness of the exploit, the attacker may set the amount of the order to be the maximum integer. After spoofing the account, the attacker can pass that fake SOO account to the mango program withdraw instruction. The mango program will allow the attacker to withdraw the entire bank vault because it believes that the user has an extremely high amount of collateral from the SOO account state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment