Safeguard
Application Security•

CWE-74 Injection: Special Elements Sent Downstream, and the Fix

CWE-74 is the parent of SQL, command, LDAP and script injection. Learn the shared pattern, what it costs, and how to stop it at the boundary where data meets code.

Safeguard Security Team
6 min read

CWE-74 is the umbrella over most injection bugs. MITRE defines it as a product that builds all or part of a command, data structure or record from externally influenced input, without neutralizing, or while incorrectly neutralizing, special elements that could change how it is parsed or interpreted when sent to a downstream component.

If you have heard of SQL injection, OS command injection, LDAP injection or cross-site scripting, you have met children of this class. Learning the parent is useful because the same fix logic applies to all of them, including injection forms that have no famous name yet.

How it happens

Every injection bug has the same three parts.

  1. An upstream component hands your code some data. It might be a form field, a header, a file name, a message, or text produced by another service.
  2. Your code places that data inside something a downstream component will interpret: a query, a shell line, a markup page, a config file, a prompt.
  3. The data contains characters that the downstream component treats as syntax rather than as plain content.

The downstream component cannot tell which parts you meant as instructions and which parts came from outside. It parses the whole thing. The attacker's text becomes part of the instruction.

The data structure case is easy to overlook, so here is a small Python illustration. Building JSON by joining strings lets a quote in the input break out of the value:

# Vulnerable: input can add or change fields
body = '{"user": "' + name + '", "role": "viewer"}'

# Safer: let the serializer handle the special characters
import json
body = json.dumps({"user": name, "role": "viewer"})

The serializer knows the downstream syntax and escapes for it. Your string concatenation does not.

The observed examples in the MITRE record show how wide the class is:

  • CVE-1999-0067 is called the canonical example of OS command injection: a CGI program does not neutralize the "|" metacharacter when invoking a phonebook program.
  • CVE-2022-1509 is injection of sed script syntax.
  • CVE-2022-36069 is a Python dependency management tool that avoids OS command injection when generating Git commands but still allows injection of optional arguments through input that begins with a dash.
  • CVE-2024-5184 is an API service using a large generative AI model that allows direct prompt injection to leak hard-coded system prompts or execute other prompts.

Gold pages: CVE-1999-0067, CVE-2022-1509, CVE-2022-36069 and CVE-2024-5184.

The last one is a reminder that the downstream component does not have to be a classic interpreter. A model that reads a prompt is also a downstream component, and text that mixes your instructions with outside content has the same structural problem.

What an attacker gains

MITRE lists five kinds of impact, and most injection attacks hit more than one.

  • Confidentiality. Many injection attacks disclose important information, both in sensitivity and in usefulness for further exploitation.
  • Access control. In some cases injectable code controls authentication, which can lead to a remote vulnerability.
  • Execution logic. Injection can significantly change the flow of a process, and in some cases lead to execution of arbitrary code.
  • Integrity. Data injection leads to loss of integrity in nearly all cases, because the control-plane data that gets injected is incidental to data recall or writing.
  • Non-repudiation. The actions performed by injected control code are often unlogged, so the attacker can hide activities.

How to find it

MITRE points to static analysis, commonly called SAST. It can find some instances by analyzing source or compiled code without running it, usually by building a model of data flow from input sources to sensitive operations. That fits the problem well: an injection bug is a path from outside data into an interpreter call with nothing neutralizing it on the way.

You can add a manual pass. List every place your code calls an interpreter: a database driver, a shell, a template engine, an LDAP or other directory client, an HTTP request builder, a model API. For each one, ask where the inputs came from and what stands between them and the call.

Click through it at your own pace, or open it full screen.

How to fix it

The MITRE record for this class is short and general. It gives two points.

  1. Choose technologies that do not have the problem. At the requirements stage, programming languages and supporting technologies might be chosen which are not subject to these issues.
  2. Filter control syntax from input. During implementation, use an appropriate mix of allowlist and denylist parsing to filter control-plane syntax from all input.

The more specific children of this class give sharper guidance, and they agree on the idea. Keep data and code apart. Use structured interfaces that do the quoting for you, such as parameterized queries and argument arrays instead of one big string. Validate against what you expect, not against what you fear. And treat any output that goes to another component as something to encode for that component's syntax.

A few habits help in daily work:

  • Prefer a function that takes separate arguments over one that takes a single command string.
  • Build structured data with a serializer, not by concatenation.
  • Reject input that does not match the exact shape you expect, rather than trying to scrub it.
  • Remember that denylists miss things. MITRE's own text calls for a mix with allowlists.

Injection also arrives through dependencies. A library that builds a Git command or a query string internally can carry the bug even when your code is clean. A free in-browser dependency scan at gold.safeguard.sh/scan lets you check a manifest without logging in, and the file stays on your machine (up to 100 dependencies). Reachability analysis then helps you see whether your code actually calls the vulnerable function, which tells you which findings to fix first.

Quick check

What do SQL injection, OS command injection and XSS have in common?

Each builds something a downstream component will interpret using outside input, without neutralizing special elements. They are all specific forms of CWE-74.

Why does MITRE suggest a mix of allowlist and denylist parsing?

An allowlist defines exactly what is acceptable. A denylist alone tends to miss at least one dangerous input, so it works better as a supplement than as the only control.

Is prompt injection in scope?

The record lists CVE-2024-5184, where direct prompt injection leaks system prompts. A model reading your text is a downstream component, so the same structural issue applies.

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.