CertiK Logo
CertiK Logo
Products
Company
incident-response
Back to all stories
Analysis Reports
Osmosis Incident Analysis
6/9/2022
Osmosis Incident Analysis

TL:DR

On June 7, 2022 Osmosis suffered a funds loss of around $5M due to a code pitfall in function MaximalExactRatioJoin that issued more LP shares than expected. Although we did audit some of the contracts from Osmosis, the file x/gamm/pool-models/internal/cfmm_common/lp.go was not part of the audit.

Event Summary

On Tuesday, June 7th, 2022, Osmosis suffered a loss of around $5M due to a code pitfall in function MaximalExactRatioJoin that is called by CalJoinPoolShares to calculate the shares based on the input amount of tokens. The flaw in the MaximalExactRatioJoin causes more LP shares to be provided to the users by CalJoinPoolShares once the users provide the liquidity by JoinPool. Essentially, the function would give 50% too many LP shares for a join. So if one should have gotten 10 LP shares, 15 would be achieved.

Osmosis announced on their social media platforms that the bug had been identified and a fix is in motion. The stolen funds have been linked to CEX accounts and that they have notified law enforcement of these exploiters. They also announced that four of the individuals who exploited Osmosis have been identified, and that two of the four will return the exploited amounts. The other two exploiters are in contact with Osmosis and waiting for further communication.

All losses will be covered. These funds will come from the strategic reserve and not the community pool, and a high rate of recovery is expected for the exploited funds. The chain is set to remain halted for at least another two days, and may be subject to change.

Attacker Addresses & Exploit Transactions

There are many transaction pairs used to exploit the project. A sample transaction is as follows. Attacker 1: Interchain Explorer by Cosmostation

Related Exploit Transactions:

Join Pool (2022-06-07 22:18:01)

Interchain Explorer by Cosmostation

Exit Pool (2022-06-07 22:18:38)

Interchain Explorer by Cosmostation

Attacker 2:

Interchain Explorer by Cosmostation

Related Exploit Transactions:

Join Pool (2022-06-07 22:49:23):

Interchain Explorer by Cosmostation

Exit Pool (2022-06-07 22:49:42):

Interchain Explorer by Cosmostation a34284

Attack Flow

The following analysis takes these two exploited transactions from Attack1 as an example.

  1. The attackers called the JoinPool() to add liquidity to the GAMM pools(in this example, it’s pool_id 678) with 29.95 USDC and 26.03 OSMO. The minted LP shares are 8.79 GAMM-678. Attack flow 1

  2. After that, the attackers called the ExitPool() from pool 678 to remove the liquidity using the previous shares.Attack flow 2

  3. Due to the miscalculation of the shares from remaining tokens, the attacker was able to withdraw the amount of tokens that are approximately 1.5 times the deposited amounts.

  4. The attackers repeated the attack flow multiple times with larger deposited token amounts to make even bigger profit.

Contracts Vulnerability

The root cause of this exploit lies in the miscalculation of shares from the remaining tokens. When users invoke the function JoinPool() in x/gamm/keeper/msg_server.go by message Msg.JoinPool,attack flow 3

the function calls function JoinPoolNoSwap() in x/gamm/keeper/pool_service.go on line 95. attack flow 4 In function JoinPoolNoSwap(), after the calculation of needed liquidity for the amount of shareOutAmount by calling function getMaximalNoSwapLPAmount() on line 192, function JoinPoolNoSwap() will call pool.JoinPool() in osmosis/x/gamm/pool-models/balancer/amm.go on line 286. attack flow 5 Function pool.JoinPool() ​​will then call function pool.CalcJoinPoolShares() on line 252, and cfmm_common.MaximalExactRatioJoin() will be called on line 283 during the execution of pool.CalcJoinPoolShares(). attack flow 6 attack flow 7 Function cfmm_common.MaximalExactRatioJoin() will calculate the usedAmount(needed liquidity) while adding liquidity, it multiplies the share ratio with the amount of tokens user put in (Line 90). However, the needed liquidity should equal to minShareRatio * total liquidity in pools. Normally, the total liquidity is larger than the the tokens one user deposited, which means that the usedAmount is less than expected. Notice that the returned value remCoins records the remaining tokens after liquidity addition. The value of remCoins is larger than expected since the usedAmount is smaller than expected. new1 After the execution of function cfmm_common.MaximalExactRatioJoin(), these remaining tokens remCoins will be used to add more liquidity to the pool on lines 300-310 by calling function pool.calSingleAssetJoin(). However, because the amount remCoins is larger than expected, more shares will be provided to users than expected. new2

Would we spot the issue during the audit?

Yes, we would find out the miscalculation of the shares from remaining tokens. The file x/gamm/pool-models/internal/cfmm_common/lp.go was not included in the files we audited.

Reference:

Osmosis GitHub codebase: GitHub - osmosis-labs/osmosis: The AMM Laboratory Osmosis project: Trade Twitter: ​​ Junønaut on Twitter Osmosis 🧪 on Twitter
reveddit