Security (EVM-03): VENUS_VTOKEN_ADDRESS accepted without allowlist or validation #9

Closed
opened 2026-04-21 08:13:44 +00:00 by Brouie · 5 comments
Brouie commented 2026-04-21 08:13:44 +00:00 (Migrated from gitlab.com)

@PlasticDigits finding from security review on #5.

Severity

Low-to-Medium. Deployment footgun; malicious or wrong token silently accepted.

Description

oracle-service/src/bsc.rs read_exchange_rate_stored:

let addr = Address::from_str(vtoken).map_err(|e| eyre!("invalid vToken address: {}", e))?;

Only validates hex format. The address is not checked against the canonical Venus vFDUSD deployment on BSC mainnet.

Why this matters

If VENUS_VTOKEN_ADDRESS is misconfigured (typo, copy-paste from wrong env, compromise of the deploy manifest), the service reads exchangeRateStored from a different vToken — e.g. vBUSD, vUSDC, or an attacker-deployed contract that implements the same signature.

A different-underlying vToken returns a rate scaled to a different underlying decimal, violating INV-MATH-001 silently (EVM-04 finding). An attacker-deployed contract can return any rate they want.

Recommendation

Hardcode the canonical Venus vFDUSD address as a compile-time constant, or maintain an allowlist with environment-specific entries (mainnet vs testnet). Compare at startup and error if mismatch:

const CANONICAL_VENUS_VFDUSD: &str = "0x...";  // to be set per the Venus team's docs
if config.venus_vtoken_address.to_lowercase() != CANONICAL_VENUS_VFDUSD.to_lowercase() {
    return Err(eyre!("VENUS_VTOKEN_ADDRESS does not match canonical Venus vFDUSD"));
}

Additionally consider bytecode hash verification on startup — the canonical Venus vToken implementation has a known deployed bytecode; any deviation is suspicious.

  • Review parent: #5
  • Pairs with EVM-04 (decimals scaling)
@PlasticDigits finding from security review on #5. ## Severity Low-to-Medium. Deployment footgun; malicious or wrong token silently accepted. ## Description `oracle-service/src/bsc.rs` `read_exchange_rate_stored`: ```rust let addr = Address::from_str(vtoken).map_err(|e| eyre!("invalid vToken address: {}", e))?; ``` Only validates hex format. The address is not checked against the canonical Venus vFDUSD deployment on BSC mainnet. ## Why this matters If `VENUS_VTOKEN_ADDRESS` is misconfigured (typo, copy-paste from wrong env, compromise of the deploy manifest), the service reads `exchangeRateStored` from a **different vToken** — e.g. vBUSD, vUSDC, or an attacker-deployed contract that implements the same signature. A different-underlying vToken returns a rate scaled to a different underlying decimal, violating INV-MATH-001 silently (EVM-04 finding). An attacker-deployed contract can return any rate they want. ## Recommendation Hardcode the canonical Venus vFDUSD address as a compile-time constant, or maintain an allowlist with environment-specific entries (mainnet vs testnet). Compare at startup and error if mismatch: ```rust const CANONICAL_VENUS_VFDUSD: &str = "0x..."; // to be set per the Venus team's docs if config.venus_vtoken_address.to_lowercase() != CANONICAL_VENUS_VFDUSD.to_lowercase() { return Err(eyre!("VENUS_VTOKEN_ADDRESS does not match canonical Venus vFDUSD")); } ``` Additionally consider bytecode hash verification on startup — the canonical Venus vToken implementation has a known deployed bytecode; any deviation is suspicious. ## Related - Review parent: #5 - Pairs with EVM-04 (decimals scaling)
Brouie commented 2026-04-21 08:17:07 +00:00 (Migrated from gitlab.com)

mentioned in issue #5

mentioned in issue #5
PlasticDigits commented 2026-04-21 13:40:07 +00:00 (Migrated from gitlab.com)

mentioned in commit c42ca853f4

mentioned in commit c42ca853f4d9ec33cd102281f954516acac42163
PlasticDigits commented 2026-04-21 13:40:07 +00:00 (Migrated from gitlab.com)

Fix (merged to main)

What changed

  • oracle-service now validates VENUS_VTOKEN_ADDRESS at startup in Config::from_env().
  • The value must match the canonical Venus vFDUSD vToken on BSC mainnet: 0xC4eF4229FEc74Ccfe17B2bdeF7715fAC740BA0ba (case-insensitive; parsed with Alloy Address).
  • If the env var points at any other address, the service fails fast with a clear error instead of silently calling exchangeRateStored on the wrong contract.

Tests

  • Unit tests in oracle-service/src/config.rs cover canonical (checksummed + lowercase) acceptance and rejection of a different token address.

Not in scope (per recommendation)

  • Bytecode hash verification on startup was not added; can be a follow-up if desired.

Commit: c42ca85 on branch fix/glab-9-venus-vtoken-canonical, merged via fast-forward into main and pushed to origin.

## Fix (merged to `main`) **What changed** - `oracle-service` now validates `VENUS_VTOKEN_ADDRESS` at startup in `Config::from_env()`. - The value must match the canonical Venus **vFDUSD** vToken on **BSC mainnet**: `0xC4eF4229FEc74Ccfe17B2bdeF7715fAC740BA0ba` (case-insensitive; parsed with Alloy `Address`). - If the env var points at any other address, the service fails fast with a clear error instead of silently calling `exchangeRateStored` on the wrong contract. **Tests** - Unit tests in `oracle-service/src/config.rs` cover canonical (checksummed + lowercase) acceptance and rejection of a different token address. **Not in scope (per recommendation)** - Bytecode hash verification on startup was not added; can be a follow-up if desired. Commit: `c42ca85` on branch `fix/glab-9-venus-vtoken-canonical`, merged via fast-forward into `main` and pushed to `origin`.
PlasticDigits commented 2026-04-21 13:49:38 +00:00 (Migrated from gitlab.com)

mentioned in issue #12

mentioned in issue #12
Brouie commented 2026-04-22 00:29:46 +00:00 (Migrated from gitlab.com)

@PlasticDigits verified fix on c42ca85.

  • CANONICAL_VENUS_VFDUSD_BSC_MAINNET = 0xC4eF4229FEc74Ccfe17B2bdeF7715fAC740BA0ba hardcoded as compile-time constant
  • validate_venus_vtoken_address parses both got/want as Address for checksum-insensitive equality
  • Called in Config::from_env — startup fails fast with clear error showing expected vs got
  • Internal-error path if the hardcoded constant ever fails to parse

Misconfiguration or intentional swap to another vToken now impossible without code change + redeploy. Correct tradeoff — Venus contract upgrades are rare, the defense is worth the friction.

@PlasticDigits verified fix on c42ca85. - `CANONICAL_VENUS_VFDUSD_BSC_MAINNET = 0xC4eF4229FEc74Ccfe17B2bdeF7715fAC740BA0ba` hardcoded as compile-time constant - `validate_venus_vtoken_address` parses both got/want as `Address` for checksum-insensitive equality - Called in `Config::from_env` — startup fails fast with clear error showing expected vs got - Internal-error path if the hardcoded constant ever fails to parse Misconfiguration or intentional swap to another vToken now impossible without code change + redeploy. Correct tradeoff — Venus contract upgrades are rare, the defense is worth the friction.
Brouie (Migrated from gitlab.com) closed this issue 2026-04-22 00:29:48 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/ust1-window#9
No description provided.