Skip to content

Latest commit

 

History

History
100 lines (74 loc) · 4.46 KB

File metadata and controls

100 lines (74 loc) · 4.46 KB

Security Policy

CVE-2026-4931 — Critical Integer Truncation in Marginal V1

Severity

Attribute Value
CVSS v3.1 Score 9.1 (Critical)
CVSS v3.1 Vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
CVE ID CVE-2026-4931
Marginal GitHub Reference MarginalProtocol/v1-core#10
GitHub Advisory Lookup CVE-2026-4931 (GitHub Advisories)
CERT/CC Case VU#643748
CWE CWE-197: Numeric Truncation Error
Affected Contract 0x3A6C55Ce74d940A9B5dDDE1E57eF6e70bC8757A7 (Ethereum Mainnet)
Status Patched (block 24,386,649 — Feb 04, 2026)

⚠️ Misclassification Notice

Several vulnerability scanners and advisory feeds are incorrectly reporting CVE-2026-4931 as "moderate severity" or "high attack complexity". This is wrong.

Attribute Correct Value Common Misclassification
Severity Critical (9.1) Moderate / High
Attack Complexity (AC) Low (AC:L) High (AC:H)

Why AC:L is correct: The exploit uses only universally available flash-loan primitives. There are no race conditions, no information leakage requirements, no special timing, and no environmental preconditions. The overflow threshold is deterministic and easily reachable. This meets every criterion for Low complexity under the CVSS v3.1 specification.

The authoritative sources — CERT/CC VU#643748 and NVD CVE-2026-4931 — both reflect CVSS 9.1 Critical with AC:L. A machine-readable OSV-format advisory with the correct scoring is included in this repository at .github/advisories/CVE-2026-4931.json.


CVSS v3.1 Metric Details

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
  • AV:N — Network: Attacker exploits remotely using universally available flash loans
  • AC:L — Low Complexity: Standard DeFi flash loan primitives; no special conditions
  • PR:N — No Privileges Required: Any external account can execute the attack
  • UI:N — No User Interaction: Fully autonomous, single-transaction attack
  • S:C — Scope Changed: Impact extends to all pool liquidity providers and lenders
  • C:H — High Confidentiality: Complete extraction of deposited funds
  • I:H — High Integrity: Full corruption of debt accounting (99.999999% precision loss)
  • A:N — Availability: Not applicable (economic drain, not service outage)

Vulnerability Description

The MarginalV1Pool contract uses Q96 fixed-point arithmetic (256-bit) internally but downcasts to uint160 without overflow protection during settlement calculations:

uint160 price = uint160(sqrtPriceX96);  // NO BOUNDS CHECK

At the EVM bytecode level, this is a bitmask operation (AND 0xfff...fff for 160 bits). When sqrtPriceX96 exceeds type(uint160).max, the high bits are silently dropped and the transaction does not revert.

An attacker uses flash loans to push sqrtPrice past the uint160 overflow threshold, causing the protocol to calculate a $100,000,000 debt as worth 0.000000000000057005 ETH.

Proof of Concept Result

Original Value:    1461501637330902918203684832716283019655932599981
Truncated Value:   57005
Precision Loss:    99.999999%

Actual Debt:       $100,000,000 USDC
Settlement Cost:   0.000000000000057005 ETH
Attacker Profit:   $99,999,999.99

Remediation

The Marginal V1 team deployed a patch at block 24,386,649 (Feb 04, 2026) using OpenZeppelin's SafeCast library (SafeCast: value doesn't fit in 160 bits), confirmed on-chain in transaction: 0xe021842bc2fe89865e41ef20aa84a8f649efe82d515fe3980b6dd160b564189a

The correct fix is:

// Use SafeCast instead of direct downcast:
uint160 price = SafeCast.toUint160(sqrtPriceX96);
// or:
require(sqrtPriceX96 <= type(uint160).max, "Price overflow");
uint160 price = uint160(sqrtPriceX96);

References