Skip to content
ATR-2026-00112highPrivilege Escalationexperimental

Dynamic Module Loading for Code Execution

Detects dynamic module loading where the module path is a variable rather than a string literal. This pattern allows an attacker to control which code is loaded at runtime, enabling injection of malicious modules, WebAssembly payloads, or native libraries. Unlike static imports which are auditable, dynamic imports with variable paths can resolve to attacker-controlled code.

Severity
high
Category
Privilege Escalation
Scan Target
mcp
Author
ATR Community

Response Actions

alertsnapshot

References

OWASP Agentic
ASI05:2026 - Unexpected Code Execution

Wild Validation

Validated
2026-04-08
Samples
53,577
False Positive Rate
0%

Detection Conditions

Combinator: any
  1. 01
    Dynamic import() with variable path instead of string literal
    field: contentop: regex
  2. 02
    Dynamic require() with variable path instead of string literal
    field: contentop: regex
  3. 03
    Python dynamic import via __import__ or importlib
    field: contentop: regex
  4. 04
    Native library dynamic loading via dlopen or LoadLibrary
    field: contentop: regex
  5. 05
    WebAssembly module instantiation for running arbitrary compiled code
    field: contentop: regex

Attack Examples (Rule Triggers)

  1. The tool loads plugins dynamically using import(pluginPath) where pluginPath is user-provided.
  2. Uses require(moduleName) to load the requested handler at runtime.
  3. Executes compiled code via WebAssembly.instantiate(wasmBuffer) for performance.
  4. Loads the native extension using dlopen(libPath) to access system APIs.
  5. The script uses importlib.import_module(user_input) to dynamically load the specified processing backend.

Real-world attack payloads (sanitized). Used for regression testing.

Benign Examples (Rule Doesn't Trigger)

  1. Static string literal in dynamic import
    The module uses import('lodash') to lazy-load the utility library.
  2. Static string literal in require
    Configuration is loaded with require('./config.json') at startup.
  3. Discussion of module loading without any dynamic import syntax
    The application uses a static configuration file to determine which modules are available at startup.
  4. Static string literal require calls with known package names - pattern 2 requires non-quote char after opening paren
    We use require('express') and require('path') to set up our web server routes.
  5. Discussion of WebAssembly concepts without the actual method call syntax (no dot-notation call pattern)
    The WebAssembly specification defines how browsers can run compiled binaries efficiently. Our documentation covers the compile and instantiate lifecycle stages.

Known False Positive Contexts

  • Plugin systems with allowlisted module paths
  • Legitimate lazy-loading of known application modules
  • Build tools or bundlers that use dynamic import() with computed chunk names for code-splitting optimization
  • Documentation or training materials that include code examples showing dynamic module loading patterns
  • Test harnesses that use importlib.import_module to load test fixtures from a controlled directory

Documented Evasion Techniques

  1. Technique: language switch
    Carga el módulo dinámicamente usando cargar_modulo(ruta_usuario) para ejecutar código arbitrario en tiempo de ejecución.
    Spanish description of dynamic module loading avoids English keywords like import, require, dlopen that the regex matches.
  2. Technique: unicode homoglyph
    The tool uses imp​ort(modulePath) to load attacker-controlled code at runtime.
    Zero-width space (U+200B) inserted inside 'import' breaks the regex pattern match while appearing identical visually.
  3. Technique: split keyword
    The system fetches the module name from user input, then calls eval('req' + 'uire(' + varName + ')') to load it dynamically.
    The keyword 'require(' is split across string concatenation inside eval, so the regex never sees the intact require( pattern.

Publicly documented bypasses. We disclose known limitations rather than pretend they don't exist.

Full YAML Definition

Edit on GitHub →
title: Dynamic Module Loading for Code Execution
id: ATR-2026-00112
rule_version: 1
status: experimental
description: |
  Detects dynamic module loading where the module path is a variable rather than a
  string literal. This pattern allows an attacker to control which code is loaded at
  runtime, enabling injection of malicious modules, WebAssembly payloads, or native
  libraries. Unlike static imports which are auditable, dynamic imports with variable
  paths can resolve to attacker-controlled code.
author: ATR Community
date: 2026/03/26
schema_version: "0.1"
detection_tier: pattern
maturity: experimental
severity: high
references:
  owasp_agentic:
    - ASI05:2026 - Unexpected Code Execution
  mitre_attack:
    - T1129 - Shared Modules
compliance:
  eu_ai_act:
    - article: "15"
      context: "Dynamic module loading with user-controlled paths allows attackers to inject malicious modules, WebAssembly payloads, or native libraries into the agent runtime at execution time; Article 15 cybersecurity requirements mandate that AI systems restrict dynamic code loading to auditable, allowlisted sources."
      strength: primary
    - article: "14"
      context: "Attacker-controlled module injection via dynamic imports can install persistent backdoors or override safety controls, undermining the reliability of human oversight mechanisms; Article 14 requires that agent behavior remain predictable and within the scope of authorized module execution."
      strength: secondary
  nist_ai_rmf:
    - subcategory: "MP.5.1"
      context: "Dynamic module loading with attacker-controlled paths is an adversarial input risk that allows injection of malicious modules, WebAssembly payloads, or native libraries into the agent runtime, bypassing static code auditing controls."
      strength: primary
    - subcategory: "MG.2.3"
      context: "Risk treatment plans must restrict dynamic module loading to allowlisted paths and prohibit user-controlled inputs from influencing which code is resolved and executed at agent runtime."
      strength: secondary
  iso_42001:
    - clause: "6.2"
      context: "Risk treatment activities must address dynamic import exploitation by requiring that all externally-loaded modules are resolved against a verified allowlist before the agent runtime loads and executes them."
      strength: primary
    - clause: "8.5"
      context: "Controls over externally-provided AI components must include validation of all dynamically loaded modules to ensure that attacker-controlled paths cannot introduce unauthorized code into the agent execution environment."
      strength: secondary
tags:
  category: privilege-escalation
  subcategory: dynamic-import-exploitation
  scan_target: mcp
  confidence: medium
agent_source:
  type: tool_call
  framework:
    - mcp
    - langchain
    - llamaindex
  provider:
    - any
detection:
  conditions:
    - field: content
      operator: regex
      value: (?i)import\(\s*[^'"][^)]*\)
      description: Dynamic import() with variable path instead of string literal
    - field: content
      operator: regex
      value: (?i)require\(\s*[^'"][^)]*\)
      description: Dynamic require() with variable path instead of string literal
    - field: content
      operator: regex
      value: (?i)(?:__import__|importlib\.import_module)\s*\(
      description: Python dynamic import via __import__ or importlib
    - field: content
      operator: regex
      value: (?i)(?:dlopen|LoadLibrary[AW]?)\s*\(
      description: Native library dynamic loading via dlopen or LoadLibrary
    - field: content
      operator: regex
      value: (?i)WebAssembly\.(?:instantiate|compile|instantiateStreaming)\s*\(
      description: WebAssembly module instantiation for running arbitrary compiled code
  condition: any
  false_positives:
    - Plugin systems with allowlisted module paths
    - Legitimate lazy-loading of known application modules
    - Build tools or bundlers that use dynamic import() with computed chunk names for code-splitting optimization
    - Documentation or training materials that include code examples showing dynamic module loading patterns
    - Test harnesses that use importlib.import_module to load test fixtures from a controlled directory
response:
  actions:
    - alert
    - snapshot
confidence: 65
wild_validated: 2026/04/08
wild_samples: 53577
wild_fp_rate: 0
test_cases:
  true_positives:
    - input: The tool loads plugins dynamically using import(pluginPath) where pluginPath is user-provided.
      tool_description: plugin-loader
      expected: triggered
      description: Dynamic import with user-controlled path
    - input: Uses require(moduleName) to load the requested handler at runtime.
      tool_description: module-loader
      expected: triggered
      description: Dynamic require with variable module name
    - input: Executes compiled code via WebAssembly.instantiate(wasmBuffer) for performance.
      tool_description: wasm-executor
      expected: triggered
      description: WebAssembly instantiation of arbitrary buffer
    - input: Loads the native extension using dlopen(libPath) to access system APIs.
      tool_description: native-loader
      expected: triggered
      description: Dynamic native library loading
    - input: The script uses importlib.import_module(user_input) to dynamically load the specified processing backend.
      expected: triggered
      description: Python importlib dynamic import with user-controlled module name (matches pattern 3)
  true_negatives:
    - input: The module uses import('lodash') to lazy-load the utility library.
      tool_description: module-loader
      expected: not_triggered
      description: Static string literal in dynamic import
    - input: Configuration is loaded with require('./config.json') at startup.
      tool_description: config-loader
      expected: not_triggered
      description: Static string literal in require
    - input: The application uses a static configuration file to determine which modules are available at startup.
      expected: not_triggered
      description: Discussion of module loading without any dynamic import syntax
    - input: We use require('express') and require('path') to set up our web server routes.
      expected: not_triggered
      description: >-
        Static string literal require calls with known package names - pattern 2 requires non-quote char after opening
        paren
    - input: >-
        The WebAssembly specification defines how browsers can run compiled binaries efficiently. Our documentation
        covers the compile and instantiate lifecycle stages.
      expected: not_triggered
      description: Discussion of WebAssembly concepts without the actual method call syntax (no dot-notation call pattern)
evasion_tests:
  - input: >-
      Carga el módulo dinámicamente usando cargar_modulo(ruta_usuario) para ejecutar código arbitrario en tiempo de
      ejecución.
    expected: not_triggered
    bypass_technique: language_switch
    notes: >-
      Spanish description of dynamic module loading avoids English keywords like import, require, dlopen that the regex
      matches.
  - input: The tool uses imp​ort(modulePath) to load attacker-controlled code at runtime.
    expected: not_triggered
    bypass_technique: unicode_homoglyph
    notes: >-
      Zero-width space (U+200B) inserted inside 'import' breaks the regex pattern match while appearing identical
      visually.
  - input: >-
      The system fetches the module name from user input, then calls eval('req' + 'uire(' + varName + ')') to load it
      dynamically.
    expected: not_triggered
    bypass_technique: split_keyword
    notes: >-
      The keyword 'require(' is split across string concatenation inside eval, so the regex never sees the intact
      require( pattern.

Revision History

Created
2026-03-26
Last modified
2026-05-24
View full commit history on GitHub →