MoveBit

May 16, 2024

Bluefin Vulnerabilities Explanation

Bluefin-Vulnerabilities-Explanation

Overview of the Bluefin Audit Contest

On February 13, 2024, Bluefin (Bluefin is a decentralized perpetual trading platform powered by the Sui blockchain) hosted the first-ever audit contest targeting Move smart contracts on the Hackenproof platform. Two security researchers from Movebit actively participated in this contest and achieved satisfactory results.

Overall, the audit revealed 2 high-risk, 3 medium-risk, and several low-risk and informational-level issues. Among them, Movebit identified 1 high-risk, 3 medium-risk, as well as several low-risk and informational-level issues during the audit.

Movebit-participated-in-HackenProof-contest-and-achieved-amazing-results

About Bluefin

Bluefin is a decentralized order book exchange designed for both professional traders and first-time traders, focusing on security, transparency, and redefining the user experience with on-chain trading platforms. Powered by the Sui blockchain, Bluefin ensures sub-second completion of trades and reflects them instantly on their user interface, providing users with high-performance trading while bridging the experience between Web2 and Web3. Bluefin will continue to leverage the best available technology to drive excellence in performance, security, and transparency, combining a wallet-less trading experience backed by leading companies such as Polychain, SIG, and Brevan Howard.

Bluefin

Analysis of Bluefin Security Issues

Bluefin is dedicated to providing users with a fast and secure trading experience. However, in the pursuit of this goal, it also faces some potential security vulnerabilities and technical challenges. In the following sections, we will conduct a detailed analysis of the security issues found in the Bluefin platform to take appropriate improvement measures, ensuring that users’ funds are adequately protected and thereby constructing a more secure and trustworthy trading environment.

1. Share Manipulation

Bluefin provides an additional bluefin_vault module that allows users to interact with vaults. Among them, the deposit_to_vault function allows users to deposit funds into vaults and receive a certain amount of shares, while the withdraw_from_vault function allows users to destroy a certain amount of shares to redeem their funds. The design of this module is similar to Ethereum’s ERC4626 model, which is a standard for tokenized treasuries.

However, Bluefin’s current deposit and withdrawal functions suffer from the classic share manipulation vulnerability found in the ERC4626 model. Let’s take a look at the section of the bluefin_vault.deposit_to_vault function regarding share calculation.

Bluefin-Share-Manipulation-1

As shown in the code snippet at line 248, the user’s share quantity is calculated as follows: the number of tokens deposited by the user multiplied by the total number of shares in the current Vault divided by the total balance of tokens in the Vault. Then, as shown in the code snippet at line 275, the user’s funds are recharged into the margin_bank through the deposit_to_bank function of the margin_bank.

Bluefin-Share-Manipulation-2

When a user first deposits funds, both vault_total_balance and total_shares are 0. At this point, the user’s share quantity equals the amount they deposit, i.e., shares = amount. This provides an opportunity for attackers to manipulate shares. It’s worth noting that there’s a check in place to ensure that shares must be greater than 0, which mitigates share manipulation attacks to some extent, but this defense measure is still insufficient.

Let’s describe the entire process of a share manipulation attack using an example involving user Alice and attacker Bob:

  1. Suppose the deposited token is SUI. Initially, the attacker deposited 1 MIST to obtain 1 share.

  2. User Alice attempts to deposit 2 SUI into the contract. If Alice tries to deposit at this point, she will receive (1 share * 2 SUI) / 1 MIST = 2e9 shares.

  3. However, before Alice’s transaction, attacker Bob directly transfers 1 SUI via the margin_bank::deposit_to_bank function.

  4. This results in Alice’s current deposited share calculation as (1 share * 2 SUI) / (1 SUI + 1 MIST).

  5. Due to truncation, the calculated result is 1 share, passing the aforementioned check that shares must be greater than 0.

  6. Finally, attacker Bob withdraws SUI via the withdraw_from_vault function. 1 share is now worth approximately 1.5 SUI because the contract holds 3 SUI + 1 MIST, but the contract has only minted 2 shares.

To address the aforementioned share manipulation issue, several solutions can be considered:

  • Revert if the amount received is not within a slippage tolerance (Add slip protection if possible).

  • The deployer should deposit enough assets into the vault such that doing this inflation attack would be too expensive.

  • Add virtual liquidity to the vault so the pricing behaves as if the vault had been deployed with enough assets.

2. Improper Permission Control

Bluefin invokes the validate_unique_tx function for each transaction to verify its uniqueness, such as trade, liquidate, deposit_to_bank, withdraw_from_bank, etc. Below is the process of creating a transaction hash, ensuring randomness through the object::new(ctx) shown in the code snippet at line 513.

Bluefin-Improper-Permission-Control-1

Bluefin verifies the uniqueness of each transaction by passing in a transaction hash, as shown in the code snippet below.

Bluefin-Improper-Permission-Control-2

The validate_unique_tx function plays a crucial role in ensuring the uniqueness of the tx_hash for each transaction within the system and relies on the continuously growing sequencer. However, the function lacks permission control and is publicly visible, meaning anyone can pass in any tx_hash and store it in the sequencer. This could lead to several potential issues:

  • Effective resource management and prevention of unrestricted execution are crucial for maintaining protocol security and gas efficiency. Attackers can freely transmit garbage data to the sequencer, increasing its capacity and causing gas consumption for transactions referencing the sequencer to increase. Sui has some limitations on transactions and the data they use, such as maximum size and the number of objects used. If the size of the sequencer grows indefinitely, it could lead to DoS attacks, rendering transactions unable to execute and funds unable to be withdrawn.

  • Malicious attackers can front-run by prematurely passing tx_hash to the sequencer, causing normal transactions to fail because the transaction hash already exists in the sequencer.

  • If the frontend relies on tx_hash to parse transactions, it may encounter issues such as transactions being unable to be parsed.

To prevent these attacks, it is recommended to add permission control to the validate_unique_tx function or change its visibility to friend.

3. The issue with Oracle Price Handling

1) Oracle does not check outdated prices

In the Bluefin exchange contract, many functions such as trade, liquidate, deleverage, etc., call the perpetual.update_oracle_price function to fetch prices from Pyth and update them in the perpetual contract for subsequent calculations and validation.

As we know, Pyth is an oracle providing real-time prices to the chain. On Sui, Pyth updates the latest prices every few minutes.

As shown in the code snippet at line 217, the protocol calls the pyth.get_price_unsafe function to fetch prices from Pyth.

Bluefin-Oracle-does-not-check-outdated-prices-1

Let’s take a look at the get_price_unsafe code together. As shown in lines 379 to 384 below, Pyth’s official documentation explicitly states that the function’s returned price is not guaranteed to be the latest. If the latest available price is needed, the get_price_no_older_than function should be used to fetch the price not later than the given time.

Bluefin-Oracle-does-not-check-outdated-prices-2

Therefore, using pyth.get_price_unsafe in the protocol may result in obtaining outdated prices, leading to inaccurate subsequent calculations and validation, thereby affecting the normal operation of the protocol.

2) Oracle does not check for zero values

Similarly, in the process of fetching prices from Pyth as described above, the protocol did not consider the scenario where the price is zero. Let’s take a look at how Pyth handles positive and negative values for prices. Since Move does not support native negative numbers, Pyth adds an additional field called negative to represent positive and negative values. The value 0 is represented as (0, false), meaning that 0 is non-negative, indicating that in Pyth, a price of 0 is considered positive.
Bluefin-Oracle-does-not-check-zero-values-1

Currently, the protocol checks whether the obtained price is non-negative using get_magnitude_if_positive, but it does not consider the case where the price is zero, meaning that we can obtain a value of 0 as a price. Additionally, Pyth may also return a price of 0. Referencing the diagram below:

Bluefin-Oracle-does-not-check-zero-values-2

To prevent Pyth from returning a price of 0, it is recommended to add a check for non-zero values for prices.

4. Dangerous Single-Step Ownership Transfer

Within the current Bluefin protocol, there are several instances of ownership transfer operations, such as changing the exchange’s administrator permissions. Administrators play a critical role in the current protocol, such as setting guardians for the exchange and designating operators with certain permissions. However, the existing implementation of administrator permission transfer poses potential security risks. The current implementation directly transfers the management of the exchange to a new address, as shown in the diagram below. While this method is straightforward, it carries risks in the event of specifying an incorrect address or malicious takeover. The single-step ownership transfer process lacks validation or confirmation steps for the new administrator, which could result in unintentionally losing control of the exchange’s management.

Bluefin-Dangerous-Single-Step-Ownership-Transfer

To mitigate these risks, it is recommended to adopt a two-step ownership transfer mechanism to manage the exchange’s administration. This mechanism involves two steps: the current administrator proposes the transfer and the new administrator confirms acceptance. Through this process, it can be ensured that the new administrator is willing and able to accept the management role, providing an additional layer of security and error prevention.

The implementation steps are as follows:

  1. Ownership Transfer Proposal: The current administrator calls a method to propose a new administrator. This operation records the proposed new administrator’s address in the contract state but does not immediately transfer the rights.

  2. Ownership Acceptance: The proposed new administrator must explicitly accept the role by calling a separate contract method. Only after this operation is executed, the management permissions will be transferred.

5. Failure to Distinguish between Limit Orders and Market Orders

In the current Bluefin trading platform, there are two common order types: limit orders and market orders. In the evaluator contract, as shown in the code snippet at lines 214-233 below, the set_max_qty_limit function is used to set the maximum transaction quantity for limit orders, while the set_max_qty_market function sets the maximum transaction quantity for market orders.

Bluefin-Failure-to-Distinguish-between-Limit-Orders-and-Market-Orders-1

However, when checking the transaction quantity, there is no distinction between limit orders and market orders. The verify_min_max_qty_checks function is used to verify that the transaction quantity must be greater than the minimum and less than the maximum. However, this function simultaneously checks both the maximum transaction quantity for limit orders and market orders when executing transaction orders. This results in the maximum quantity check for market orders also being applied to limit orders, which is unreasonable.

Bluefin-Failure-to-Distinguish-between-Limit-Orders-and-Market-Orders-2

To improve this situation, it is recommended to differentiate between limit orders and market orders in the verify_min_max_qty_checks function and perform the corresponding maximum transaction quantity checks separately. This ensures that correct quantity checks are applied to different types of orders, enhancing the security and effectiveness of the trading platform.

Summary

Through a thorough analysis of the issues identified during the Bluefin audit contest, we have proposed solutions and improvement suggestions for each issue to ensure that the Bluefin platform can provide a secure and efficient trading experience. Each issue has been carefully analyzed here, and corresponding strategies have been proposed. With these improvements, we believe that Bluefin will be able to further solidify its position in the decentralized perpetual trading field and provide users with a safer and more reliable trading environment.

About MoveBit

MoveBit, a subsidiary brand of BitsLab, is a blockchain security team that specializes in the Move ecosystem. It’s at the forefront of utilizing cutting-edge Formal Verification techniques. The team comprises security professionals with extensive experience in both academia and enterprise. As one of the earliest contributors to the Move ecosystem, MoveBit has collaborated closely with Move developers to establish security standards for secure Move applications, making it the most secure Web3 destination.

Requests a quote

OLDER > < NEWER