CWE-99, Improper Control of Resource Identifiers (Resource Injection), is a class level weakness. The product takes input from an upstream component and uses it as an identifier for a resource that may be outside the intended sphere of control, without restricting that input.
Think of any name your program accepts and then acts on: a filename, a port number, a socket path, a queue name, a config profile. If a user can influence that name, the program may reach something the author never intended.
How it happens
MITRE describes two conditions that must both be true. First, an attacker can specify the identifier used to access a system resource, for instance part of a filename to open or a port number to use. Second, by specifying the resource the attacker gains a capability they would not otherwise have, such as overwriting the file, running with an attacker controlled configuration, or sending sensitive information to a third party server.
# Vulnerable: the user picks the output file name
def save_report(name, data):
with open("/var/reports/" + name, "w") as f:
f.write(data)
# name = "../../etc/cron.d/job" reaches outside the reports folder
A related example from MITRE's observed list is CVE-2013-4787: a mobile OS verified the cryptographic signature of a file in an archive, then installed a different file with the same name that was also listed in the archive. The identifier, a file name, no longer pointed to the thing that had been checked.
What an attacker gains
MITRE lists confidentiality and integrity impact. The attacker can read or modify application data and read or modify files or directories, which may include protected files, directories and configuration files.
How to find it
MITRE says static analysis can find some instances by following data flow. Manually, list every API call that takes a name or number from outside: file open, socket connect, bind, database selection, queue or topic names. Trace each identifier to its source.
How to fix it
MITRE's mitigation is input validation using an accept known good strategy. Assume all input is malicious, define exactly which identifiers are valid, and reject everything else. Check length, type, range, syntax and business rules. Do not rely only on denylists, though they can help flag attacks.
import re
SAFE_NAME = re.compile(r"[a-z0-9_]{1,40}")
def save_report(name, data):
if not SAFE_NAME.fullmatch(name):
raise ValueError("invalid report name")
with open("/var/reports/" + name + ".txt", "w") as f:
f.write(data)
Better still, let the server generate the identifier and give the user an ID to refer to it later.
Quick check
Is resource injection only about files?
No. The identifier can be a port number, a socket, or any name used to reach a resource outside the intended scope.