Sysvar Substitution
Detects unverified sysvar account usage that could allow attackers to substitute fake system data.
Sysvar Substitution
Overview
Remediation Guide: How to Fix Sysvar Substitution Issues
The sysvar substitution detector identifies Solana programs that read data from accounts in a pattern consistent with sysvar access (Clock, Rent, EpochSchedule) without first verifying the account’s key matches the known sysvar address. If a program does not validate that a sysvar account is the real sysvar, an attacker can pass a fake account with arbitrary data, manipulating timestamps, rent calculations, or epoch information.
Sigvex tracks key verification statements (CheckKey) and data access patterns that resemble sysvar reads (characteristic offsets for Clock, Rent, and EpochSchedule sysvars). Accounts accessed with sysvar-like patterns without key verification are flagged. CWE mapping: CWE-345 (Insufficient Verification of Data Authenticity).
Why This Is an Issue
Sysvar accounts provide trusted system information:
- Clock: Current slot, epoch, and unix timestamp. Manipulating these can bypass time locks, vesting schedules, and expiration checks.
- Rent: Rent exemption threshold. Manipulating this can cause incorrect rent calculations, potentially draining accounts.
- EpochSchedule: Epoch boundaries and warmup periods. Manipulating this can confuse staking logic.
Without key verification, an attacker creates a data account mimicking the sysvar layout and passes it instead:
- Time manipulation: A fake Clock sysvar with a future timestamp can unlock time-locked funds prematurely.
- Rent bypass: A fake Rent sysvar with a zero threshold can make the program believe any account is rent-exempt.
- Oracle manipulation: Programs that use sysvar data as oracle input can be fed arbitrary values.
How to Resolve
Native Rust
use solana_program::{
account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult,
sysvar::Sysvar,
};
pub fn check_expiry(accounts: &[AccountInfo]) -> ProgramResult {
let clock_account = &accounts[0];
// Method 1: Use the type-safe Sysvar interface (recommended)
let clock = Clock::from_account_info(clock_account)?;
// This internally validates the account key
// Method 2: Manual key validation
if clock_account.key != &solana_program::sysvar::clock::id() {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
Anchor
#[derive(Accounts)]
pub struct CheckExpiry<'info> {
// Sysvar<Clock> validates the account key automatically
pub clock: Sysvar<'info, Clock>,
}
Examples
Vulnerable Code
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult};
pub fn release_vested(accounts: &[AccountInfo]) -> ProgramResult {
let clock_account = &accounts[0]; // Not validated!
let vesting = &accounts[1];
// Read timestamp from unvalidated "clock" account
let data = clock_account.data.borrow();
let timestamp = i64::from_le_bytes(data[32..40].try_into()?);
// Attacker passes fake account with future timestamp
if timestamp > UNLOCK_TIME {
// Releases funds prematurely!
release_funds(vesting)?;
}
Ok(())
}
Fixed Code
use solana_program::{
account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult,
sysvar::Sysvar,
};
pub fn release_vested(accounts: &[AccountInfo]) -> ProgramResult {
let clock_account = &accounts[0];
let vesting = &accounts[1];
// Use type-safe interface that validates the account key
let clock = Clock::from_account_info(clock_account)?;
if clock.unix_timestamp > UNLOCK_TIME {
release_funds(vesting)?;
}
Ok(())
}
Sample Sigvex Output
{
"detector_id": "sysvar-substitution",
"severity": "high",
"confidence": 0.82,
"description": "Account v0 is accessed with a Clock sysvar data pattern (offsets 0, 8, 16, 24, 32) without key verification. An attacker can pass a fake account with arbitrary data.",
"location": { "function": "release_vested", "offset": 1 }
}
Detection Methodology
The detector uses pattern-based sysvar identification:
- Key verification collection: Tracks all
CheckKeystatements to identify accounts whose keys have been verified. - Sysvar pattern matching: Identifies data accesses at characteristic offsets for known sysvars (Clock: offsets 0, 8, 16, 24, 32; Rent: offsets 0, 8, 16; EpochSchedule: offsets 0, 8, 16, 17).
- Gap detection: Reports accounts accessed with sysvar patterns that lack key verification. Only the first access per account is reported to reduce noise.
Context modifiers:
- Anchor programs: confidence reduced (Anchor’s
Sysvar<T>validates account keys) - Admin/initialization functions: confidence reduced by 40%
- Read-only/view functions: confidence reduced by 50%
Limitations
False positives:
- Account data reads at offsets that coincidentally match sysvar patterns but are unrelated to sysvar access will be flagged. The detector relies on offset heuristics, not semantic analysis.
- Programs that validate sysvar keys through indirect means (e.g., checking owner equals the sysvar program) may be flagged.
False negatives:
- Custom sysvar-like accounts (e.g., oracle feeds with similar layouts) are not tracked.
- Sysvar access through the
Sysvar::get()function (which does not require an account) is not relevant and correctly ignored.
Related Detectors
- Hardcoded Sysvar Address — detects hardcoded sysvar addresses instead of using derivation functions
- Clock Account Spoofing — specifically detects Clock sysvar spoofing