VNX-CS-009 – C# Use of Weak Cryptographic Algorithm (MD5, SHA1, DES, RC2, 3DES)
Overview
This rule flags C# files (.cs) where any of the following classes are instantiated with new AlgoName() or created via AlgoName.Create(): MD5, SHA1, SHA1Managed, DES, DESCryptoServiceProvider, RC2, RC2CryptoServiceProvider, TripleDES, TripleDESCryptoServiceProvider, and MD5CryptoServiceProvider. These classes are available in System.Security.Cryptography and represent algorithms that are considered cryptographically broken or insufficiently strong for modern security requirements.
MD5 and SHA-1 are hash functions with known collision attacks — two different inputs can be crafted to produce the same hash output. MD5 collisions can be computed in seconds on modern hardware, and chosen-prefix collisions (where the attacker can choose the beginning of both inputs) are practical. SHA-1 suffered its first practical chosen-prefix collision in 2020. Using these algorithms for integrity verification allows attackers to substitute malicious data without changing the hash value.
DES has a 56-bit key, which is exhaustively searchable in hours using modern hardware. RC2 is similarly weak. TripleDES provides a maximum of 112 bits of effective security and is far slower than AES, making it an inferior choice in every respect.
Severity: High | CWE: CWE-327 – Use of a Broken or Risky Cryptographic Algorithm
Why This Matters
The practical break of MD5 has had real security consequences. The Flame malware (2012) used an MD5 collision attack to forge a valid Microsoft code-signing certificate, allowing it to appear as legitimately signed Windows Update software. This allowed the malware to spread within networks that trusted Microsoft’s signing infrastructure. SHA-1 TLS certificates were deprecated by all major browsers by 2017 precisely because chosen-prefix collisions make certificate forgery practical.
For password storage, MD5 and SHA-1 are catastrophically weak — they are fast hash functions designed for speed, and modern GPUs can compute billions of MD5 or SHA-1 hashes per second, reducing the time to crack a password database from years to hours or minutes even with salted hashes. Password hashing must use purpose-built slow KDFs: PBKDF2, bcrypt, scrypt, or Argon2.
In .NET, these algorithms are still available for legacy interoperability reasons, but they carry [Obsolete] attributes in newer framework versions and should not be used in new code for any security-relevant purpose. CAPEC-97 (Cryptanalysis of Cellular Phone Communication) and ATT&CK T1600 both relate to this class of cryptographic weakness.
What Gets Flagged
// FLAGGED: MD5 used for password or data hashing
var md5 = new MD5CryptoServiceProvider();
byte[] hash = md5.ComputeHash(Encoding.UTF8.GetBytes(password));
// FLAGGED: SHA1 used for HMAC or integrity check
using var sha1 = SHA1.Create();
byte[] checksum = sha1.ComputeHash(fileBytes);
// FLAGGED: DES encryption
using var des = new DESCryptoServiceProvider();
des.Key = desKey;
Remediation
- For hashing data integrity (files, documents, non-password data): use SHA-256 (
SHA256.Create()) or SHA-3 (SHA3_256.Create()in .NET 8+). - For password hashing: use
Rfc2898DeriveBytes(PBKDF2 with SHA-256) with at least 310,000 iterations, or use a third-party library providing bcrypt or Argon2. - For symmetric encryption: replace DES/3DES/RC2 with AES using GCM mode (
AesGcmclass, .NET 3.0+) for authenticated encryption. - For HMAC: use
HMACSHA256orHMACSHA512instead ofHMACMD5orHMACSHA1.
// SAFE: SHA-256 for data integrity
using var sha256 = SHA256.Create();
byte[] hash = sha256.ComputeHash(fileBytes);
// SAFE: PBKDF2 with SHA-256 for password hashing
using var pbkdf2 = new Rfc2898DeriveBytes(
password,
salt: RandomNumberGenerator.GetBytes(32),
iterations: 310_000,
hashAlgorithm: HashAlgorithmName.SHA256);
byte[] passwordHash = pbkdf2.GetBytes(32);
// SAFE: AES-GCM for authenticated encryption
byte[] key = RandomNumberGenerator.GetBytes(32);
byte[] nonce = RandomNumberGenerator.GetBytes(AesGcm.NonceByteSizes.MaxSize);
byte[] ciphertext = new byte[plaintext.Length];
byte[] tag = new byte[AesGcm.TagByteSizes.MaxSize];
using var aesGcm = new AesGcm(key, AesGcm.TagByteSizes.MaxSize);
aesGcm.Encrypt(nonce, plaintext, ciphertext, tag);