Cleartext Storage of Sensitive Information in Executable
Description
Cleartext Storage of Sensitive Information in Executable is a vulnerability that occurs when a product stores sensitive information in cleartext within an executable binary. This includes passwords, encryption keys, API keys, private keys, and other secrets embedded directly in compiled code or stored in data sections of executables. Attackers can extract this information through reverse engineering, binary analysis, or simple string extraction from the executable file. Even when information is encoded (such as Base64), it provides no meaningful protection as attackers can easily identify and decode common encoding schemes.
Risk
Embedding sensitive information in executables creates severe, persistent security vulnerabilities. The distributed nature of executables means secrets are exposed to anyone with access to the binary file. Reverse engineering tools and techniques are widely available, making extraction straightforward even for moderately skilled attackers. Simple string searches often reveal passwords and keys stored as ASCII text. Embedded secrets cannot be rotated without redistributing the entire application. The vulnerability affects all instances of the application - a single binary analysis compromises every deployment. Real-world exploits have included embedded RSA private keys enabling server spoofing, hard-coded administration passwords, and SSH keys granting root access. The impact is amplified when executables are distributed to untrusted parties or made publicly available.
Solution
Never embed sensitive information directly in executable code or resources. Use external configuration files with proper access controls and encryption. Implement secure key management systems that provide keys at runtime rather than compile time. Use environment variables for credentials in deployment environments. Implement hardware security modules (HSMs) for cryptographic key storage in high-security applications. For applications requiring embedded authentication, use challenge-response protocols rather than static secrets. If some form of embedded secret is unavoidable, use white-box cryptography techniques to obfuscate keys, understanding this provides only defense in depth rather than true security. Implement code obfuscation as an additional layer but never rely on it alone. Use binary scanning tools during CI/CD to detect accidentally embedded secrets.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Attackers can extract sensitive information from executables through reverse engineering or string analysis, compromising encryption keys, passwords, API keys, and other secrets. |
| Access Control, Authentication | Scope: Access Control, Authentication Exposed credentials enable unauthorized access. Compromised private keys allow server impersonation and man-in-the-middle attacks. All deployments using the same executable are equally compromised. |
Example Code
Vulnerable Code (C/Java)
The following examples demonstrate cleartext executable storage vulnerabilities:
// Vulnerable: Hard-coded credentials in C executable
#include <stdio.h>
#include <string.h>
// Vulnerable: Password as string constant in binary
#define ADMIN_PASSWORD "SuperSecretAdmin123"
// Vulnerable: Encryption key embedded in code
static const unsigned char encryption_key[] = {
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
};
// Vulnerable: API key as string
static const char *API_KEY = "sk_live_51H2x3y4z5a6b7c8d9e0f1g2h3";
// Vulnerable: Database credentials embedded
static const char *DB_CONNECTION =
"Server=prod.db.example.com;User=admin;Password=DbP@ss2024!;";
int authenticate(const char *password) {
// Vulnerable: Comparing against embedded password
if (strcmp(password, ADMIN_PASSWORD) == 0) {
return 1; // Authenticated
}
return 0;
}
void encrypt_data(unsigned char *data, size_t len) {
// Vulnerable: Using embedded key
aes_encrypt(data, len, encryption_key);
}
// Running 'strings' on this binary reveals:
// SuperSecretAdmin123
// sk_live_51H2x3y4z5a6b7c8d9e0f1g2h3
// Server=prod.db.example.com;User=admin;Password=DbP@ss2024!;
// Vulnerable: Hard-coded credentials in Java
public class VulnerableConfig {
// Vulnerable: Password as constant
private static final String ADMIN_PASSWORD = "AdminSecret2024!";
// Vulnerable: API key embedded in class
private static final String STRIPE_API_KEY = "sk_live_abcd1234efgh5678";
// Vulnerable: Database credentials
private static final String DB_URL =
"jdbc:mysql://prod.db.example.com/app";
private static final String DB_USER = "root";
private static final String DB_PASSWORD = "RootP@ssw0rd!";
// Vulnerable: Encryption key as byte array
private static final byte[] AES_KEY = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};
// Vulnerable: Comparing against hard-coded password
public boolean authenticate(String password) {
// Hash comparison doesn't help - hash is also embedded!
if (password.equals("68af404b513073584c4b6f22b6c63e6b")) {
return true;
}
return false;
}
public Connection getConnection() throws SQLException {
// Vulnerable: Using embedded credentials
return DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
}
}
# Vulnerable: Python script compiled to executable
# (using PyInstaller, cx_Freeze, etc.)
# Vulnerable: Hard-coded credentials
AWS_ACCESS_KEY = "AKIAIOSFODNN7EXAMPLE"
AWS_SECRET_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
# Vulnerable: Embedded RSA private key
RSA_PRIVATE_KEY = """
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA0Z3VS5JJcds3xfn/ygWyf8Lsh...
-----END RSA PRIVATE KEY-----
"""
# Vulnerable: Database password
DB_CONFIG = {
'host': 'db.production.example.com',
'user': 'dbadmin',
'password': 'ProductionDbPass123!', # Embedded!
'database': 'production_db'
}
class VulnerableAuth:
# Vulnerable: Master password embedded
MASTER_PASSWORD = "MasterSecretKey2024!"
def authenticate(self, password):
return password == self.MASTER_PASSWORD
Fixed Code (C/Java)
// Fixed: External credential retrieval in C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Fixed: Credentials retrieved from environment or secure storage
char* get_admin_password() {
// Fixed: Read from environment variable
char *password = getenv("ADMIN_PASSWORD");
if (password == NULL) {
fprintf(stderr, "ADMIN_PASSWORD not set\n");
exit(1);
}
return password;
}
// Fixed: Key retrieved from secure storage at runtime
int get_encryption_key(unsigned char *key, size_t key_len) {
// Option 1: Read from secure file with restricted permissions
FILE *keyfile = fopen("/etc/myapp/secrets/encryption.key", "rb");
if (keyfile == NULL) {
return -1;
}
// File should be chmod 0600, owned by app user
size_t read = fread(key, 1, key_len, keyfile);
fclose(keyfile);
return (read == key_len) ? 0 : -1;
}
// Fixed: API key from environment
char* get_api_key() {
return getenv("API_KEY");
}
// Fixed: Database connection from secure config
char* get_db_connection() {
// Read from encrypted config file or secrets manager
return read_encrypted_config("database.connection");
}
int authenticate(const char *password) {
// Fixed: Get password from secure source
char *stored_password = get_admin_password();
// Fixed: Constant-time comparison
int result = constant_time_compare(password, stored_password);
return result;
}
void encrypt_data(unsigned char *data, size_t len) {
unsigned char key[16];
// Fixed: Retrieve key at runtime
if (get_encryption_key(key, sizeof(key)) != 0) {
fprintf(stderr, "Failed to retrieve encryption key\n");
return;
}
aes_encrypt(data, len, key);
// Fixed: Clear key from memory after use
explicit_bzero(key, sizeof(key));
}
// Fixed: External credential management in Java
import java.sql.*;
import java.io.*;
import java.nio.file.*;
public class SecureConfig {
// Fixed: No embedded credentials!
// Fixed: Retrieve password from environment
public static String getAdminPassword() {
String password = System.getenv("ADMIN_PASSWORD");
if (password == null || password.isEmpty()) {
throw new SecurityException("ADMIN_PASSWORD not configured");
}
return password;
}
// Fixed: Retrieve API key from secrets manager
public static String getApiKey() {
// Using AWS Secrets Manager, HashiCorp Vault, or similar
SecretsClient client = SecretsClient.create();
return client.getSecretValue("myapp/stripe_api_key");
}
// Fixed: Database credentials from secure source
public static Connection getConnection() throws SQLException {
// Fixed: Read from encrypted config or secrets manager
String dbUrl = System.getenv("DB_URL");
String dbUser = System.getenv("DB_USER");
String dbPassword = getSecretFromVault("database/password");
if (dbPassword == null) {
throw new SecurityException("Database credentials not available");
}
return DriverManager.getConnection(dbUrl, dbUser, dbPassword);
}
// Fixed: Encryption key from secure key store
public static byte[] getEncryptionKey() {
try {
// Read from secure file with restricted permissions
Path keyPath = Paths.get("/etc/myapp/secrets/encryption.key");
// Verify file permissions (Unix)
// Should be readable only by application user
return Files.readAllBytes(keyPath);
} catch (IOException e) {
throw new SecurityException("Cannot retrieve encryption key", e);
}
}
// Fixed: Hash-based authentication without embedded hash
public boolean authenticate(String password) {
// Fixed: Retrieve stored hash from secure storage
String storedHash = getStoredPasswordHash();
// Fixed: Use secure password verification
return BCrypt.checkpw(password, storedHash);
}
private String getStoredPasswordHash() {
// Retrieve from secure database or config
return secureConfig.get("auth.password_hash");
}
private static String getSecretFromVault(String secretPath) {
// Implementation using HashiCorp Vault, AWS Secrets Manager, etc.
VaultClient vault = VaultClient.create();
return vault.readSecret(secretPath);
}
}
# Fixed: External credential retrieval in Python
import os
from functools import lru_cache
import boto3 # For AWS Secrets Manager
class SecureConfig:
"""Configuration manager with external secret retrieval."""
# Fixed: No embedded credentials!
@staticmethod
def get_admin_password():
"""Retrieve admin password from environment."""
password = os.environ.get('ADMIN_PASSWORD')
if not password:
raise ValueError("ADMIN_PASSWORD environment variable not set")
return password
@staticmethod
@lru_cache(maxsize=1) # Cache to avoid repeated calls
def get_aws_credentials():
"""Get AWS credentials from IAM role or environment."""
# Use IAM roles when running in AWS
# Falls back to environment variables
session = boto3.Session()
credentials = session.get_credentials()
return credentials
@staticmethod
def get_api_key(key_name):
"""Retrieve API key from secrets manager."""
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId=key_name)
return response['SecretString']
@staticmethod
def get_db_config():
"""Get database configuration from secure source."""
return {
'host': os.environ.get('DB_HOST', 'localhost'),
'user': os.environ.get('DB_USER'),
'password': SecureConfig.get_api_key('myapp/db_password'),
'database': os.environ.get('DB_NAME')
}
@staticmethod
def get_encryption_key():
"""Retrieve encryption key from secure storage."""
# Read from file with restricted permissions
key_path = '/etc/myapp/secrets/encryption.key'
# Verify file permissions
import stat
mode = os.stat(key_path).st_mode
if mode & (stat.S_IRWXG | stat.S_IRWXO):
raise PermissionError("Key file has insecure permissions")
with open(key_path, 'rb') as f:
return f.read()
# Fixed: Authentication without embedded secrets
class SecureAuth:
def authenticate(self, password):
stored_hash = self._get_stored_hash()
return bcrypt.checkpw(password.encode(), stored_hash.encode())
def _get_stored_hash(self):
# Retrieve from secure database
return database.get_password_hash(self.username)
The fix retrieves all sensitive data from external secure sources rather than embedding in code.
Exploited in the Wild
DLL-Embedded RSA Private Key (Network Software, 2005)
CVE-2005-1794 documented a product that stored an RSA private key directly in a DLL, enabling attackers to spoof the server and perform man-in-the-middle attacks against all users.
Executable Administration Passwords (Enterprise Software, 2001)
CVE-2001-1527 documented administration passwords stored in cleartext within executables, compromising all deployments of the software.
Tools to Test/Exploit
-
Strings — Extracts printable strings from binary files.
-
IDA Pro — Disassembler for reverse engineering binaries.
-
Ghidra — NSA's open-source reverse engineering suite.
-
TruffleHog — Scans for secrets in binaries and code.
CVE Examples
-
CVE-2005-1794 — RSA private key stored in DLL.
-
CVE-2001-1527 — Administration passwords in cleartext in executable.
References
-
MITRE Corporation. "CWE-318: Cleartext Storage of Sensitive Information in Executable." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/318.html
-
OWASP Foundation. "Cryptographic Storage Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html
-
NIST. "Key Management Guidelines." SP 800-57. https://csrc.nist.gov/publications/detail/sp/800-57-part-1/rev-5/final