CWE-497 is the exposure of sensitive system-level information to people who do not have the same level of access to the underlying system as the product does. Web applications are the usual victims, but MITRE marks it as neither language nor technology specific.
Think of it as the app saying too much about the machine it runs on. File paths, other operating system users, installed packages, environment details and version numbers all count.
How it happens
Network facing products run on top of an operating system or similar environment. When they talk to outside parties, details about that environment are expected to stay hidden. MITRE lists the kinds of details: path names for data files, other OS users, installed packages and the application environment.
The information leaks in two main ways. The product may hand it over directly. Or it ends up buried in diagnostic and debugging messages. An information exposure occurs when system data or debugging information leaves the program through an output stream or logging function that unauthorized parties can see.
Errors are the biggest source. MITRE notes that an attacker can use other weaknesses to cause errors, and the response to those errors can reveal detailed system information. A product may use diagnostic methods that give significant implementation details, such as stack traces, as part of its error handling.
Here is the pattern in Python with Flask:
# Vulnerable: debug mode and raw exception text go to the client
app.run(debug=True)
@app.errorhandler(Exception)
def handle(e):
return str(e) + "\n" + traceback.format_exc(), 500
The response now includes file paths, library names and line numbers. The same thing happens when a framework runs in a development configuration in production.
A second pattern shows up in the record. CVE-2021-32638 is described as a code analysis product that passes access tokens as a command line parameter or through an environment variable, which makes them visible to other processes through the ps command. That is system level exposure to a different audience: other local processes rather than remote users.
What an attacker gains
MITRE lists one consequence: reading application data, a loss of confidentiality.
On its own the leak may look minor. Its value is in planning. MITRE explains that debugging information helps an adversary learn about the system and form an attack plan. Messages that reveal technologies, operating systems and product versions let an attacker tune the attack to known vulnerabilities in those technologies. A version banner turns a blind guess into a targeted one.
It also makes other bugs worse. An injection flaw that returns database errors is far easier to exploit than one that returns a generic failure.
How to find it
The MITRE record lists automated static analysis, which can find some instances by modeling data flow from sources of system information to outputs. Add these simple checks:
- Trigger errors on purpose in a test environment: send malformed input, request a missing page, break a database connection. Read the full response body and headers.
- Search the code for places that return exception text, call
printStackTrace, or writetracebackoutput to a response. - Check that debug flags and development settings are off in every production configuration.
- Look at response headers such as
ServerandX-Powered-Byfor product and version detail. - Review how secrets and tokens are passed to child processes. Command line arguments are visible to other local users on many systems.
A dependency scan helps with the other half. Version information is only dangerous because known vulnerabilities exist in those versions. The free in-browser scan lists known issues in the dependencies in your manifest, with no login and up to 100 dependencies. Reachability analysis then shows whether your code actually calls the vulnerable function, which helps you decide what to fix first.
How to fix it
MITRE's mitigation is explicit: production applications should never use methods that generate internal details such as stack traces and error messages, unless that information is committed directly to a log that the end user cannot view. It adds that error message text should be HTML entity encoded before it is written to the log, to protect against cross-site scripting aimed at whoever reads the logs.
Turn that into steps.
- Return a generic message and a short error id to the client.
- Write the full detail to a server side log that only operators can read.
- Encode message text before logging it if the log is shown in a web viewer.
- Disable debug mode in production and test that it is off.
- Remove or generalize version banners where you can.
- Pass secrets through files with tight permissions or a secrets manager, rather than command line arguments.
import logging, uuid
log = logging.getLogger("app")
@app.errorhandler(Exception)
def handle(e):
error_id = uuid.uuid4().hex[:8]
log.exception("unhandled error %s", error_id) # detail stays on the server
return {"error": "Internal error", "id": error_id}, 500
The user sees an id they can quote to support. You find the trace by searching your logs for it.
Quick check
Is a stack trace in an error page a real problem if nothing secret is in it?
Yes. Paths, library names and versions help an attacker choose known exploits and plan the next step. Keep detail in a private log.
Where should detailed error information go in production?
Into a log the end user cannot view. The client gets a generic message.
Why are command line arguments risky for tokens?
Other processes on the same system can see them, for example through ps, as in the CVE listed above.