Back to skill

Security audit

神经元

Security checks for vulnerabilities and agentic risk

Overview

The skill is purpose-aligned, but it automatically shares prompts and results with unauthenticated LAN peers, which creates a real privacy and answer-integrity risk.

Install only in a trusted, controlled LAN and avoid sending secrets, credentials, private business data, or regulated personal data through it. Treat peer answers as untrusted unless peers are authenticated and allowlisted; review memory exports carefully because they may contain full prompts and results.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/neuron_skill.py:127
Finding
Unauthenticated UDP discovery permits peer-list poisoning and unsafe task distribution<![CDATA[ ## Vulnerability Details **File Location**: `scripts/neuron_skill.py:127-149, 217-228` **Vulnerability Type**: Unauthenticated network discovery and peer trust **Risk Level**: High ### Vulnerable Code ```python while True: try: data, addr = sock.recvfrom(1024) msg = json.loads(data.decode()) if msg.get("type") == "announce": node_id = msg["node_id"] ip = msg["ip"] if node_id == self.node_id: continue with self.lock: self.nodes[node_id] = { "ip": ip, "last_seen": time.time() } except socket.timeout: continue except Exception as e: print(f"监听错误: {e}") ``` ```python if hasattr(context, 'rpc_call'): result = context.rpc_call( node_id=node["id"], skill="neuron", params={"question": question, "distribute": False, "task_id": task_id}, timeout=self.config["task_timeout"] ) ``` ### Technical Analysis The discovery listener accepts every correctly formatted UDP announcement without authenticating the sender, validating message integrity, checking an allowlist, preventing replay, or confirming that the advertised node identity belongs to the source. The packet's source address in `addr` is not validated against the advertised address. Accepted node IDs are subsequently passed to `context.rpc_call`. Consequently, a reachable attacker that also controls or can register the announced RPC node identity may cause user questions to be sent to an unauthorized node. Even where the RPC framework independently rejects the identity, forged announcements can still poison the local node list, trigger repeated failed RPC calls, and consume task-processing resources. The code also places no effective bound on discovered peers. Although `max_parallel_tasks` exists in configuration, it is not enforced when creating one thread per discovered node. ...[truncated 1436 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Authenticate discovery announcements using signatures or message authentication codes tied to provisioned node credentials. - Use mutual authentication and encrypted transport for all distributed task RPC calls. - Maintain an explicit allowlist of trusted peer identities and require administrative or user approval before adding new peers. - Validate announcement source addresses and do not trust an IP address supplied inside the datagram. - Add nonces, bounded timestamps, and replay protection. - Impose strict limits on the number of discovered nodes and enforce `max_parallel_tasks` through a bounded executor. - Require explicit user consent before distributing questions, particularly when prompts may contain sensitive information. - Reject or quarantine peers that repeatedly fail authentication or RPC connectivity checks. ]]>

T01 · Skill Instruction Hijacking

Error
Location
scripts/neuron_skill.py:260
Finding
Untrusted peer responses enable prompt injection during result aggregation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/neuron_skill.py:260-272` **Vulnerability Type**: Indirect prompt injection through peer-controlled model input **Risk Level**: High ### Vulnerable Code ```python def _aggregate_results(self, question: str, results: Dict[str, str], context: Any) -> str: summary_prompt = self._build_summary_prompt(question, results) if context and hasattr(context, 'call_model'): return context.call_model(prompt=summary_prompt) else: return f"[聚合] {len(results)} 个节点响应。摘要: {summary_prompt}" def _build_summary_prompt(self, question: str, results: Dict[str, str]) -> str: summary = f"原始问题: {question}\n\n" summary += f"来自 {len(results)} 个节点的响应:\n" for node_id, result in results.items(): summary += f"\n[节点 {node_id}]:\n{result}\n" summary += "\n请将这些响应综合成一个全面、准确的答案。" return summary ``` ### Technical Analysis Peer responses are untrusted network-derived data. The implementation concatenates each response directly into the same prompt that instructs the aggregation model. There is no structural separation between instructions and peer data, no provenance enforcement, and no requirement that the model treat peer text solely as quoted evidence. A malicious peer can return text containing instructions that claim to override the aggregation task, suppress other responses, fabricate conclusions, or request disclosure of prompt content. Authentication alone would not fully resolve this issue because a compromised or malicious trusted peer could still submit adversarial content. ### Attack Path 1. An attacker becomes a participating node, potentially by exploiting unauthenticated discovery or compromising an authorized peer. 2. The initiating node sends a distributed question to the attacker-controlled peer. 3. The peer returns a response containing adversarial instructions, for example instructions to ignore the original question and emit attacker-selected content. 4. `_ ...[truncated 872 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Treat every peer response as untrusted evidence rather than as an instruction. - Pass peer results through a structured interface that separates trusted system instructions from untrusted data. - In the trusted aggregation instruction, explicitly require the model to quote, compare, and summarize peer content without following instructions found inside it. - Apply content-size limits and reject malformed or unexpectedly instruction-like responses where practical. - Preserve peer provenance and confidence metadata so malicious or anomalous responses can be excluded. - Require authenticated peers, but do not rely on authentication as the sole prompt-injection defense. - Consider deterministic preprocessing or extraction of factual claims before exposing peer output to the aggregation model. - Do not automatically rebroadcast a final result unless it passes validation or user approval. ]]>

other

Warning
Location
scripts/neuron_skill.py:53
Finding
Hostname-bearing persistent identity and local IP are unnecessarily exposed<![CDATA[ ## Vulnerability Details **File Location**: `scripts/neuron_skill.py:53-64, 89-96, 107-119` **Vulnerability Type**: Local environment reconnaissance and network information disclosure **Risk Level**: Medium ### Vulnerable Code ```python new_node_id = f"node-{socket.gethostname()}-{str(uuid.uuid4())[:8]}" try: identity = { "node_id": new_node_id, "hostname": socket.gethostname(), "created_at": time.time(), "created_date": time.strftime("%Y-%m-%d %H:%M:%S") } with open(self.node_identity_path, 'w', encoding='utf-8') as f: json.dump(identity, f, indent=2, ensure_ascii=False) ``` ```python def _get_local_ip(self) -> str: try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) ip = s.getsockname()[0] s.close() return ip except Exception: return "127.0.0.1" ``` ```python msg = json.dumps({ "type": "announce", "node_id": self.node_id, "ip": self.local_ip, "timestamp": time.time() }).encode() sock.sendto(msg, ('<broadcast>', self.config["discovery_port"])) ``` ### Technical Analysis The Skill reads the local hostname and route-selected local IP address. It embeds the hostname in a persistent node identifier, stores both the node identifier and hostname in `scripts/node_identity.json`, and repeatedly broadcasts the hostname-bearing identifier together with the local IP. Determining a usable local address is reasonably related to the declared LAN discovery functionality. Collecting and disclosing the hostname is not necessary, however, because a random opaque UUID can uniquely identify a node. The persistent hostname-bearing identifier also enables long-term correlation of announcements. The UDP `connect()` call is used to determine the selected local interface; it does not itself establish a TCP connection. Nevertheless, hardcoding a public address for route selection is unnecessary and can produce mis ...[truncated 1161 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Generate the node identity from a full cryptographically random UUID without including the hostname. - Remove the `hostname` field from `node_identity.json`. - Broadcast only the minimum information necessary for discovery. - Consider short-lived or rotatable identifiers where persistent correlation is unnecessary. - Start network discovery only after explicit configuration or user consent rather than automatically during object construction. - Determine interface addresses through local interface enumeration or bind configuration instead of relying on a hardcoded public destination. - Document exactly which metadata is persisted and broadcast. - Restrict identity-file permissions to the current user and store mutable state in an appropriate application-data directory rather than the installed script directory. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/config.json:1
Finding
Out-of-range default UDP port prevents discovery and causes repeated errors<![CDATA[ ## Vulnerability Details **File Location**: `scripts/config.json:1-7` **Vulnerability Type**: Invalid network configuration and missing input validation **Risk Level**: Medium ### Vulnerable Code ```json { "discovery_port": 83668, "broadcast_interval": 5, "node_timeout": 15, "task_timeout": 30, "max_parallel_tasks": 10 } ``` The same invalid fallback is defined in `scripts/neuron_skill.py:71-77`: ```python default_config = { "discovery_port": 83668, "broadcast_interval": 5, "node_timeout": 15, "task_timeout": 30, "max_parallel_tasks": 10 } ``` ### Technical Analysis Valid UDP port numbers range from 0 through 65535. The shipped discovery port, `83668`, is outside this range. The listener attempts to bind to this invalid port, while the sender repeatedly attempts to broadcast to it. The listener thread has no outer recovery loop around socket creation and binding, so a bind failure terminates that thread. The sender catches send failures inside an infinite loop and emits another error after each configured interval. Configuration values are loaded without range or type validation. This invalid default currently prevents the unauthenticated discovery workflow from operating as documented. Changing the port to a valid value makes the network attack surfaces described in the other findings reachable. ### Attack Path 1. A user initializes `NeuronProcessor` with the shipped configuration. 2. `_start_discovery()` starts sender and listener threads. 3. Socket operations receive port `83668`. 4. The listener fails during `bind()` and terminates. 5. The sender repeatedly fails during `sendto()` and logs errors every broadcast interval. 6. Automatic node discovery remains unavailable, causing distributed execution to fall back to local processing or fail to find peers. No attacker interaction is required. If an untrusted party can modify the configuration, other unvalidated values could also cause excessive delays, rapid l ...[truncated 479 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace `83668` with a valid, documented UDP port in both `config.json` and the Python fallback. - Validate `discovery_port` as an integer in the range 1–65535 before creating threads. - Validate all timing and concurrency settings with safe minimum and maximum values. - Fail initialization with one clear diagnostic instead of allowing partially initialized background threads. - Wrap listener setup in controlled error handling and close sockets reliably. - Add automated tests that instantiate the default configuration and verify successful socket parameter validation. - Update `SKILL.md` and firewall examples to reference the corrected port. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (9)

Missing User Warnings

High
Confidence
95% confidence
Finding
The documentation advertises broadcasting final results for collective learning, which implies generated outputs may be shared network-wide without clear disclosure of the privacy and confidentiality consequences. Final answers can contain user secrets, proprietary data, or derived sensitive inferences, so rebroadcasting them materially expands the exposure surface beyond the originating node.

Missing User Warnings

High
Confidence
97% confidence
Finding
The skill states that user questions are automatically distributed to all available LAN nodes, but it does not provide a prominent, explicit warning at the point of use that user-supplied content will be transmitted to other machines. Because prompts often contain sensitive business, personal, or credential-bearing data, automatic forwarding across the network can cause unintended data disclosure to every discovered node.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill documents code capabilities that include environment access and file read/write behavior, but it does not declare any explicit tool scope or permissions boundary in the manifest. In a distributed skill that persists node identity and reads configuration, this omission increases the risk of over-privileged execution and makes it harder for operators to understand or restrict what the skill can access.

Tainted flow: 'msg' from socket.socket.recvfrom (line 128, network input) → socket.socket.sendto (network output)

Medium
Category
Data Flow
Content
"ip": self.local_ip,
                        "timestamp": time.time()
                    }).encode()
                    sock.sendto(msg, ('<broadcast>', self.config["discovery_port"]))
                except Exception as e:
                    print(f"广播错误: {e}")
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill distributes user questions to peer nodes and later broadcasts final aggregated results without any explicit consent, disclosure, trust model, or node authentication. In the context of a distributed AI skill, this is particularly risky because prompts and outputs may contain sensitive user data and are shared across the LAN with potentially untrusted or spoofed nodes.

Context-Inappropriate Capability

Medium
Confidence
86% confidence
Finding
The skill exposes generic import/export of its full task memory to arbitrary local file paths, which goes beyond the core LAN distribution purpose and can persist or ingest sensitive prompts, results, and metadata. In an agent environment, this increases the risk of unintended data exfiltration, unsafe file writes, and loading attacker-controlled JSON into runtime state.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
Exporting the in-memory task store writes potentially sensitive questions, intermediate node outputs, and final results to an arbitrary file path with no warning, redaction, or access control. This creates a straightforward confidentiality risk if the file is written to a shared, predictable, or attacker-influenced location.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The file presents the skill description and operating instructions exclusively in Chinese. If organizational policy requires offering language or locale choice rather than forcing a single language, this is a natural-language policy concern because no opt-in or documented locale constraint is provided.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
User-facing docstrings, CLI descriptions, help text, and status messages are consistently Chinese-only. Under the language/locale policy, forcing a single language without opt-in or documented justification can be a natural-language policy issue.

Static analysis

No suspicious patterns detected.