This article is meant to analyze the Platypus Finance incident that occurred on the 17th of February 2023 at approximately 19:16 UTC¹ in an impartial way and identify the root cause.
Vulnerability Analysis
The vulnerability that was exploited stems from incorrect integration between the PlatypusTreasure² ³ and the MasterPlatypusV4⁴ ⁵contracts of the Platypus Finance ecosystem and namely an improper logical check being present in the MasterPlatypusV4 contract.
The PlatypusTreasure contract was recently launched⁶ ⁷ to support the USP stablecoin of Platypus Finance. The contract contains a mechanism that allows borrowing the USP stablecoin with assets that are presently staked in the IMasterPlatypus implementation referenced by the collateral settings of an LP asset (PlatypusTreasure::_getCollateralAmount).
While this by itself is not a vulnerable feature, the problem arises in how the outdated MasterPlatypusV4 implementation integrates with the an IPlatypusTreasure contract. The MasterPlatypusV4 contract implementation that was referenced by the proxy at the time of the attack was deployed on the 14th of November, 2022⁸, and contained a fatal misconception in its emergencyWithdraw mechanism.
The contract was built with the integration of IPlatypusTreasure at a later point as evidenced by its platypusTreasure member and its optionality in the codebase. Within the withdrawal workflows of the MasterPlatypusV4 contract, the IPlatypusTreasure::isSolvent function is invoked to perform a debt solvency evaluation.
Core Problem
The MasterPlatypusV4::emergencyWithdraw function performs its solvency check before updating the LP tokens associated with the stake position. As a result, it is possible for a user to withdraw their funds while they are being utilized as collateral for a debt position in PlatypusTreasure as the solvency check will succeed in all circumstances.
The MasterPlatypusV4::withdraw function is not susceptible to the same attack vector as it performs a solvency check after the stake position has been updated, ensuring that the PlatypusTreasure::isSolvent function takes into account the stake position with reduced LP tokens.
The issue could have been prevented by re-ordering the MasterPlatypusV4::emergencyWithdraw statements and performing the solvency check after the user’s amount entry has been set to 0 which would have prohibited the attack from taking place.
Alternatively, the MasterPlatypusV4::emergencyWithdraw could utilize the debtAmount variable yielded by PlatypusTreasure::isSolvent and ensured that it is 0, preventing emergency withdrawals with active debt positions.
Attack Scenario
Given that it is possible to provide collateral for a debt and proceed to withdraw it without repaying the debt, the exploiter is able to create what is known as “bad debt” in the system and acquire the debt’s upside.
The attack was executed in a single transaction which was submitted at approximately 2023–02–17 19:16:54 UTC¹ in which they performed the following:
- Acquired a
44,000,000 USDCloan off the Aave V3 protocol - Deposited the same amount of
USDCin the Platypus FinancePoolto acquire theLP-USDCasset - Deposited the newly created
LP-USDCtokens to theMasterPlatypusV4implementation - Performed a borrow operation on the
PlatypusTreasurecontract of the maximum amount ofUSPthey were able to borrow, which amounted to roughly~41,794,533units - Performed an emergency withdrawal of the
LP-USDCassets they had deposited in theMasterPlatypusV4implementation, thereby causing their debt to become “bad” as it becomes no longer serviceable - Withdrew all
USDCfunds associated with theLP-USDCposition they had created in the second step, acquiring~43,999,999 USDCin return - Performed liquidation of
9,250,000 USPtokens in numerous currencies via the Platypus FinancePool
Interestingly, the attacker’s contract performed multiple staticcall operations to the 0x000000000000000000636F6e736F6c652e6c6f67 address during the transaction’s execution. This address is in fact the “console” address in use by the hardhat toolkit⁹, indicating that the attacker used the hardhat toolkit to produce their contract.
The attacker did not liquidate the full ~41,794,533 amount of USP tokens they acquired, instead opting for smaller trades presumably due to insufficient liquidity in the USP pools they utilized. In detail, the transaction performed the following swaps before repaying the flash-loan:
- Exchange
2,500,000 USPfor~2,425,762 USDC - Exchange
2,500,000 USPfor~1,946,900 USDC.e(BridgedUSDC) - Exchange
1,600,00 USPfor~1,552,550 USDT - Exchange
1,250,00 USPfor~1,217,581 USDT.e(BridgedUSDT) - Exchange
700,000 USPfor~687,369 BUSD - Exchange
700,000 USPfor~691,984 DAI.e(BridgedDAI)
Ultimately, the attacker was able to retain the following assets post-execution:
+~2,425,762 USDC=~2,427,390 USD @ 1.00067117+~1,946,900 USDC.e=~1,948,206 USD @ 1.00067117+~1,552,550 USDT=~1,553,651 USD @ 1.00070943+~1,217,581 USDT.e=~1,219,725 USD @ 1.00176158+~687,369 BUSD=~688,527 USD @ 1.00168506+~691,984 DAI.e=~692,355 USD @ 1.00053726+~33,044,533 USP= Indeterminate Evaluation
As the value of the USP asset is no longer considered canonical, we can assess the financial impact of the stablecoin assets it was liquidated to and sum a total estimated profit of roughly ~8,529,854 USD as of 2023-02-17 15:20:00 UTC.
Security Audit
Omniscia performed two security audits of the Platypus Finance platform simultaneously on November 21st, 2021 with a conclusion date of December 5th, 2021 and a final delivery date of December 24th, 2021. The publicly available audits¹⁰ ¹¹ did not cover the USP stablecoin or the PlatypusTreasure implementation.
While the MasterPlatypus implementation was in scope in the “governance” audit of the protocol, we performed an audit of the V1 implementation which contained no integration points with an external platypusTreasure system.
The Platypus Finance protocol has introduced numerous updates since the time the audit was finalized, including all contracts involved in the vulnerability (MasterPlatypusV4, USP, and PlatypusTreasure). These contracts were never in scope of any audit conducted by us and thus are considered to be unaudited code by our team.
Conclusion
The attack ultimately arose from improper integration of the MasterPlatypusV4 contract with PlatypusTreasure and did not perform its solvency check in the correct order, enabling collateral to be withdrawn with an active debt. As a result, “bad debt” could be created in the system at the expense of the protocol’s USP token which was then exchanged for multiple stablecoins.
Sources
- Snowtrace Transaction of the Attack: https://snowtrace.io/tx/0x1266a937c2ccd970e5d7929021eed3ec593a95c68a99b4920c2efa226679b430
- Snowtrace Address of the
PlatypusTreasureContract’s Proxy: https://snowtrace.io/address/0x061da45081ace6ce1622b9787b68aa7033621438 - Snowtrace Address of the
PlatypusTreasureContract: https://snowtrace.io/address/0xbcd6796177ab8071f6a9ba2c3e2e0301ee91bef5 - Snowtrace Address of the
MasterPlatypusV4Contract’s Proxy: https://snowtrace.io/address/0xff6934aac9c94e1c39358d4fdcf70aeca77d0ab0 - Snowtrace Address of the
MasterPlatypusV4Contract: https://snowtrace.io/address/0xc007f27b757a782c833c568f5851ae1dfe0e6ec7 - Snowtrace Transaction of the
PlatypusTreasureContract’s Proxy Deployment: https://snowtrace.io/tx/0x326d5c2e0ebb68c5f267b1f2fb654729ef5bb2bcaf09a5adea382e206b17315d - Snowtrace Transaction of the
USPMinter Set toPlatypusTreasure’s Proxy: https://snowtrace.io/tx/0x535ee1baa8688a5fb23c4b7d84aae65081e2663a783eb58357661e85c613d01b - Snowtrace Transaction of the
MasterPlatypusV4Contract’s Deployment: https://snowtrace.io/tx/0x0723124dfd5abdeafbfeab072a02610c868a7b7b32f641aa50fc157eca636d7d - Hardhat
console.solAddress: https://github.com/NomicFoundation/hardhat/blob/[email protected]/packages/hardhat-core/console.sol#L5 - Omniscia Security Audit of Platypus Finance’s Core: https://omniscia.io/platypus-finance-core-implementation/
- Omniscia Security Audit of Platypus Finance’s Governance: https://omniscia.io/platypus-finance-governance-staking/
https://medium.com/@omniscia.io/platypus-finance-incident-post-mortem-7b71a0a47a5e
All Comments