Exposure of Sensitive System Information to an Unauthorized Control Sphere

Description

Exposure of Sensitive System Information to an Unauthorized Control Sphere is a vulnerability where a product reveals sensitive system-level information to actors who do not have the same level of access as the product itself. Network-based products like web applications operate on top of operating systems and application servers but should conceal these underlying details from end users. Information such as file paths, operating system users, installed software packages, and application environment details may leak through product communications, diagnostic messages, error responses, or debugging output. Attackers exploit these details to understand system architecture and refine their attack strategies.

Risk

System information disclosure provides attackers with valuable reconnaissance data. Error responses revealing technologies, operating system versions, and product information enable targeting of known vulnerabilities specific to those versions. Stack traces expose implementation details, internal class names, and code structure. File paths reveal directory layouts that aid path traversal attacks. Database connection strings may include credentials. Environment variables might contain API keys or tokens. Process listings show running services and their configurations. This information asymmetry gives attackers significant advantages in planning and executing attacks.

Solution

Configure production systems to never expose internal details such as stack traces, debug messages, or verbose error information to end users. Implement generic, user-friendly error pages that log detailed information server-side only. Sanitize all error messages before display, removing paths, version numbers, and technical details. Use security headers to prevent information leakage. Ensure logging mechanisms don't expose sensitive data through log viewers. HTML entity-encode error messages written to logs to prevent XSS attacks against log viewing interfaces. Separate debug and production configurations.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Read Application Data - Sensitive system information disclosure allows attackers to understand the technology stack, identify potential vulnerabilities, and craft targeted attacks.
OtherScope: Other

Technical Impact: Reconnaissance - Exposed information aids attackers in mapping the system architecture and identifying attack vectors.

Example Code

Vulnerable Code

// Vulnerable: Exposing stack traces to users
import javax.servlet.http.*;
import java.io.*;

public class VulnerableErrorServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws IOException {
        try {
            processRequest(request);
        } catch (Exception e) {
            // Vulnerable: Full stack trace sent to client
            PrintWriter out = response.getWriter();
            out.println("<html><body>");
            out.println("<h1>An error occurred:</h1>");
            out.println("<pre>");
            e.printStackTrace(out);  // Exposes internal details!
            out.println("</pre>");
            out.println("</body></html>");
        }
    }

    // Vulnerable: Exposing system information
    protected void showSystemInfo(HttpServletResponse response)
            throws IOException {
        PrintWriter out = response.getWriter();

        // Vulnerable: Exposing PATH
        out.println("PATH: " + System.getenv("PATH"));

        // Vulnerable: Exposing user
        out.println("User: " + System.getProperty("user.name"));

        // Vulnerable: Exposing OS details
        out.println("OS: " + System.getProperty("os.name") + " " +
                    System.getProperty("os.version"));

        // Vulnerable: Exposing Java version
        out.println("Java: " + System.getProperty("java.version"));

        // Vulnerable: Exposing working directory
        out.println("Dir: " + System.getProperty("user.dir"));
    }
}

// Vulnerable: Database connection string in error messages
public class VulnerableDatabaseService {

    private static final String CONNECTION_STRING =
        "jdbc:mysql://db-server:3306/mydb?user=admin&password=secret123";

    public void connect() {
        try {
            Connection conn = DriverManager.getConnection(CONNECTION_STRING);
        } catch (SQLException e) {
            // Vulnerable: Connection string with credentials logged/displayed
            System.out.println("Failed to connect: " + CONNECTION_STRING);
            throw new RuntimeException("Database error: " + e.getMessage() +
                " Connection: " + CONNECTION_STRING);
        }
    }
}
# Vulnerable: Exposing system info in Python web app
from flask import Flask, request
import traceback
import os
import subprocess

app = Flask(__name__)

@app.route('/status')
def vulnerable_status():
    # Vulnerable: Exposing environment variables
    env_info = "<h2>Environment Variables:</h2><pre>"
    for key, value in os.environ.items():
        env_info += f"{key}={value}\n"  # May include secrets!
    env_info += "</pre>"

    # Vulnerable: Exposing process list
    try:
        ps_output = subprocess.check_output(['ps', 'aux']).decode()
        process_info = f"<h2>Running Processes:</h2><pre>{ps_output}</pre>"
    except:
        process_info = ""

    return env_info + process_info

@app.errorhandler(500)
def vulnerable_error_handler(error):
    # Vulnerable: Full traceback to client
    return f"""
    <html>
    <body>
        <h1>Internal Server Error</h1>
        <h2>Traceback:</h2>
        <pre>{traceback.format_exc()}</pre>
        <h2>Request Details:</h2>
        <pre>
        Path: {request.path}
        Method: {request.method}
        Server: {request.host}
        </pre>
    </body>
    </html>
    """, 500

# Vulnerable: Debug mode in production
if __name__ == '__main__':
    app.run(debug=True)  # Never in production!
<?php
// Vulnerable: Exposing PHP info
function vulnerableDebugPage() {
    // Vulnerable: Full PHP configuration exposed
    phpinfo();  // Shows all PHP settings, paths, modules
}

// Vulnerable: Detailed error messages
function vulnerableQuery($userId) {
    $conn = new mysqli("localhost", "root", "password123", "mydb");

    $query = "SELECT * FROM users WHERE id = " . $userId;

    if (!$result = $conn->query($query)) {
        // Vulnerable: Query and connection details exposed
        die("Query failed: " . $query .
            "\nError: " . $conn->error .
            "\nServer: " . $conn->server_info);
    }

    return $result;
}

// Vulnerable: Exception with system details
try {
    include("/var/www/config/secret.php");
} catch (Exception $e) {
    // Vulnerable: Full path exposed
    echo "Error loading config: " . $e->getMessage();
    echo "\nFile: " . $e->getFile();
    echo "\nLine: " . $e->getLine();
    echo "\nTrace: " . $e->getTraceAsString();
}
?>

Fixed Code

// Fixed: Secure error handling
import javax.servlet.http.*;
import java.io.*;
import java.util.logging.*;

public class SecureErrorServlet extends HttpServlet {
    private static final Logger logger = Logger.getLogger(
        SecureErrorServlet.class.getName()
    );

    @Override
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws IOException {
        try {
            processRequest(request);
        } catch (Exception e) {
            // Fixed: Log detailed error server-side
            String errorId = generateErrorId();
            logger.log(Level.SEVERE,
                "Error ID " + errorId + ": " + e.getMessage(), e);

            // Fixed: Generic message to client
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            PrintWriter out = response.getWriter();
            out.println("<html><body>");
            out.println("<h1>An error occurred</h1>");
            out.println("<p>Please contact support with error ID: " +
                        escapeHtml(errorId) + "</p>");
            out.println("</body></html>");
        }
    }

    // Fixed: Never expose system information
    protected void showStatus(HttpServletResponse response)
            throws IOException {
        PrintWriter out = response.getWriter();

        // Fixed: Only expose necessary, non-sensitive info
        out.println("Application Status: Running");
        out.println("Version: 1.0");
        out.println("Health: OK");

        // Fixed: No paths, versions, users, or environment details
    }

    private String generateErrorId() {
        return "ERR-" + System.currentTimeMillis() + "-" +
               (int)(Math.random() * 10000);
    }

    private String escapeHtml(String input) {
        return input.replace("&", "&amp;")
                    .replace("<", "&lt;")
                    .replace(">", "&gt;")
                    .replace("\"", "&quot;");
    }
}

// Fixed: Secure database service
public class SecureDatabaseService {
    private static final Logger logger = Logger.getLogger(
        SecureDatabaseService.class.getName()
    );

    // Fixed: Credentials from secure configuration
    private String getConnectionString() {
        return SecureConfig.getDatabaseUrl();  // No inline credentials
    }

    public void connect() {
        try {
            Connection conn = DriverManager.getConnection(getConnectionString());
        } catch (SQLException e) {
            // Fixed: Log full details server-side only
            logger.log(Level.SEVERE, "Database connection failed", e);

            // Fixed: Generic message, no connection details
            throw new ServiceException("Database temporarily unavailable");
        }
    }
}
# Fixed: Secure error handling in Flask
from flask import Flask, request, render_template
import logging
import uuid
import os

app = Flask(__name__)

# Fixed: Configure secure logging
logging.basicConfig(
    filename='/var/log/app/error.log',
    level=logging.ERROR,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

@app.route('/status')
def secure_status():
    # Fixed: Only expose safe, non-sensitive info
    return {
        "status": "healthy",
        "version": "1.0.0",
        "uptime": get_uptime_safe()
    }

@app.errorhandler(500)
def secure_error_handler(error):
    # Fixed: Generate error ID for tracking
    error_id = str(uuid.uuid4())[:8]

    # Fixed: Log full details server-side
    logger.error(f"Error {error_id}: {error}", exc_info=True)

    # Fixed: Generic message to client
    return render_template('error.html', error_id=error_id), 500

@app.errorhandler(Exception)
def handle_exception(e):
    error_id = str(uuid.uuid4())[:8]
    logger.exception(f"Unhandled exception {error_id}")

    # Fixed: Never expose exception details
    return {
        "error": "An unexpected error occurred",
        "error_id": error_id,
        "message": "Please contact support with this error ID"
    }, 500

def get_uptime_safe():
    # Fixed: Return sanitized uptime without system details
    try:
        with open('/proc/uptime', 'r') as f:
            uptime_seconds = float(f.readline().split()[0])
            return f"{int(uptime_seconds // 3600)} hours"
    except:
        return "unknown"

# Fixed: Never run with debug=True in production
if __name__ == '__main__':
    debug_mode = os.environ.get('FLASK_DEBUG', 'false').lower() == 'true'
    if os.environ.get('ENVIRONMENT') == 'production':
        debug_mode = False  # Force disable in production
    app.run(debug=debug_mode)
<?php
// Fixed: Secure PHP configuration
ini_set('display_errors', 0);  // Never display errors to users
ini_set('log_errors', 1);      // Log errors instead
ini_set('error_log', '/var/log/php/error.log');

// Fixed: Custom error handler
function secureErrorHandler($errno, $errstr, $errfile, $errline) {
    $errorId = uniqid('ERR-');

    // Fixed: Log full details server-side
    error_log("[$errorId] Error $errno: $errstr in $errfile:$errline");

    // Fixed: Generic message to user
    if (!headers_sent()) {
        header('HTTP/1.1 500 Internal Server Error');
    }

    echo json_encode([
        'error' => 'An error occurred',
        'error_id' => $errorId,
        'message' => 'Please contact support'
    ]);

    return true;  // Don't execute PHP's internal error handler
}
set_error_handler('secureErrorHandler');

// Fixed: Secure database queries
function secureQuery($userId) {
    $conn = getSecureConnection();  // Connection from secure config

    // Fixed: Prepared statement
    $stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->bind_param("i", $userId);

    if (!$stmt->execute()) {
        $errorId = uniqid('DB-');
        error_log("[$errorId] Database error: " . $stmt->error);

        // Fixed: Generic message
        throw new Exception("Database error. Reference: $errorId");
    }

    return $stmt->get_result();
}

// Fixed: Secure exception handling
try {
    $config = loadSecureConfig();
} catch (Exception $e) {
    $errorId = uniqid('CFG-');

    // Fixed: Log details, show generic message
    error_log("[$errorId] Config error: " . $e->getMessage() .
              " in " . $e->getFile() . ":" . $e->getLine());

    http_response_code(500);
    echo "Configuration error. Reference: $errorId";
}
?>

CVE Examples

  • CVE-2021-32638: Code analysis product exposed access tokens via command-line parameters or environment variables, making them visible through the ps command to other users on the same system.

References

  1. MITRE Corporation. "CWE-497: Exposure of Sensitive System Information to an Unauthorized Control Sphere." https://cwe.mitre.org/data/definitions/497.html
  2. OWASP. "Information Exposure Through Error Messages."
  3. OWASP. "Error Handling Cheat Sheet."