Debug Messages Revealing Unnecessary Information
Description
Debug Messages Revealing Unnecessary Information occurs when a product fails to adequately prevent the revealing of unnecessary and potentially sensitive system information within debugging messages. Debug messages help troubleshoot issues by revealing internal system state, such as through memory dumps or boot logs via interfaces like UART or scan chains. While detailed debug information aids troubleshooting, it risks exposing details that could help attackers understand vulnerabilities or system architecture. Though "security by obscurity" alone is insufficient, limiting debug information supports a "defense-in-depth" strategy.
Risk
Verbose debug messages have significant security implications. Memory contents exposed. System architecture revealed. Security bypass techniques disclosed. Pointer information leaked enabling ASLR bypass. Cryptographic keys exposed. Internal paths revealed. Authentication details disclosed. Privilege levels disclosed. Attack surface exposed through detailed technical information. Medium likelihood of exploitation.
Solution
Ensure that debug messages do not reveal unnecessary information during the debug process for the intended response. Implement different verbosity levels for debug and production. Strip sensitive information from error responses. Sanitize memory addresses before output. Never include cryptographic material in debug output. Review all debug outputs during security audits. Disable or restrict debug interfaces in production. Use secure debug authentication.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Sensitive memory contents and cryptographic keys may be exposed. |
| Access Control | Scope: Access Control Protection mechanisms may be bypassed using revealed information. |
| Authentication | Scope: Authentication, Authorization Credential and session information may be exposed. |
Example Code
Vulnerable Code
// Vulnerable: Debug messages revealing sensitive information
#include <stdio.h>
#include <stdint.h>
#include <string.h>
// VULNERABLE: Debug function exposes memory addresses
void vulnerable_debug_memory_error(void* address, size_t size, int error_code) {
// VULNERABLE: Exposes actual memory addresses (defeats ASLR)
printf("[DEBUG] Memory error at address %p, size %zu, code %d\n",
address, size, error_code);
// VULNERABLE: Memory dump includes sensitive data
printf("[DEBUG] Memory contents:\n");
hexdump(address, size); // Could contain keys, passwords, etc.
}
// VULNERABLE: Boot log reveals system configuration
void vulnerable_boot_log(void) {
// VULNERABLE: Reveals exact memory layout
printf("[BOOT] Code region: 0x%08lx - 0x%08lx\n",
(unsigned long)code_start, (unsigned long)code_end);
printf("[BOOT] Data region: 0x%08lx - 0x%08lx\n",
(unsigned long)data_start, (unsigned long)data_end);
printf("[BOOT] Stack base: 0x%08lx\n", (unsigned long)stack_base);
// VULNERABLE: Reveals security configuration
printf("[BOOT] Secure boot: %s\n", secure_boot_enabled ? "enabled" : "disabled");
printf("[BOOT] Debug fuse: %s\n", debug_fuse_blown ? "blown" : "intact");
// VULNERABLE: Reveals cryptographic key storage location
printf("[BOOT] Key storage at: 0x%08lx\n", (unsigned long)key_storage);
}
// VULNERABLE: Authentication debug reveals too much
int vulnerable_auth_debug(const char* username, const char* password) {
// VULNERABLE: Reveals password storage location
const char* stored_password = get_stored_password(username);
if (stored_password == NULL) {
printf("[AUTH DEBUG] User not found in database at %s\n",
user_database_path); // VULNERABLE: Reveals path
return -1;
}
if (strcmp(password, stored_password) != 0) {
// VULNERABLE: Reveals expected password length and location
printf("[AUTH DEBUG] Password mismatch for user '%s'\n", username);
printf("[AUTH DEBUG] Expected password stored at %p (len=%zu)\n",
stored_password, strlen(stored_password));
printf("[AUTH DEBUG] Expected first char: '%c'\n",
stored_password[0]); // VULNERABLE: Partial password!
return -1;
}
printf("[AUTH DEBUG] Successful auth, session key: 0x%016llx\n",
session_key); // VULNERABLE: Exposes session key!
return 0;
}
// VULNERABLE: TAP response reveals internal structure
void vulnerable_tap_response(int command, int status) {
// VULNERABLE: Reveals TAP hierarchy when only success/failure needed
printf("TAP Response:\n");
printf(" Chain: CPU_TAP -> DEBUG_TAP -> TRACE_TAP\n");
printf(" IR length: %d bits\n", ir_length);
printf(" DR length: %d bits\n", dr_length);
printf(" Command: 0x%04x\n", command);
printf(" Internal state: 0x%08x\n", internal_state);
printf(" Status: %s\n", status ? "SUCCESS" : "FAILURE");
}
# Vulnerable: Python debug logging with sensitive data
import logging
import traceback
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
class VulnerablePaymentProcessor:
def __init__(self):
self.api_key = "sk_live_abc123def456" # Production API key
def process_payment(self, card_number, cvv, amount):
# VULNERABLE: Logs credit card details
logger.debug(f"Processing payment: card={card_number}, cvv={cvv}, amount={amount}")
try:
result = self._call_payment_api(card_number, cvv, amount)
# VULNERABLE: Logs API response with sensitive data
logger.debug(f"API response: {result}")
return result
except Exception as e:
# VULNERABLE: Full stack trace may reveal internal paths
logger.debug(f"Payment failed: {traceback.format_exc()}")
# VULNERABLE: Reveals API key in error
logger.debug(f"Used API key: {self.api_key}")
raise
class VulnerableCryptoHandler:
def decrypt(self, ciphertext, key):
# VULNERABLE: Logs cryptographic key
logger.debug(f"Decrypting with key: {key.hex()}")
try:
plaintext = self._perform_decryption(ciphertext, key)
# VULNERABLE: Logs decrypted sensitive data
logger.debug(f"Decrypted plaintext: {plaintext}")
return plaintext
except Exception as e:
# VULNERABLE: Reveals internal memory state
logger.debug(f"Decryption failed, key buffer at: {id(key):#x}")
logger.debug(f"Ciphertext length: {len(ciphertext)}")
raise
Fixed Code
// Fixed: Debug messages without sensitive information
#include <stdio.h>
#include <stdint.h>
#include <string.h>
// Debug verbosity levels
typedef enum {
DEBUG_LEVEL_NONE = 0,
DEBUG_LEVEL_ERROR = 1,
DEBUG_LEVEL_INFO = 2,
DEBUG_LEVEL_VERBOSE = 3
} debug_level_t;
// Current debug level - set at compile time for production
#ifdef PRODUCTION_BUILD
static const debug_level_t current_level = DEBUG_LEVEL_ERROR;
#else
static debug_level_t current_level = DEBUG_LEVEL_VERBOSE;
#endif
// FIXED: Debug function without sensitive addresses
void secure_debug_memory_error(int region_id, int error_code) {
if (current_level < DEBUG_LEVEL_ERROR) return;
// FIXED: Use region ID instead of actual address
printf("[ERROR] Memory error in region %d, code %d\n",
region_id, error_code);
// FIXED: No memory dump in production
#ifndef PRODUCTION_BUILD
// Only in debug builds, with sanitization
if (current_level >= DEBUG_LEVEL_VERBOSE && is_debug_authenticated()) {
printf("[DEBUG] Additional info available via secure debug\n");
}
#endif
}
// FIXED: Boot log without sensitive details
void secure_boot_log(void) {
// FIXED: Only reveal necessary boot status
printf("[BOOT] System initializing...\n");
// FIXED: Generic status without addresses
printf("[BOOT] Memory: OK\n");
printf("[BOOT] Peripherals: OK\n");
// FIXED: Don't reveal security configuration
// Security status is need-to-know only
if (is_debug_authenticated()) {
// Even authenticated debug gets limited info
printf("[BOOT] Security subsystem: initialized\n");
}
}
// FIXED: Authentication debug without revealing secrets
int secure_auth_debug(const char* username, const char* password) {
(void)password; // Don't log password at all
// FIXED: Generic error messages
if (!user_exists(username)) {
log_auth_event("AUTH_USER_NOT_FOUND", username);
return -1; // Don't reveal database location or structure
}
if (!verify_password(username, password)) {
log_auth_event("AUTH_PASSWORD_MISMATCH", username);
// FIXED: Don't reveal expected password or its properties
return -1;
}
// FIXED: Don't log session keys
log_auth_event("AUTH_SUCCESS", username);
return 0;
}
// FIXED: TAP response with minimal information
void secure_tap_response(int status) {
// FIXED: Only success/failure, no internal details
if (status) {
printf("TAP: OK\n");
} else {
printf("TAP: FAIL\n");
}
// FIXED: Detailed info only via authenticated secure debug
#ifndef PRODUCTION_BUILD
if (is_tap_debug_authenticated()) {
// Limited additional info for authenticated debug
printf("TAP: Additional diagnostics available\n");
}
#endif
}
// FIXED: Secure debug with authentication
typedef struct {
uint8_t challenge[32];
uint8_t response[32];
bool authenticated;
} debug_session_t;
static debug_session_t debug_session = {0};
bool is_debug_authenticated(void) {
return debug_session.authenticated;
}
bool authenticate_debug_session(const uint8_t* response) {
// Verify challenge-response before enabling verbose debug
if (verify_debug_challenge(debug_session.challenge, response)) {
debug_session.authenticated = true;
return true;
}
return false;
}
# Fixed: Python secure debug logging
import logging
import hashlib
import os
class SanitizedFormatter(logging.Formatter):
"""Custom formatter that sanitizes sensitive data."""
SENSITIVE_PATTERNS = [
'password', 'key', 'secret', 'token', 'card', 'cvv', 'ssn'
]
def format(self, record):
msg = super().format(record)
# Sanitize potential sensitive data
for pattern in self.SENSITIVE_PATTERNS:
# Replace values that look like they contain sensitive data
import re
msg = re.sub(
rf'{pattern}["\']?\s*[:=]\s*["\']?[^"\'\s,}}]+',
f'{pattern}=[REDACTED]',
msg,
flags=re.IGNORECASE
)
return msg
# FIXED: Production logger configuration
def get_secure_logger(name):
logger = logging.getLogger(name)
# FIXED: INFO level in production, DEBUG only in dev
if os.environ.get('PRODUCTION'):
logger.setLevel(logging.INFO)
else:
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setFormatter(SanitizedFormatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
))
logger.addHandler(handler)
return logger
logger = get_secure_logger(__name__)
class SecurePaymentProcessor:
def __init__(self):
self.api_key = os.environ.get('PAYMENT_API_KEY')
def process_payment(self, card_number, cvv, amount):
# FIXED: Log only non-sensitive identifiers
masked_card = f"****{card_number[-4:]}" if len(card_number) >= 4 else "****"
logger.info(f"Processing payment: card={masked_card}, amount={amount}")
try:
result = self._call_payment_api(card_number, cvv, amount)
# FIXED: Log only transaction status
logger.info(f"Payment completed: transaction_id={result.get('id')}")
return result
except Exception as e:
# FIXED: Generic error without internal details
logger.error(f"Payment failed: {type(e).__name__}")
# FIXED: Log to separate secure audit log if needed
self._audit_log_payment_failure(masked_card, amount)
raise
class SecureCryptoHandler:
def decrypt(self, ciphertext, key):
# FIXED: Never log cryptographic keys
logger.debug(f"Decrypting data, ciphertext_length={len(ciphertext)}")
try:
plaintext = self._perform_decryption(ciphertext, key)
# FIXED: Never log decrypted sensitive data
logger.debug("Decryption successful")
return plaintext
except Exception as e:
# FIXED: Generic error without memory details
logger.error(f"Decryption failed: {type(e).__name__}")
raise
def _audit_log(self, operation, success):
"""Secure audit log without sensitive details."""
# Log only operation type and outcome
audit_entry = {
'operation': operation,
'success': success,
'timestamp': time.time()
}
secure_audit_write(audit_entry)
CVE Examples
- CVE-2021-25476: DRM leaks pointer information, enabling ASLR bypass.
- CVE-2020-24491: Processor debug message contains memory transaction addresses.
- CVE-2017-18326: Modem debug messages include cryptographic keys.
Related CWEs
- CWE-200: Exposure of Sensitive Information to an Unauthorized Actor (parent)
- CWE-209: Generation of Error Message Containing Sensitive Information (peer)
- CWE-1207: Debug and Test Problems (category)
- CWE-532: Insertion of Sensitive Information into Log File (related)
References
- MITRE Corporation. "CWE-1295: Debug Messages Revealing Unnecessary Information." https://cwe.mitre.org/data/definitions/1295.html
- OWASP. "Information Exposure Through Debug Information"
- NIST. "SP 800-123 Guide to General Server Security"