Skip to content

[Security] Plugin parser.py Dynamically Executed Without Signature Enforcement — Remote Code Execution #202

@advikdivekar

Description

@advikdivekar

Description

A CRITICAL security vulnerability exists in backend/secuscan/executor.py at lines 695–711, combined with disabled-by-default signature enforcement in backend/secuscan/config.py lines 55–56. The executor dynamically imports and executes any parser.py found in a plugin directory using Python's importlib. Plugin signature enforcement is off by default:

# config.py lines 55–56 — enforcement disabled by default
plugin_signature_key: Optional[str] = None
enforce_plugin_signatures: bool = False

Additionally, debug: bool = True is the default, causing get_plugin_manager_for_request() in routes.py:119 to call await init_plugins(settings.plugins_dir) on every API request — a tampered parser is picked up immediately without a server restart.

# executor.py lines 695–711 — parser.py executed with no pre-execution integrity check
if parser_path.exists():
    spec = importlib.util.spec_from_file_location(f"parser_{plugin.id}", parser_path)
    ...
    loader.exec_module(module)        # arbitrary Python executed here
    if hasattr(module, "parse"):
        parsed = module.parse(parser_input)

Impact

Any attacker who can write a file to the plugins/<id>/parser.py path achieves immediate arbitrary Python code execution in the context of the backend server process — no authentication, no integrity check, no restart required. The process can read the full SQLite database (all vault credentials), access the local network, install persistence, or exfiltrate data. With no API authentication (see Issue #199), the attack surface for achieving filesystem writes is significantly wider.

Steps to Reproduce

  1. Overwrite plugins/nmap/parser.py with:
    import subprocess
    def parse(output):
        subprocess.Popen(["bash", "-c", "id > /tmp/secuscan_pwned"])
        return {"findings": []}
  2. Send any POST /api/v1/task/start request with plugin_id = "nmap".
  3. Observed result: /tmp/secuscan_pwned is created on the server with the output of id, confirming OS-level code execution under the backend process user.

Expected Behaviour

Plugin parsers must be verified with an HMAC signature against a server-held secret key before execution. The application should refuse to load unsigned plugins when enforcement is on, and enforcement should be on by default. Debug-mode hot-reload of plugin code must be disabled.

Proposed Fix

1. Enable enforcement by default:

# backend/secuscan/config.py
enforce_plugin_signatures: bool = True   # was False

2. Disable hot-reload of plugins in debug mode:

# backend/secuscan/routes.py
async def get_plugin_manager_for_request():
    return get_plugin_manager()   # always use the startup-stable instance

3. Re-verify parser integrity immediately before execution:

# backend/secuscan/executor.py — replace dynamic import block
def _load_and_run_parser(self, plugin_id, parser_path, parser_input):
    plugin_manager = get_plugin_manager()
    plugin = plugin_manager.get_plugin(plugin_id)
    plugin_dir = plugin_manager.plugins_dir / plugin_id
    # Re-check integrity right before exec — not just at load time
    if not plugin_manager._verify_plugin_integrity(plugin, plugin_dir):
        raise RuntimeError(f"Parser integrity check failed for {plugin_id}. Refusing execution.")
    spec = importlib.util.spec_from_file_location(f"parser_{plugin_id}", parser_path)
    if spec and spec.loader:
        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)
        if hasattr(module, "parse"):
            return module.parse(parser_input)
    raise ValueError(f"parser.py for {plugin_id} missing 'parse' function")

4. Document the signing key as required in .env.example:

# REQUIRED for plugin integrity enforcement
SECUSCAN_ENFORCE_PLUGIN_SIGNATURES=true
SECUSCAN_PLUGIN_SIGNATURE_KEY=replace-with-a-signing-key-never-commit-this

Files Affected

  • backend/secuscan/config.py
  • backend/secuscan/executor.py
  • backend/secuscan/routes.py
  • backend/secuscan/plugins.py
  • .env.example

Verification

# Tamper with parser.py — backend must detect mismatch and refuse execution
echo "def parse(x): import os; os.system('id')" >> plugins/nmap/parser.py
# Trigger a scan — must log integrity failure and raise RuntimeError, NOT execute the tampered code
# /tmp/secuscan_pwned must NOT be created after the fix is applied

# With SECUSCAN_ENFORCE_PLUGIN_SIGNATURES=true and no key — backend must refuse to start
SECUSCAN_ENFORCE_PLUGIN_SIGNATURES=true python -m uvicorn backend.secuscan.main:app ...
# Expected: error logged, unsigned plugins rejected

Labels

type:security level:advanced gssoc:approved


Please assign this issue to me under GSSoC 2026. I will open a PR with a complete fix covering all affected files, proper test coverage, and verification steps.

Metadata

Metadata

Assignees

Labels

area:backendBackend API, database, or service workarea:pluginsScanner plugin metadata, schemas, or plugin runtime workarea:securitySecurity-sensitive implementation or testslevel:critical80 pts difficulty label for critical or high-impact PRspriority:highHigh-priority issuetype:securitySecurity work category bonus label

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions