Full Report
The backstory on how vulnerabilities were discovered is always fascinating to me. To me, the vulnerability is cool but I want to be able to reproduce the research process to find my own bugs rather than find the individual vulnerability. While auditing a project, they were concerned with the ERC-4626 vaults that they were integrated with. As a result, they decided to look into these. In one of the vaults, one of them was violating CEI. Are there more vaults? When calling withdraw() to transfer ETH, the totalAssets were being updated after this call. So, the classic reentrancy was on! To exploit the reentrancy, the author of the Twitter thread has a nice diagram. The basic idea is to get more and more of the assets with burning less and less of the shares. Eventually, this can be used to drain all of the assets, since the ratio of assets to shares gets broken. With a vulnerability on a live project, what do you do? The author decided to hop in their discord and reach out to them. After not getting a response in 5 minutes they reached out to the SEAL-911 bot Telegram bot. Within 2 minutes, they responded to them. Using trusted channels contact had been made with the protocol at the 20 minute mark. Now, 30 minute later they fixed the vulnerability and deployed the code. In the end, the vulnerability was patched at 51 minutes between starting the reporting process and fixed deployment. It's an incredibly fast time! Overall, it's a great find! But, two things stick out to me. First, looking for issues in code that your client uses but isn't owned by feels strange to me. Is that really a good use of time by them? To me, it feels out of scope, even if there is impact. The second thing was the response time. When the author didn't get a response in the Discord within 5 minutes, they reached out through alternative means. To me, this feels a tad aggressive and fast. If the vulnerability has been there for a year, what are the odds that an attacker going to find and exploit the issue at the same time if you spend a few extra hours waiting? Probably none but I appreciate the want to fix this for the protocol.
Analysis Summary
Based on the provided context regarding the vulnerability research process involving ERC-4626 vaults, here is the summarized technical information.
# Vulnerability: Reentrancy in ERC-4626 Vault Asset Accounting
## CVE Details
- **CVE ID:** N/A (Web3/Smart Contract vulnerabilities often lack traditional CVE assignments; tracked via security advisories/post-mortems).
- **CVSS Score:** 8.6 (High) - Estimated based on potential for total loss of funds.
- **CWE:** CWE-667 (Improper Locking), CWE-703 (Improper Check or Handling of Exceptional Conditions).
## Affected Systems
- **Products:** DeFi protocols utilizing ERC-4626 Tokenized Vault standard implementations.
- **Versions:** Specific implementations of the `withdraw()` function that violate the Checks-Effects-Interactions (CEI) pattern.
- **Configurations:** Vaults that handle native ETH transfers or use hooks (like ERC-777) that allow control flow to be handed back to the caller during execution.
## Vulnerability Description
The flaw is a classic **Reentrancy** vulnerability found in a specific ERC-4626 vault implementation. The contract failed to follow the **Checks-Effects-Interactions (CEI) pattern**. Specifically, when a user calls the `withdraw()` function, the contract transfers ETH to the caller *before* updating the `totalAssets` state variable. Because the internal accounting (total assets vs. shares) is updated after the external call, a malicious contract can re-enter the vault during the transfer and trigger additional withdrawals or liquidations while the contract still "believes" it holds a higher balance of assets than it actually does.
## Exploitation
- **Status:** Identified by researchers; patched before malicious exploitation. PoC logic described via architectural diagrams.
- **Complexity:** Medium (Requires a smart contract to orchestrate the reentrant calls).
- **Attack Vector:** Network (Smart Contract Interaction).
## Impact
- **Confidentiality:** None.
- **Integrity:** High (Internal accounting and asset-to-share ratios are corrupted).
- **Availability:** High (Potential for total drainage of vault assets).
## Remediation
### Patches
- **Status:** Deployed. The specific protocol patched the vulnerability within 51 minutes of the initial report.
- **Fix:** Implementation of the CEI pattern—ensuring that state variables (balances/total assets) are updated *before* any external calls or ETH transfers are made.
### Workarounds
- Use of OpenZeppelin’s `ReentrancyGuard` or similar "nonReentrant" modifiers on all state-changing functions.
- Ensuring the use of `SafeERC20` and avoiding native ETH transfers that trigger `fallback()` or `receive()` functions in untrusted contracts before state updates.
## Detection
- **Indicators of Compromise:** Multiple `Withdraw` events within a single transaction hash; sudden decoupling of the asset-to-share price ratio.
- **Detection Methods and Tools:**
- Static Analysis: Tools like Slither or Aderyn can detect CEI violations.
- Formal Verification: Proving that `totalAssets` always matches the sum of underlying balances.
- Monitoring: Real-time alerts for reentrant calls to sensitive vault functions.
## References
- **SEAL-911:** hxxps://t[.]me/seal_911_bot (Emergency response for Whitehead security incidents).
- **ERC-4626 Standard:** hxxps://eips[.]ethereum[.]org/EIPS/eip-4626.
- **Security Advisory:** Original Twitter/X thread regarding the 51-minute patch timeline.