Back to skill

Security audit

nmap MCP server for AI-assisted network security auditing

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed nmap scanning tool, but it exposes high-impact network scanning with weak parameter and scope controls.

Install only in a dedicated, tightly scoped environment. Narrow allowed_cidrs before use, avoid hostname targets where possible, avoid nmap_custom_scan unless necessary, treat saved scan output as sensitive, and pin dependencies before production deployment.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
server.py:189
Finding
Unvalidated Scan Parameters Permit Nmap Option and NSE Script Injection<![CDATA[ ## Vulnerability Details **File Location**: `server.py:189-195, 371, 392, 417-418, 471-472, 502-503, 535-536` **Vulnerability Type**: Nmap argument injection and unsafe NSE script selection **Risk Level**: High ### Vulnerable Code ```python def _nmap_structured(target: str, extra_args: list[str], timeout: int = 300) -> dict: """ Run nmap with -oX - to get XML output, parse it with python-nmap, return a structured dict. """ nm = nmap.PortScanner() args_str = " ".join(extra_args) log.info("python-nmap scan: target=%s args=%s", target, args_str) try: nm.scan(hosts=target, arguments=args_str, timeout=timeout) ``` The following tool parameters are inserted into that argument string without validation: ```python result = _nmap_structured( target, ["-sT", "-p", ports, "-T4", "--open"], timeout=TIMEOUTS["standard"] ) ``` ```python result = _nmap_structured( target, ["--privileged", "-sU", "-p", ports, "-T4"], timeout=TIMEOUTS["deep"] ) ``` ```python port_args = ["--top-ports=1000"] if ports == "common" else ["-p", ports] extra = ["-sT", f"--script={scripts}", "-T4"] + port_args ``` Equivalent unvalidated `ports` handling also appears in service detection, vulnerability scanning, and full reconnaissance. ### Technical Analysis The public MCP tools accept `ports` and `scripts` as arbitrary strings. Although these values are initially placed in Python lists, `_nmap_structured` joins the entire list into a single string before passing it to `python-nmap`. Whitespace and option prefixes inside user-controlled values can therefore be interpreted as additional Nmap command-line options. The stricter checks implemented for `nmap_custom_scan`, including `_DANGEROUS_FLAGS`, are not applied to these tools. Consequently, an attacker can attempt to append output options, data-directory options, or NSE script selections through a crafted port value. The `scripts` parameter is particularly sensitive ...[truncated 2065 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate port specifications with a strict parser rather than a permissive string check. Accept only individual integers from 1 through 65535, comma-separated lists, and correctly ordered numeric ranges. 2. Reject all whitespace, leading hyphens, empty components, path separators, and non-port tokens in `ports`. 3. Validate NSE script names against a conservative allowlist. If multiple scripts are permitted, parse a comma-separated list and validate each name independently. 4. Prohibit absolute paths, relative paths, `..`, path separators, whitespace, and option prefixes in `scripts`. 5. Apply centralized validation to every tool accepting `ports` or `scripts`, including TCP, SYN, UDP, service detection, script scanning, vulnerability scanning, and full reconnaissance. 6. Avoid joining arguments containing user input into a command string. Prefer an API that preserves a real argument vector through process execution. 7. If `python-nmap` requires an argument string, construct it only from validated, normalized values and use `shlex.join` where appropriate. 8. Run Nmap under a dedicated, minimally privileged account and restrict writable directories. 9. Add regression tests for whitespace-based option injection, script paths, output flags, data-directory flags, malformed port ranges, and leading-hyphen values. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
server.py:100
Finding
Hostname Scope Enforcement Is Vulnerable to DNS Rebinding<![CDATA[ ## Vulnerability Details **File Location**: `server.py:100-119, 189-195` **Vulnerability Type**: DNS rebinding and time-of-check/time-of-use scope bypass **Risk Level**: High ### Vulnerable Code ```python # Hostname — resolve to IPs and validate every resolved address. # Fail closed: if resolution fails or any IP is out of scope, reject. try: resolved = socket.getaddrinfo(target, None) addrs = {r[4][0] for r in resolved} if not addrs: log.warning("Scope check: hostname '%s' resolved to no addresses — rejecting", target) return False for raw in addrs: try: addr = ipaddress.ip_address(raw) if not any(addr in allowed for allowed in ALLOWED_CIDRS): log.warning("Scope check: hostname '%s' resolves to out-of-scope IP %s", target, raw) return False except ValueError: return False return True except socket.gaierror: log.warning("Scope check: hostname '%s' could not be resolved — rejecting", target) return False ``` After validation, the original hostname—not the validated address—is passed to Nmap: ```python nm = nmap.PortScanner() args_str = " ".join(extra_args) log.info("python-nmap scan: target=%s args=%s", target, args_str) try: nm.scan(hosts=target, arguments=args_str, timeout=timeout) ``` ### Technical Analysis Scope enforcement resolves a hostname and validates the returned addresses against `ALLOWED_CIDRS`. Nmap subsequently receives the original hostname and performs its own resolution. The validated DNS result is not pinned or reused. This creates a time-of-check/time-of-use discrepancy. An attacker controlling the hostname's DNS responses can return an allowed private or loopback address during scope validation and then return an out-of-scope address when Nmap resolves the same hostname. A short DNS time-to-live, alternating answers, or resolver behavior that avoids caching can make this bypass practical. Vali ...[truncated 1385 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve the hostname exactly once before execution. 2. Validate every resulting IPv4 and IPv6 address against the allowlist. 3. Pass only the validated literal IP addresses to Nmap, never the original hostname. 4. Reject ambiguous hostname scans if execution cannot be bound reliably to the validated addresses. 5. Record both the submitted hostname and the exact validated IP addresses in the audit log and persisted scan record. 6. Consider disabling hostname targets entirely in high-assurance deployments. 7. Add integration tests that simulate changing DNS answers between validation and scan execution. 8. Do not rely solely on DNS caching or TTL behavior, because neither provides a dependable security boundary. 9. Where feasible, enforce the destination allowlist at the network layer as defense in depth. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Dependencies Are Installed Without Exact Version or Integrity Pinning<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-4`, `SKILL.md:27`, `README.md:67` **Vulnerability Type**: Mutable dependency resolution and supply-chain exposure **Risk Level**: Medium ### Vulnerable Code ```text fastmcp>=2.0.0 python-nmap>=0.7.1 pyyaml>=6.0 pytest>=7.0 # for running tests ``` The setup documentation also recommends unconstrained direct installation: ```bash pip install fastmcp python-nmap pyyaml ``` and: ```bash pip install -r requirements.txt ``` ### Technical Analysis All dependencies use open-ended lower bounds, and the direct installation command in `SKILL.md` does not impose even those minimum constraints. A future installation can therefore resolve to package versions that were not reviewed with this project. No integrity hashes are provided. The installation process consequently trusts the configured package index, package metadata, and whichever release is selected at installation time. If an upstream project, maintainer account, distribution channel, or package index is compromised, malicious build or package code may execute during installation or import. The audit did not identify a known malicious dependency in the listed package names. The finding concerns the absence of reproducible and integrity-verified dependency controls. ### Attack Path 1. A user follows the documented setup process. 2. `pip` queries the configured package index. 3. Because exact versions and hashes are absent, `pip` selects a mutable future release satisfying the lower bound, or any current release for the unconstrained command. 4. A compromised or malicious package release is downloaded. 5. Package build, installation, or import-time code executes with the privileges of the installing user or MCP server account. 6. The compromised dependency can access data and resources available to that account. This path depends on compromise or malicious publication in the dependency supply chain; no such compromise was establis ...[truncated 617 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every runtime dependency to a reviewed exact version using `==`. 2. Generate and commit a lock file containing cryptographic hashes. 3. Install with hash verification, such as `pip install --require-hashes -r requirements.lock`. 4. Separate runtime and development dependencies so `pytest` is not installed in production. 5. Update `SKILL.md` to use the locked requirements file rather than unconstrained package names. 6. Use a dedicated virtual environment or container and avoid administrative installation. 7. Configure an approved package index or internal mirror. 8. Review dependency updates before regenerating the lock file. 9. Add automated dependency vulnerability and provenance scanning to the release process. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (26)

Privileged Container / Container Escape

High
Category
Privilege Escalation
Content
"""
    _require_scope(target, "nmap_arp_discovery")
    # ARP discovery + privileged for raw socket access
    result = _nmap_structured(target, ["--privileged", "-sn", "-PR", "-T4"], timeout=TIMEOUTS["quick"])
    scan_id = _save_scan("nmap_arp_discovery", target, result)

    hosts = []
Confidence
80% confidence
Finding
Potential security issue detected. Manual review is recommended.

Privileged Container / Container Escape

High
Category
Privilege Escalation
Content
"""
    _require_scope(target, "nmap_arp_discovery")
    # ARP discovery + privileged for raw socket access
    result = _nmap_structured(target, ["--privileged", "-sn", "-PR", "-T4"], timeout=TIMEOUTS["quick"])
    scan_id = _save_scan("nmap_arp_discovery", target, result)

    hosts = []
Confidence
80% confidence
Finding
Potential security issue detected. Manual review is recommended.

Privileged Container / Container Escape

High
Category
Privilege Escalation
Content
"""
    _require_scope(target, "nmap_arp_discovery")
    # ARP discovery + privileged for raw socket access
    result = _nmap_structured(target, ["--privileged", "-sn", "-PR", "-T4"], timeout=TIMEOUTS["quick"])
    scan_id = _save_scan("nmap_arp_discovery", target, result)

    hosts = []
Confidence
80% confidence
Finding
Potential security issue detected. Manual review is recommended.

Privileged Container / Container Escape

High
Category
Privilege Escalation
Content
"""
    _require_scope(target, "nmap_arp_discovery")
    # ARP discovery + privileged for raw socket access
    result = _nmap_structured(target, ["--privileged", "-sn", "-PR", "-T4"], timeout=TIMEOUTS["quick"])
    scan_id = _save_scan("nmap_arp_discovery", target, result)

    hosts = []
Confidence
80% confidence
Finding
Potential security issue detected. Manual review is recommended.

Privileged Container / Container Escape

High
Category
Privilege Escalation
Content
"""
    _require_scope(target, "nmap_arp_discovery")
    # ARP discovery + privileged for raw socket access
    result = _nmap_structured(target, ["--privileged", "-sn", "-PR", "-T4"], timeout=TIMEOUTS["quick"])
    scan_id = _save_scan("nmap_arp_discovery", target, result)

    hosts = []
Confidence
80% confidence
Finding
Potential security issue detected. Manual review is recommended.

Credential Access

High
Category
Privilege Escalation
Content
def test_get_scan_path_traversal_blocked(self):
        """nmap_get_scan must strip path traversal chars from scan_id."""
        result = json.loads(self.server.nmap_get_scan("../../etc/passwd"))
        self.assertIn("error", result)

    def test_list_scans_returns_records(self):
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def test_semicolon_rejected(self):
        with self.assertRaises(ValueError):
            self.server.nmap_custom_scan("127.0.0.1", "-sT -p 22; rm -rf /")

    def test_pipe_rejected(self):
        with self.assertRaises(ValueError):
Confidence
100% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def test_semicolon_rejected(self):
        with self.assertRaises(ValueError):
            self.server.nmap_custom_scan("127.0.0.1", "-sT -p 22; rm -rf /")

    def test_pipe_rejected(self):
        with self.assertRaises(ValueError):
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def test_semicolon_rejected(self):
        with self.assertRaises(ValueError):
            self.server.nmap_custom_scan("127.0.0.1", "-sT -p 22; rm -rf /")

    def test_pipe_rejected(self):
        with self.assertRaises(ValueError):
Confidence
100% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Chaining Abuse

High
Category
Tool Misuse
Content
def test_semicolon_rejected(self):
        with self.assertRaises(ValueError):
            self.server.nmap_custom_scan("127.0.0.1", "-sT -p 22; rm -rf /")

    def test_pipe_rejected(self):
        with self.assertRaises(ValueError):
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
Required for SYN scans, OS detection, and ARP discovery. Only needs to be done once (redo after nmap upgrades):

```bash
sudo setcap cap_net_raw+ep $(which nmap)

# Verify
getcap $(which nmap)
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill exposes network-scanning functionality and its setup/usage clearly implies shell execution, file access, and environment-variable usage, but it does not declare any explicit tool scope such as permissions or allowed-tools. In an agent environment, that omission weakens operator visibility and policy enforcement, making it easier for the skill to invoke powerful capabilities without clear guardrails.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The instructions tell users to grant cap_net_raw to the nmap binary, which increases the binary's privilege to open raw sockets, but they do not include a clear warning about the security implications. If nmap or a related execution path is abused, that added capability can enable more sensitive network operations than an unprivileged binary could perform.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# 2. Grant nmap raw socket capability (required for SYN + OS detection)
#    Only needs to be done once. Re-run after nmap upgrades.
sudo setcap cap_net_raw+ep $(which nmap)

# 3. Verify it worked
getcap $(which nmap)
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
cmd = [NMAP_BIN] + args
    log.info("Running: %s", " ".join(cmd))
    try:
        r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
        return {
            "success": r.returncode == 0,
            "stdout": r.stdout,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
_audit("nmap_custom_scan", target, {"flags": flags}, f"REJECTED: forbidden char", False)
            raise ValueError(f"Forbidden character in flags. Use individual tool functions for complex scans.")

    # Block flags that write to paths or execute arbitrary code
    # (nmap output flags, --script with path, --datadir, etc.)
    if _DANGEROUS_FLAGS.search(flags):
        _audit("nmap_custom_scan", target, {"flags": flags}, "REJECTED: dangerous flag", False)
Confidence
80% confidence
Finding
The custom scan tool intentionally exposes broad nmap flag control to callers, and the current denylist only blocks some metacharacters and a subset of dangerous options. That still leaves a powerful recon capability that can run arbitrary nmap scan modes and potentially abusive NSE usage within allowed scope, which is risky in an agent context because tool access is not further permission-gated.

Ssd 3

Medium
Confidence
97% confidence
Finding
The skill persistently stores prior scan results, including targets, commands, findings, and raw outputs, and exposes them through list/retrieval tools without any access-control checks or ownership scoping. In a multi-user or shared agent environment, this can leak sensitive internal network data, vulnerability findings, and reconnaissance history to later users or sessions.

Rp1

Low
Category
MCP Rug Pull
Confidence
60% confidence
Finding
pip install without ==version installs the latest release, which could include malicious changes.

Rp1

Low
Category
MCP Rug Pull
Confidence
82% confidence
Finding
The setup instruction installs fastmcp without a pinned version, which makes builds non-reproducible and increases supply-chain risk if a future release introduces malicious code or breaking security changes. For infrastructure-facing software that executes scans and handles logs/files, dependency drift is a meaningful security concern.

Unpinned Dependencies

Low
Category
Supply Chain
Content
fastmcp>=2.0.0
python-nmap>=0.7.1
pyyaml>=6.0
pytest>=7.0  # for running tests
Confidence
98% confidence
Finding
The dependency specification uses a lower-bound only constraint (fastmcp>=2.0.0), which permits installation of any future release, including vulnerable or breaking versions. In a security-sensitive skill that exposes network scanning capability and likely parses untrusted inputs/results, lack of pinning weakens supply-chain control and makes builds non-reproducible.

Unverifiable Dependency: fastmcp has 14 known advisory(ies) (CVE-2025-69196 (FastMCP OAuth Proxy token reuse across MCP servers); GHSA-c2jp-c369-7pvx (FastMCP Auth Integration Allows for Confused Deputy Account Takeover); CVE-2025-64340 (FastMCP has a Command Injection vulnerability - Gemini CLI) +11 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
99% confidence
Finding
The manifest does not pin fastmcp, and the package is reported to have numerous known advisories, including high-risk authentication and command-injection issues. Because this skill is an MCP server for network scanning, a vulnerable fastmcp release could expose remote attack paths, privilege misuse, or command execution in a particularly sensitive operational context.

Unpinned Dependencies

Low
Category
Supply Chain
Content
fastmcp>=2.0.0
python-nmap>=0.7.1
pyyaml>=6.0
pytest>=7.0  # for running tests
Confidence
96% confidence
Finding
python-nmap is specified with an unbounded minimum version, so deployments may resolve to different package versions over time, including versions with future vulnerabilities or incompatible behavior. Because this skill wraps nmap and likely handles external process interaction, dependency drift increases supply-chain and operational risk.

Unpinned Dependencies

Low
Category
Supply Chain
Content
fastmcp>=2.0.0
python-nmap>=0.7.1
pyyaml>=6.0
pytest>=7.0  # for running tests
Confidence
99% confidence
Finding
PyYAML is unpinned despite being a historically sensitive package with multiple deserialization/input-handling advisories. Allowing any version >=6.0 makes it impossible to ensure that all installations use a reviewed safe release, which is especially risky if YAML is ever parsed from untrusted sources in the MCP server or its configuration.

Unverifiable Dependency: pyyaml has 8 known advisory(ies) (CVE-2019-20477 (Deserialization of Untrusted Data in PyYAML); CVE-2020-1747 (Improper Input Validation in PyYAML); CVE-2020-14343 (Improper Input Validation in PyYAML) +5 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
97% confidence
Finding
PyYAML has a history of unsafe deserialization and input-validation issues, and the unpinned requirement prevents verification that deployed environments use a fixed version. If the skill consumes YAML configuration or external YAML content, a vulnerable PyYAML version could enable code execution or parser abuse.

Static analysis

No suspicious patterns detected.