The OWASP Smart Contract Top 10 got its 2026 refresh in February, and the reshuffling tells a story most people missed. Access Control stayed on top (no surprise — compromised admin keys caused more damage than every other category combined in 2025). Business Logic jumped to #2. But the real headline is SC10:2026 — Proxy & Upgradeability Vulnerabilities, a brand-new entry that reflects what auditors have been screaming about for two years.
Let me walk through what actually changed, what it means for builders, and — most importantly — the defense patterns that map to each category.
What Moved and Why It Matters
Access Control (SC01) → Still #1
The Venus Protocol exploit on March 15 is the latest poster child: an attacker bypassed deposit checks by directly transferring tokens to the vTHE contract address, exploiting nine months of accumulated position to extract $3.7M.
Defense pattern:
// Don't just check msg.sender — validate the entire call path
modifier onlyAuthorizedPath() {
require(msg.sender == address(router), "Direct calls blocked");
require(tx.origin != msg.sender, "No EOA direct access");
_;
}
// For admin functions, use timelock + multisig, always
modifier onlyGovernance() {
require(
msg.sender == address(timelockController),
"Must go through timelock"
);
_;
}
The 2026 lesson: "admin key" isn't one thing. Separate your upgrade authority, pause authority, fee-setting authority, and oracle-update authority into distinct roles with distinct security requirements.
Business Logic (SC02) → Jumped to #2
This is the category that eats auditors alive. The $50M Aave swap disaster (March 12) wasn't a bug — the protocol "worked as designed." A user swapped $50.4M USDT through a SushiSwap pool with $73K liquidity, got ~$36K of AAVE back, and MEV bots extracted $34M in the sandwich.
Aave's response: Aave Shield, blocking swaps exceeding 25% price impact by default.
Defense pattern: Protocol-level circuit breakers
function swap(
address tokenIn,
address tokenOut,
uint256 amountIn
) external returns (uint256 amountOut) {
uint256 estimatedOut = oracle.getExpectedOutput(
tokenIn, tokenOut, amountIn
);
uint256 actualOut = _executeSwap(tokenIn, tokenOut, amountIn);
uint256 impact = ((estimatedOut - actualOut) * 10000) / estimatedOut;
// Hard limit: revert if price impact > 25%
require(impact <= MAX_PRICE_IMPACT, "Price impact too high");
// Soft limit: require explicit override for > 5%
if (impact > SOFT_PRICE_IMPACT) {
require(
msg.sender == tx.origin &&
_hasHighImpactApproval(msg.sender),
"High impact requires explicit approval"
);
}
return actualOut;
}
Price Oracle Manipulation (SC03) → Held at #3
Venus was hit by this too. The attacker accumulated THE tokens for nine months in a low-liquidity Thena pool, then used the inflated collateral value to borrow against it.
Defense: Multi-source oracles with staleness checks
function getPrice(address token) external view returns (uint256) {
(uint256 chainlinkPrice, uint256 chainlinkAge) = _getChainlinkPrice(token);
(uint256 twapPrice, uint256 twapPeriod) = _getTWAPPrice(token);
require(chainlinkAge < MAX_ORACLE_AGE, "Oracle stale");
require(twapPeriod >= MIN_TWAP_PERIOD, "TWAP too short");
uint256 deviation = _calculateDeviation(chainlinkPrice, twapPrice);
require(deviation < MAX_ORACLE_DEVIATION, "Oracle deviation too high");
return chainlinkPrice < twapPrice ? chainlinkPrice : twapPrice;
}
The New Entry: Proxy & Upgradeability (SC10)
This is the one that made me sit up. Proxy vulnerabilities have been causing catastrophic losses for years — storage collisions, double initialization, unauthorized upgrades — but they never had their own OWASP category until now.
Why 2026? Because the attack surface exploded:
- Storage collisions in UUPS proxies where implementation contracts share storage slots unintentionally
-
Double initialization where
initialize()can be called again after an upgrade - Unauthorized upgrades where upgrade authority isn't properly restricted
- Selfdestruct in implementation (pre-Dencun) that could brick a proxy permanently
Defense: The upgrade security checklist
contract SecureImplementation is
UUPSUpgradeable,
AccessControlUpgradeable
{
uint256[50] private __gap;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize() external initializer {
__AccessControl_init();
__UUPSUpgradeable_init();
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(UPGRADER_ROLE, address(timelockController));
}
function _authorizeUpgrade(address newImplementation)
internal
override
onlyRole(UPGRADER_ROLE)
{
require(
IERC1822Proxiable(newImplementation).proxiableUUID() ==
_IMPLEMENTATION_SLOT,
"Invalid implementation"
);
}
}
For Solana/Anchor developers, the equivalent is program upgrade authority:
#[account(
constraint = program.programdata_address()? == Some(program_data.key()),
)]
pub program: Program<'info, MyProgram>,
#[account(
constraint = program_data.upgrade_authority_address == Some(upgrade_authority.key()),
)]
pub program_data: Account<'info, ProgramData>,
pub upgrade_authority: Signer<'info>,
The Cross-Cutting Theme: Composition Kills
The 2026 OWASP list explicitly calls out that modern exploits chain multiple categories. Venus combined SC01 (access control bypass) + SC03 (oracle manipulation) + SC04 (flash-loan amplification). The Aave incident chained SC02 (no circuit breaker) + SC03 (price impact in low-liquidity pool).
Your audit checklist should test combinations, not just individual categories:
- SC01 + SC10: Can a non-admin trigger an upgrade through a proxy?
- SC03 + SC04: Can a flash loan move a price oracle enough to trigger liquidations?
- SC02 + SC05: Can invalid inputs bypass business logic invariants?
- SC08 + SC06: Can a reentrancy attack exploit an unchecked external call?
- SC01 + SC03: Can an unauthorized actor update the oracle address?
What This Means For Your Next Audit
If you're a developer shipping in 2026:
- Run PROXION alongside Slither for proxy-specific checks (SC10)
- Implement circuit breakers at the protocol level, not just the frontend (SC02)
- Use multi-source oracles with deviation and staleness checks (SC03)
- Separate your admin roles — upgrade authority ≠ pause authority ≠ fee authority (SC01)
- Test vulnerability combinations, not just individual categories
- For Solana: Pay special attention to CPI reentrancy with Token-2022 transfer hooks and Firedancer verification lag
The OWASP 2026 list isn't just a reshuffled version of 2025. The addition of SC10 and the elevation of business logic to #2 reflect a maturing threat landscape where the easy bugs are caught by automated tools, and what's left are the architectural failures that require thinking in systems, not just functions.
Found this useful? I publish weekly deep-dives on DeFi security, smart contract vulnerabilities, and audit tooling. Follow for more.
Top comments (0)