Safeguard
Application Security•

CWE-913 Improper Control of Dynamically-Managed Code Resources

eval, reflection and deserialization let input shape running code. CWE-913 explains the risk, the real CVEs, and how to restrict dynamic features.

Safeguard Security Team
4 min read

CWE-913, Improper Control of Dynamically-Managed Code Resources, is a parent class for a whole family of bugs. The product does not properly restrict reading from or writing to dynamically managed code resources such as variables, objects, classes, attributes, functions, or executable instructions or statements.

It is mostly a risk in interpreted languages and in any language with reflection or deserialization. If your code can build or change code at run time, it applies to you.

How it happens

MITRE puts the trade plainly. Many languages let programmers create or modify code, or the resources used by code, while the program runs. That brings flexibility and speeds up development. It also becomes extremely dangerous if attackers can directly influence those code resources in unexpected ways.

Common forms:

  • Evaluating a string as code.
  • Using a class or function name from input to pick what to call.
  • Deserializing data that can name types to instantiate.
  • Changing attributes or variables whose names come from input.
# Vulnerable: input becomes code
result = eval(request.args["formula"])

# Fixed: parse the narrow thing you actually need
import ast
result = ast.literal_eval(request.args["formula"])

literal_eval accepts only literals, such as numbers, strings and lists, not calls or expressions. Whether even that is enough depends on what the feature must do. Often a small parser for an agreed format is better.

The observed CVEs show the same idea in different clothing:

  • CVE-2022-2054 (details): a Python compiler used eval() to execute malicious strings as Python code.
  • CVE-2018-1000613: a cryptography API used unsafe reflection when deserializing a private key.
  • CVE-2015-8103: a deserialization issue in a commonly used Java library allowed remote code execution.
  • CVE-2006-7079: a chain where extract, used for a register_globals compatibility layer, enabled path traversal.

Two of those are bugs in libraries, not in the application using them. That is why dependency hygiene matters here. A free in-browser dependency scan at no login can show whether a library you ship has a published issue. It keeps the file on your machine and covers up to 100 dependencies. Reachability analysis then tells you whether your code actually calls the vulnerable function, which is a good way to sort the findings.

What an attacker gains

MITRE lists two consequences. Integrity: execute unauthorized code or commands. And other impact, varying by context: alter execution logic. In practice, the first is the worst case. The attacker's input runs with the application's privileges.

How to find it

MITRE lists fuzzing as the detection method, with high effectiveness. Fuzz testing generates large numbers of diverse inputs, randomly or algorithmically, and invokes the code with them. Even random inputs often find crashes, memory corruption or resource consumption, and fuzzing produces repeatable test cases that show the bug clearly.

Manual checks go alongside:

  1. Search the code base for eval, exec, reflection calls, dynamic imports, and deserialization of data that crosses a trust boundary.
  2. For each hit, trace where the argument comes from.
  3. List dependencies that deserialize or evaluate, and check them for known issues.

How to fix it

From MITRE:

  1. For any externally influenced input, check it against an allowlist of acceptable values.
  2. Refactor the code so it does not need to be dynamically managed.

The second is the stronger option. If a feature can be written as a lookup table of known functions, it needs no dynamic dispatch at all.

# Dispatch table instead of getattr on a user-supplied name
HANDLERS = {"csv": export_csv, "json": export_json}
handler = HANDLERS.get(fmt)
if handler is None:
    abort(400)
handler(data)

For deserialization, restrict which types can be created, and avoid formats that let the data choose the class.

Quick check

Why is allowlisting preferred?

It limits input to values you decided are safe, instead of trying to list everything that is dangerous.

What is the better fix than validation?

Refactor so dynamic management is not needed, for example a fixed table of allowed functions.

Which detection method does MITRE list?

Fuzzing, rated as highly effective at surfacing crashes, memory corruption and resource consumption with repeatable test cases.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.