VNX-339: Small Seed Space in Pseudo-Random Number Generator (PRNG)
Detects a PRNG seeded with a value narrowed to a small range (a modulus, a 16-bit mask or cast, a length or count, a small random range) when its output is security-relevant, and any narrowed seed on SecureRandom or ChaCha8. Python, Java, Kotlin, Go, C, C++, PHP, Ruby and JavaScript.
| Field | Value |
|---|---|
| Rule ID | VNX-339 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | Python, Java, Kotlin, Go, C, C++, PHP, Ruby, JavaScript / TypeScript |
| CWE | CWE-339 |
| CAPEC | CAPEC-97 |
| ATT&CK | T1557 |
| CWSS vector | CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | prng seed cwe-339 |
| Source | internal/sast/rules/vnx-339.rego |
Overview
VNX-339 maps to CWE-339: Small Seed Space in PRNG. However strong the generator, a seed with a few thousand possible values gives a few thousand possible output sequences, and an attacker can try every one.
The rule reports a seed reduced with a modulus (x % 1000) or a mask of at most 16 bits (& 0xFFFF), cast to an 8- or 16-bit type ((short), (unsigned char), uint16_t, uint8(...)), taken from a length or count (key.length(), len(s), strlen(), .size()), or drawn from a small random range (randint(0, 999), nextInt(1000), Intn(256)), inline or through a variable assigned one of those within eight lines above, passed to the same seeding APIs as VNX-336.
A narrowed seed on a SecureRandom or ChaCha8 is always reported. A general-purpose PRNG is reported only when its output is security-relevant: the seed call sits in a function named for a secret, the generator is stored under a secret’s name, or the same file draws from a PRNG into a secret.
What Gets Flagged
// FLAGGED: the seed is the length of the key
public String createPasswordReset(String username, String key) {
Random random = new Random();
random.setSeed(key.length());
return Integer.toHexString(random.nextInt());
}
// FLAGGED: a 16-bit seed for a SecureRandom
sr.setSeed(stamp & 0xFFFF);
/* FLAGGED: a thousand possible seeds */
srand(seed % 1000);
# FLAGGED: the seed comes from a 1000-value range
random.seed(random.randint(0, 999))
What Is Not Flagged
- Full-width seeds, and narrowed seeds in files that produce no secret.
- Test files, and calls in comments or string literals.
Constant seeds are VNX-336 and clock/PID seeds VNX-337; a seed that is both narrowed and clock-derived is reported by both.
Remediation
- Do not seed generators that produce secrets; use a CSPRNG that seeds itself from the operating system (
secrets,SecureRandom,crypto/rand,random_bytes(),crypto.randomBytes()). - For confirmed false positives, add a
# vulnetix-ignore: VNX-339comment on the line.
References
- CWE-339: Small Seed Space in PRNG
- OWASP Cryptographic Storage Cheat Sheet: Secure random number generation
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-339
// nosec vnx-339
Or record an ignore rule: vulnetix ignore add --rule vnx-339 --reason "...". See Suppressing findings.