Mike Shultz 2021-02-22.
Primitive Finance is a DeFi project that is tokenizing options trading built upon the Uniswap interface. Currently they offer options trading on WETH and SUSHI using DAI as the strike asset.
On Feb 20th, a vulnerability was discovered by Dedaub and reported through Immunefi. Together with the Primitive team, they decided to prepare a whitehat attack to save user funds. The contracts were immutable and unpausable, leaving no other options for remediation.
The attack uses Uniswap flash-swaps to call one of their core contracts (a "connector") with specially crafted attack contracts that mimic the expected Primitive options contracts. The connector uses information from these contracts to make decisions on how to transfer tokens that were previously approved by users.
Primitive contracts have previously been through an audit by OpenZeppelin that covered the Primitives
, Option
, Redeem
, and Trader
contracts. The referenced repository and commit in this audit appear to have been renamed and undergone a restructuring since the audit.
The audit did not include the Connector contracts that were the target for the exploit.
- Attack transactions
- Primitive's Attack Post-Mortem
The attack requires some reconnaissance ahead of time. You must know ahead of time what tokens the connector has been approved for. With that information, you will know how much you can siphon from the victim accounts.
- Create a fake token ("FAKE")
- Create a malicious Option contract that uses the real token ("REAL") and FAKE as strike
- Create a Uniswap pair for REAL-FAKE swaps
- Start a flash swap for the amount of REAL that the connector has been approved for that calls
flashMintShortOptionsThenSwap()
with the malicious Uniswap pair and options addresses. - Pass the REAL tokens of the flash swap to the Option contract, minting malicious option tokens("MOPT"). The malicious Option contract then transfers REAL tokens to attacker
- Transfer MOPT to victim
- Settle flash swap by paying the malicious pair with the victim's funds
- Remove liquidity from REAL-FAKE Uniswap pair
The result is that the attacker now have the victim's REAL, and they have worthless MOPT that cannot be redeemed for FAKE, which has no value.
As of this writing, there appears to be no fix in place. Their published future plans include:
- Strict approvals (no more infinite approvals)
- Use
permit()
for all tokens that support it - Add pausability to contracts
- "Update the frontend with new tools for users to interact with the option markets, until a new Connector contract is deployed."
No further mention of how they might fix the connector, or if they will.
This timeline is a direct copy from Primtive's own post-mortem:
- 15:30 UTC Feb 20: Dedaub team confirms critical vulnerability by exploiting a contract in a test environment.
- 16:30 UTC Feb 20: Yannis Smaragdakis at Dedaub discloses the critical vulnerability to Mitchell Amador at Immunefi.
- 17:00 UTC Feb 20: Immunefi team confirms the vulnerability.
- 17:40 UTC Feb 20: Primitive Finance confirms receipt of vulnerability from Immunefi.
- 17:45 UTC Feb 20: Primitive Finance engages Emiliano (ReviewsDAO).
- 17:50 UTC Feb 20: Primitive Finance war room created with Primitive Finance, Dedaub, ReviewsDAO, and Immunefi teams.
- 17:53 UTC Feb 20: War room assembles, begins preparing whitehat hack and operations.
- 18:15 UTC Feb 20: Primitive Frontend updated with all buttons calibrated to reset approvals to 0 wei, to prevent new wallets becoming vulnerable.
- 20:45 UTC Feb 20: Scope of the vulnerable wallets and funds at risk confirmed, with the help of Jon Itzler’s Dune Analytics queries.
- 17:17 UTC Feb 21: Whitehack contracts prepared, code review begins.
- 19:45 UTC Feb 21: Alice Henshaw from Open Zeppelin joins war room to offer additional support.
- 22:18 UTC Feb 21: War room re-assembles to initiate whitehack.
- 00:19 UTC Feb 22: Staging complete, attack is ready.
- 00:41 UTC Feb 22: Pre-emptive reach out to known address holders to reset allowances, exposed funds reduce by 35%.
- 01:06 UTC Feb 22: Primitive Team executes first whitehat attack, rescuing first wallet.
- 01:12 UTC Feb 22: Primitive Team executes second whitehat attack, rescuing second wallet.
- 01:14 UTC Feb 22: Primitive Frontend updated with emergency reset page. Announcement made in discord.
- 01:16 UTC Feb 22: Primitive Team executes third whitehat attack, rescuing third wallet.
- 03:14 UTC Feb 22: Primitive Team safely returns all rescued funds to their owners.
- 04:00 UTC Feb 22: Confirmed 98% of originally exposed funds have been saved.
At this time, there is no reason to believe that OUSD would be impacted by the attack. It would require users to be able to feed malicious contract addresses to ours which we have recently reviewed against. For good measure, I've also done an additional source review of our token, vault, and strategies contracts for address
arguments and how they are used. I found that all of our address
arguments that are allowed publicly are properly validated or aren't used in a way that could call arbitrary code. With the only exception being Governor
/Timelock
, which are inherently used to execute arbitrary transactions.
One interesting angle to this attack is Uniswap's "flash swap" capabilities which were new to me as of this writing. They allow a user to essentially "take" token A and "give" token B later on in the transaction. While still atomic (executed in a single transaction), it allows the user to perform other actions before settling.
While I don't think there are any current vulnerabilities in OUSD relating to flash swaps, I think it's worth everyone to give it consideration. It should especially be kept in mind if we consider a Uniswap LP strategy in the future.