Back to skill

Security audit

Zynd Agent Network

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it advertises, but it exposes network-facing agent messages, identity data, and payment authority with weak scoping and limited warnings.

Install only if you are comfortable running a network-facing agent integration. Use a low-privilege Zynd key and wallet with minimal funds, avoid sending private or regulated data to third-party agents, prefer HTTPS endpoints, bind webhook servers to localhost or protect them behind authentication, disable or protect /messages, and review the SDK version before setup.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/zynd_webhook_server.py:200
Finding
Unauthenticated Public Message Disclosure and Sender Spoofing<![CDATA[ ## Vulnerability Details **File Location**: `scripts/zynd_webhook_server.py`, lines 25–27, 83–94, 128–139, 200–211, and 247–253 **Vulnerability Type**: Missing authentication and authorization on public webhook endpoints **Risk Level**: High ### Vulnerable Code ```python parser.add_argument( "--host", default="0.0.0.0", help="Host to bind to (default: 0.0.0.0)" ) ``` ```python @app.route("/webhook", methods=["POST"]) def handle_webhook(): """Handle async incoming messages.""" try: if not request.is_json: return jsonify( {"error": "Content-Type must be application/json"} ), 400 payload = request.get_json() message = AgentMessage.from_dict(payload) ``` ```python @app.route("/messages", methods=["GET"]) def list_messages(): """List received messages.""" with lock: return jsonify( { "count": len(received_messages), "messages": received_messages[-20:], # Last 20 } ), 200 ``` ```python app.run( host=args.host, port=args.port, debug=False, use_reloader=False, threaded=True, ) ``` ### Technical Analysis The webhook server binds to all interfaces by default. Its message-receiving endpoints accept any JSON payload that can be parsed by `AgentMessage.from_dict`, but the server does not authenticate the caller, validate a message signature, verify the supplied DID, enforce sender authorization, or prevent replay. The unauthenticated `GET /messages` endpoint returns the last 20 stored messages. Each stored record includes the complete serialized message, receipt time, and source IP address. Consequently, anyone who can connect to the service can retrieve potentially sensitive delegated task content and network metadata. The implementation also lacks request-size, rate, and message-retention limits. Because `received_messages` grows for the lifetime of the process, repeated valid su ...[truncated 1282 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bind to `127.0.0.1` by default and require an explicitly configured trusted reverse proxy for external exposure. 2. Authenticate every webhook request using verifiable message signatures or a mutually authenticated transport. 3. Validate the supplied DID against the trusted Zynd identity service and verify that the message was signed by the corresponding identity. 4. Add timestamps, nonces, and replay detection. 5. Remove `/messages` in production. If operationally required, protect it with strong authentication and authorization and redact message content and source IPs. 6. Apply strict body-size limits, schema validation, per-source rate limits, and bounded message retention. 7. Use TLS at the server or reverse-proxy layer. 8. Return generic errors rather than exposing internal exception strings. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/zynd_webhook_server.py:95
Finding
Untrusted Webhook Content Is Forwarded Into the Agent Processing Channel<![CDATA[ ## Vulnerability Details **File Location**: `scripts/zynd_webhook_server.py`, lines 95–103 and 140–148 **Vulnerability Type**: Indirect prompt-injection channel through untrusted webhook content **Risk Level**: High ### Vulnerable Code ```python # Print the message for the OpenClaw agent to read print(f"\n{'=' * 60}") print(f" INCOMING MESSAGE") print(f"{'=' * 60}") print(f" From : {message.sender_id}") print(f" Type : {message.message_type}") print(f" ID : {message.message_id}") print(f" Content :") print(f" {message.content}") print(f"{'=' * 60}\n") ``` The synchronous endpoint performs the same operation: ```python # Print the message print(f"\n{'=' * 60}") print(f" INCOMING SYNC REQUEST") print(f"{'=' * 60}") print(f" From : {message.sender_id}") print(f" Type : {message.message_type}") print(f" ID : {message.message_id}") print(f" Content :") print(f" {message.content}") print(f"{'=' * 60}\n") ``` ### Technical Analysis The script's stated design is to print incoming messages to standard output so that the OpenClaw agent can read and process them. However, the printed content originates from an unauthenticated remote request and is not separated into a constrained data channel or marked as untrusted input. An attacker can therefore place instruction-like text in `message.content`. If the consuming agent interprets the output as actionable instructions rather than untrusted data, the remote message can attempt to redirect the agent's workflow, induce tool calls, request disclosure of contextual information, or trigger consequential actions. The static code does not itself execute the message as code and does not contain a hard-coded instruction-hijacking payload. The vulnerability is the unsafe trust boundary between an external input channel and an AI agent's processing context. ### Attack Path 1. The webhook server is reachable by an attacker. 2. The attacker submits a structurally valid message ...[truncated 999 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Authenticate and cryptographically verify senders before forwarding their content. 2. Deliver message content through a structured API or queue rather than an undifferentiated stdout stream. 3. Explicitly label all remote content as untrusted data and delimit it from system and operator instructions. 4. Ensure the consuming agent cannot treat webhook text as authorization for tool use. 5. Require explicit user or operator approval before filesystem access, credential use, payments, command execution, or external side effects. 6. Apply content-length limits and normalize control characters before logging or forwarding messages. 7. Maintain provenance metadata separately and do not trust sender fields supplied in the request body. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/zynd_call.py:100
Finding
Sensitive Task and Identity Data Can Be Sent to Arbitrary or Plaintext Destinations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/zynd_call.py`, lines 29–33, 71–77, 100–112, and 120–143 **Vulnerability Type**: Unrestricted outbound destination, plaintext sensitive-data transmission, and unsafe payment trust boundary **Risk Level**: High ### Vulnerable Code ```python parser.add_argument( "--webhook", required=True, help="Target agent's webhook URL (from search results)", ) ``` ```python # Ensure webhook URL points to sync endpoint webhook_url = args.webhook if not webhook_url.endswith("/sync"): if webhook_url.endswith("/webhook"): webhook_url = webhook_url + "/sync" elif not "/webhook" in webhook_url: webhook_url = webhook_url.rstrip("/") + "/webhook/sync" ``` ```python agent_id = config["id"] identity_credential = config["did"] secret_seed = config["seed"] # Create message message = AgentMessage( content=args.message, sender_id=agent_id, message_type="query", sender_did=identity_credential, ) ``` ```python if args.pay: # Use x402 payment processor processor = X402PaymentProcessor(secret_seed) response = processor.post( webhook_url, json=message.to_dict(), headers={"Content-Type": "application/json"}, timeout=args.timeout, ) # Check for payment info payment_response = response.headers.get("x-payment-response") if payment_response: print(f"Payment processed: {payment_response}") processor.close() else: # Direct HTTP POST (no payment) response = requests.post( webhook_url, json=message.to_dict(), headers={"Content-Type": "application/json"}, timeout=args.timeout, ) ``` ### Technical Analysis The destination URL is accepted directly from a command-line argument. The code modifies only its path and performs no validation of the URL scheme, hostname, resolved IP address, port, registry membership, or TLS status. The outbound message includes user-provided ta ...[truncated 2412 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `https://` and reject plaintext HTTP except for an explicit, development-only localhost mode. 2. Resolve destination names and reject loopback, link-local, private, multicast, and reserved address ranges unless specifically authorized. 3. Revalidate the destination after redirects or disable redirects entirely. 4. Verify that the webhook URL exactly matches a trusted, signed registry record for the selected agent. 5. Restrict allowed ports and normalize URLs with a standards-compliant parser before validation. 6. Inform users what task and identity information will leave the local environment, and require confirmation for sensitive content. 7. Before enabling payment, display and enforce the network, token, recipient, and maximum amount. 8. Require explicit confirmation for each payment or establish a narrowly scoped, user-approved spending budget. 9. Keep payment keys separate from general identity configuration and use a wallet with minimal funds. 10. Pin and audit the SDK code responsible for x402 challenge validation and transaction signing. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/zynd_register.py:111
Finding
Zynd API Key Can Be Redirected to a Caller-Controlled Registry<![CDATA[ ## Vulnerability Details **File Location**: `scripts/zynd_register.py`, lines 55–59, 76–81, and 111–129 **Vulnerability Type**: Credential exposure through an unrestricted service endpoint override **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument( "--registry-url", default="https://registry.zynd.ai", help="Zynd registry URL (default: https://registry.zynd.ai)", ) ``` ```python # Get API key api_key = os.environ.get("ZYND_API_KEY") if not api_key: print("ERROR: ZYND_API_KEY environment variable is not set.") print("Get your API key from https://dashboard.zynd.ai") sys.exit(1) ``` ```python agent_config = AgentConfig( name=args.name, description=args.description, capabilities=capabilities, webhook_host="0.0.0.0", webhook_port=args.port, webhook_url=webhook_url, registry_url=args.registry_url, api_key=api_key, price=args.price, config_dir=config_dir, ) print(f"\nRegistering agent '{args.name}' on the Zynd Network...") print(f"Capabilities: {json.dumps(capabilities, indent=2)}") print(f"Registry: {args.registry_url}") print(f"Webhook URL: {webhook_url}") print(f"Config dir: {config_dir}") if args.price: print(f"Price per request: {args.price}") print() agent = ZyndAIAgent(agent_config=agent_config) ``` ### Technical Analysis The API reference states that agent creation and webhook updates use `ZYND_API_KEY` in an HTTP authentication header. The registration script places this secret and the caller-selected `registry_url` into the same `AgentConfig` without validating that the endpoint is the legitimate Zynd registry. Consequently, an invocation using an attacker-controlled registry URL can cause SDK registration or refresh operations to authenticate to the substituted service. A plaintext custom URL would additionally expose the credential to network observers. The same unrestricted `--registry-url` pattern exists in the other scripts, but registration pro ...[truncated 1231 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the registry override from normal production usage. 2. Allowlist the exact canonical origin `https://registry.zynd.ai`. 3. If custom registries are required, permit them only through an explicit development mode and require operator confirmation. 4. Require HTTPS and validate certificates and hostnames. 5. Reject URLs containing user information, fragments, unexpected ports, or non-HTTP schemes. 6. Use separately issued, narrowly scoped credentials for test or private registries. 7. Ensure redirect handling cannot forward authentication headers to a different origin. 8. Document API-key scope, rotation, and revocation procedures. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/setup.sh:27
Finding
Unbounded Third-Party SDK Installation Creates a Supply-Chain Execution Risk<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh`, lines 27–33 **Vulnerability Type**: Unpinned dependency installation and execution **Risk Level**: Medium ### Vulnerable Code ```bash echo "" echo "Installing zyndai-agent SDK..." python3 -m pip install --quiet --upgrade "zyndai-agent>=0.2.2" echo "" echo "Verifying installation..." python3 -c "import zyndai_agent; print(f'zyndai-agent installed successfully')" 2>/dev/null ``` ### Technical Analysis The setup script accepts every current or future release satisfying `zyndai-agent>=0.2.2` and requests an upgrade. It does not use an exact version, upper bound, lockfile, package hash, signed artifact, or isolated environment. Python packages may execute code during installation depending on their build configuration. The verification step also imports the installed package immediately, which executes its top-level initialization code. A compromised maintainer account, malicious future release, or compromised transitive dependency could therefore execute code with the permissions of the user running setup. Using the default pip package index does not by itself prove that the current package is malicious. The confirmed weakness is the absence of controls that bind installation to the version reviewed with the Skill. ### Attack Path 1. A malicious or compromised release of `zyndai-agent`, or one of its unconstrained transitive dependencies, is published. 2. The user or OpenClaw agent runs `scripts/setup.sh`. 3. Pip resolves the malicious version because it satisfies `>=0.2.2` and `--upgrade` favors a newer release. 4. Pip installs the package and may execute build-related package code. 5. The verification command imports the package and executes module initialization. 6. Malicious code runs with access to the invoking user's files, network access, environment variables, and any credentials available to the process. ### Impact Assessment A compromised dependency can potentially obtain ...[truncated 446 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the SDK to an exact audited version, for example `zyndai-agent==<reviewed-version>`. 2. Generate a lockfile covering all transitive dependencies. 3. Record and enforce package hashes with pip's `--require-hashes` option. 4. Review release artifacts and update pins only through a controlled dependency-review process. 5. Install into a dedicated virtual environment rather than the user's global Python environment. 6. Never run the installation as root or an administrator. 7. Prefer immutable, reproducibly built artifacts from a trusted source. 8. Audit package import-time behavior before using an import as installation verification. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code does partially match the communication and payment portions of the description: it can send tasks to another agent via webhook, optionally use x402 micropayments, and receive/display responses. However, the declared purpose prominently includes discovering/searching for specialized agents across the Zynd network, and this code does not perform any search, discovery, or registry interaction. The --registry-url argument is defined but unused. The script’s actual purpose is narrower: direct calling of a known agent endpoint using a preconfigured local identity. This is a material scope difference, so the description does not accurately represent what this specific code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description describes a client capability for discovering, contacting, and paying other agents on the Zynd network. The supplied code does not implement those behaviors. Instead, it is a registration utility for publishing this agent to the network and keeping its webhook registration fresh. While registration may be related to participating in the network, the primary purpose is materially different from discovery-and-communication with other agents. The script also starts a long-running webhook refresh loop, but it does not search for agents, dispatch tasks, handle outbound micropayments, or retrieve responses as described.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The description presents a broad client/network capability: discovering agents by capability, communicating with them, sending tasks, paying them automatically, and receiving responses. This code chunk only implements an inbound webhook listener for receiving messages. It exposes /webhook and /webhook/sync endpoints, logs incoming messages, tracks them in memory, and provides /health and /messages endpoints. While this is related to agent communication within the Zynd ecosystem, it is only a narrow receiving component and lacks the key advertised behaviors of discovery, outbound task dispatch, and micropayment handling. Therefore the declared description materially overstates what this code chunk actually does.

Missing User Warnings

High
Confidence
97% confidence
Finding
The Create Agent response includes a base64-encoded seed but the documentation does not label it as secret key material or warn that exposure enables takeover of the agent identity and any wallet/payment functions derived from it. Because this skill supports agent registration, communication, and micropayments, mishandling the seed could let an attacker impersonate the agent or control associated funds.

Missing User Warnings

High
Confidence
95% confidence
Finding
The payment section states that the payment address is derived from the agent's seed but does not warn that seed compromise can compromise the payment identity and potentially any funds or payment authorization tied to that account. In this skill's context, where automatic x402 micropayments are a core feature, weak seed-handling guidance materially increases financial and impersonation risk.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README explicitly advertises that the skill can receive incoming webhook requests and make outbound paid agent calls, but it does not clearly warn users that enabling these features can expose prompts/data to third-party agents and can trigger real micropayment spending. In this skill’s context, that omission is materially risky because the core functionality is remote agent communication plus payment, so users may enable it without understanding privacy, trust-boundary, and cost consequences.

Session Persistence

Medium
Category
Rogue Agent
Content
### 1. Get Your API Key

1. Visit [dashboard.zynd.ai](https://dashboard.zynd.ai)
2. Connect your wallet and create an account
3. Copy your **API Key**

### 2. Configure OpenClaw
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill invokes Python scripts, requires an API key, and interacts with external network services, yet it declares no explicit tool scope or permissions boundary. This can cause an agent framework or user to underestimate the skill's ability to access secrets and make outbound requests, increasing the risk of unintended data exposure or unsafe execution.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The registration flow tells users to publish an agent name, description, capabilities, and public IP to a discovery network without prominently warning that this information becomes externally discoverable. Exposing infrastructure details and service metadata can increase attack surface, enable targeting, and leak operational information users may not expect to publish.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The workflow explicitly encourages sending user tasks to external agents but does not require a clear warning or consent step before transmitting user content off-system. Because prompts may contain proprietary, personal, or regulated data, this creates a real risk of confidentiality loss to third-party agents outside the local trust boundary.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The API reference shows webhook messages carrying DID credentials, sender/receiver identifiers, prompts, and message content, but does not warn that these fields may contain sensitive identity or task data. In a multi-agent network skill, this omission increases the chance developers will log, forward, or store this data insecurely, leading to privacy exposure or credential misuse.

External Transmission

Medium
Category
Data Exfiltration
Content
processor.close()
        else:
            # Direct HTTP POST (no payment)
            response = requests.post(
                webhook_url,
                json=message.to_dict(),
                headers={"Content-Type": "application/json"},
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The webhook handler prints full incoming message contents, sender IDs, and message IDs to stdout, and the application also exposes stored messages via a GET endpoint, but the startup UX does not clearly warn operators that all inbound data will be logged and retrievable. In an agent environment, incoming messages may contain sensitive prompts, tokens, personal data, or business data, so this default disclosure behavior increases the chance of accidental leakage through logs, consoles, or shared process output.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The /messages endpoint returns the last 20 received messages, including full message contents and sender/source metadata, with no authentication or authorization. Because the server binds to 0.0.0.0 by default, anyone who can reach the port can enumerate potentially sensitive inter-agent traffic, making this a real information disclosure issue in a network-facing component.

Static analysis

No suspicious patterns detected.