Previous posts (#3, #4, #5) hypothesized that we can take a step towards making DeFi “SAFU” by breaking down Security Audits For Users (SAFU) and illustrated that with case studies of DeFi stablecoin, Zk-Rollup payment and risk coverage projects.
This post dives into another interesting DeFi project — Opyn, a DeFi options protocol.
To provide some context for new readers (others may skip this section), DeFi projects rely on external security audits as a stamp of security approval. Audit is not a security warranty of “bug-free” code by any stretch of imagination but a best-effort endeavour by trained security experts operating within reasonable constraints of time, understanding, expertise and of course, decidability.
Audit reports illustrate security issues with descriptions of underlying vulnerabilities, likelihood/impact, potential exploit scenarios and recommended/resolved fixes. They also provide subjective insights into code quality, documentation and testing. The scope/depth/format of audit reports varies across auditing teams but they generally cover similar aspects.
Security companies execute audits for clients who pay for their services. Engagements are therefore geared towards priorities of project owners and not project users. Audits are not intended to alert potential project users of any inherent risk. That is not their business/technical goal.
Nevertheless, the conjecture is that project users may be basing their risk without considering if the project had any security audits or incorrectly assuming no/minimal risk for audited projects without bothering to, or having the expertise (understandably so) to evaluate audit report contents.
Evaluating audit reports requires a reasonable level of expertise in smart contract security. Breaking down security audit reports for the benefit of less security-savvy users may make DeFi a bit safer by helping them DYOR.
This post gives a breakdown of (1) Opyn v1 (Convexity Protocol) audit from OpenZeppelin and process quality report from DeFiSafety, and (2) Opyn v2 (Gamma Protocol) audit from OpenZeppelin and formal verification from Certora, where it draws attention to key takeaways in the context of the exploit and incident response.
Context
Project Overview: Opyn protocol allows DeFi users to create/trade call/put options on ETH/ERC20s and take short/long positions on them for hedging their risks.
Opyn v1, Convexity protocol, offers American asset-settled options that have to be manually exercised before expiry. Opyn v2, Gamma protocol, offers European cash-settled options that auto-exercise upon expiry. Opyn options (oTokens) are ERC20s.
Opyn protocol is well documented on contract architecture with descriptions, interfaces and CFGs. It describes the roles of admins/operators and displays deployed contract addresses. It also highlights their strong emphasis on security with audits, formal verification and bug bounty as shown below:
Exploit & Incident Response Overview: On 5 August 2020, a bug was exploited in Opyn’s ETH Put option contracts allowing an attacker to “double exercise” oTokens and steal Put sellers’ collateral. Out of 943,425.13 USDC at risk, 572,165.13 was recovered via whitehacks but 371,260 USDC was lost to the exploit.
The exploit got noticed after user reports on Discord. The Opyn team immediately contacted their security advisors and given the absence of pause functionality in their contracts, they removed liquidity from affected contracts, detached their app frontend from affected contracts, raised option exercise fee to reduce attacker profitability and assembled a whitehack team to help them further.
The team then analysed the exploit and underlying vulnerability to identify all possible attack vectors and funds at risk. They executed two white hacks with the help of samczsun to recover more than half of the funds at risk. They kept the community sufficiently informed of ongoing progress and reimbursed affected users in coming days. The detailed timeline of events and actions are described here and here.
The exploited vulnerability was in code updated (a bug fix by Opyn team) while the OpenZeppelin audit was already underway and therefore was not clearly communicated and covered in audit scope. The Opyn team took responsibility for events leading to this exploit and proposed future measures including improved security best practices, test driven development, use of security tools, better audit process management, contract pause functionality, transaction monitoring for better/faster incident response and increased bug bounties.
The technical underpinnings behind the exploit are best explained in Opyn’s post-mortem. To summarize key points, oETH Put options gave option holders the right to sell their ETH for some pre-specified amount of USDC anytime before option expiry. This is triggered by an external exercise() function in OptionsContract.sol which takes oTokensToExercise (number of oTokens being exercised) and vaultsToExerciseFrom (array of vaults to exercise from) as parameters. The callers transfer in their oTokens and get a corresponding amount of collateral from the underlying vaults. The external exercise() function calls an internal _exercise() function in a for loop depending on the number of vaults that need to be exercised. The vulnerable code snippet in the internal _exercise() function is shown below:
When the attacker calls oToken.exercise(), say with 2 vaults and 1 ETH as the underlying (with corresponding oTokens), the external exercise() function calls the internal _exercise() function twice in the for loop. Each time, the msg.value check on Line 805 passes and the USDC collateral is transferred to attacker. So the attacker effectively exercises two options each worth 1 ETH (total 2 ETH) but having sent only 1 ETH. This vulnerability effectively allows an attacker to pay for the underlying one time but use that for multiple exercises.
Non-ETH Put options were not vulnerable to this vulnerability because their transfer mechanism was different and happens via transferFrom() call (on Line 808) each time the internal _exercise() function is called. ETH transfer however happens only once via msg.value when the external exercise() function is called the first time.
Detection of this vulnerability depends on data-flow analysis that is also very specific to contract logic and is unlikely to have been caught by automated static analysis tools. Manual analysis with deep-knowledge of contract logic could perhaps have detected this inconsistency.
Audits Overview: Opyn v1 was audited by OpenZeppelin in February 2020 and its process quality reviewed by DeFiSafety in August 2020. Opyn v2 was audited by OpenZeppelin in November 2020 and formally verified by Certora in December 2020. Highlights of these reports are provided in the following sections.
Opyn v1 OpenZeppelin Report Breakdown
OpenZeppelin’s audit of Opyn v1 reported on 10 February 2020 (duration/effort not mentioned) finding 1 critical (fixed), 2 high (WIP/partial), 2 medium (fixed) and 10 low (unaddressed/partial/fixed/justified) severity issues along with 17 other information notes (unaddressed/partial/fixed/justified). The status of fixes/justifications were at the time of the report and the current status is unclear.
OpenZeppelin’s audit report is well detailed starting with a list of contracts in audit scope and an overview of system semantics before describing each finding in great detail to justify the severity.
C01 was due to missing use of SafeMath sub in a require statement which was fixed. H01 was about managing protocol solvency during liquidation events. H02 was about a privileged EOA admin instead of a multi-sig for protocol governance. It is not clear if H01 and H02 have been completely fixed.
Other findings were related to security/programming best practices to reduce attack surface and improve readability. These included potential race conditions, missing events/check-effect-interaction-pattern/SafeMath in few places, differential handling of ETH and ERC20 tokens, unclear contract logic/flow, unbounded loops, default visibility and misleading names/comments.
Opyn v1 DeFiSafety Report Breakdown
A DeFiSafety review analyzes a project’s public artifacts (public documents, software repository, website etc.) to evaluate quality of its development/deployment process and assign scores accordingly. (This is currently performed on a voluntary pro-bono basis.)
The review analyzes process quality across four broad areas: (1) Code and Team - Executing Code Verification (2) Documentation (3) Testing and (4) Audits. Details behind this process and scoring criteria is described here.
DeFiSafety scoring matrix (v0.5) and scores of Opyn v1 are shown below (details here). While it performed well overall, it lost points on missing deployed-source match, comments, test coverage/report, formal verification and multiple audits.
Software quality assurance standards are already commonplace in software development. Such a process quality review is currently complimentary to Web3 security audits. Despite some fundamental challenges in measuring/interpreting impact of process quality on software’s real-world security posture, this is an interesting approach to consider in a broader assessment.
Opyn v2 OpenZeppelin Report Breakdown
OpenZeppelin’s audit of Opyn v2 reported on 10 November 2020 (duration/effort not mentioned) finding 1 critical (fixed), 2 high (fixed), 5 medium (unaddressed/fixed/justified) and 12 low (unaddressed/fixed/justified) severity issues along with 7 other information notes (unaddressed/partial/fixed). The status of fixes/justifications were at the time of the report and the current status is unclear.
OpenZeppelin’s audit report is well detailed starting with a list of contracts in audit scope, an overview of privileged system roles and dependencies on Chainlink/Compound price oracles before describing each finding in great detail to justify the severity.
C01 was due to missing input validation on oToken address which is user/attacker controlled. H01 was about ETH getting locked in contract and H02 was about protocol insolvency due to market fluctuations of assets. All were fixed.
Other findings were related to contract logic and security/programming best practices pertaining to collateral whitelisting, use of transfer(), external calls, price dispute ordering, explicit declaration/casting, indexed events, input validation, non-atomic actions and deprecated APIs.
Opyn v2 Certora Report Breakdown
Certora’s formal verification of Opyn v2’s contracts from 5 October to 28 December 2020 reported 2 high (fixed), 1 medium (fixed) and 5 low severity findings.
The Certora Prover proved the implementation correctness of MarginCalculator.sol, MarginVault.sol, Controller.sol, MarginPool.sol, MarginVault.sol, Whitelist.sol and Otoken.sol against their formal specifications (detailed in report’s Appendix A).
H01 was a DoS on minting new options. H02 addressed a missing Chainlink API validation. M01 calls out an indexing issue with add/remove vaults. The low-severity issues call out inconsistencies with array overflow, zero-address asset in array, gas inefficiency and sign confusion.
Conclusion
This post gave a breakdown of audit reports on Opyn — an options trading protocol for users to hedge against DeFi risks or take speculative positions on ETH/ERC20s — from OpenZeppelin, DeFiSafety and Certora, in the context of the exploit on Opyn v1 Convexity Protocol which happened after OpenZeppelin’s audit.
Overall, there were some red flags raised by the audit, six months before the exploit, which were presumably fixed. But the exploited vulnerability unfortunately got introduced during the audit and hence remained undiscovered.
Security hindsight, after an exploit, is certainly 20/20 and regretful. Better specification, more documentation, extensive testing of corner cases, use of security tools during development, internal code reviews, multiple independent audits covering the same scope for redundancy and a public bug bounty program would help raise the bar.
The exploited vulnerability was not a common Solidity coding pitfall that could have been detected by automated tools checking for security best practices. It was a design/programming error and was particular to the exploited contract’s logic. A careful internal design/code review or better audit process management could probably have caught it.
Audits provide a project’s security snapshot over a brief (typically few weeks) period. However, smart contracts need to evolve over time to add new features, fix bugs or optimize. Relying on external audits after every change is impractical. We observe from Opyn’s incident that even small changes may introduce exploitable vulnerabilities. Projects therefore need in-house security expertise and follow Secure Software Development Lifecycle (SSDLC) with “Continuous Auditing” and “Continuous Monitoring/Response” (similar to CI/CD process).
Vulnerabilities can be reduced but cannot be ruled out. Guarded launches with built-in circuit breakers and better on-chain monitoring/response are therefore critical.
Breaking down DeFi Security Audits For Users (SAFU) may be worthwhile as a public good but certainly should not be interpreted as an endorsement/indictment of projects/auditors or as financial/investment advice. DeFi SAFU should be a collective community responsibility.
I hope you found this somewhat useful. Thanks for reading and look forward to your comments and feedback.
great content