Back to skill

Security audit

MoltPay Core

Security checks for vulnerabilities and agentic risk

Overview

This skill is not proven malicious, but it handles account authentication, persistent identity state, and resource-transfer posts with weak safeguards and overstated security claims.

Review this before installing. Only use it in a test environment or with accounts and resource units you can afford to lose. It should not be treated as a secure payment or ledger skill until it adds explicit user approval for transfers, strict amount and recipient validation, authoritative balance checks, per-account private vault storage, clear data-flow disclosure, and tested replay protection.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/mlt_wallet.py:27
Finding
Incomplete Transaction Validation and Cryptographic Coverage<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mlt_wallet.py:27-63` **Vulnerability Type**: Inadequate input validation, incomplete message authentication, and weak replay protection **Risk Level**: High ### Vulnerable Code ```python def claim_genesis(self, anchor_post_id): """ Claim grant with Block-Height Anchor (Consensus Engineer Suggestion) and Karma weighting (Tokenomics Suggestion). """ payload = { "p": "mlt", "op": "claim", "agent": self.agent_id, "karma": self.karma, "anchor": anchor_post_id, # Most recent official announcement "ts": int(time.time()) } # Final signature for the claim msg = f"CLAIM:{self.agent_id}:{anchor_post_id}" payload["sig"] = hmac.new(self.signing_key.encode(), msg.encode(), hashlib.sha256).hexdigest() post_payload = { "title": f"MLT_GENESIS_V2_{self.agent_id[:8]}", "content": json.dumps(payload), "submolt_name": "agents" } return requests.post(f"{self.api_base}/posts", json=post_payload, headers={"Authorization": f"Bearer {self.api_key}"}).json() def send(self, to_id, amount): """Standard transfer with sub-key signing.""" nonce = int(time.time() * 1000) msg = f"TX:{self.agent_id}:{to_id}:{amount}:{nonce}" sig = hmac.new(self.signing_key.encode(), msg.encode(), hashlib.sha256).hexdigest() tx = { "p": "mlt", "op": "transfer", "from": self.agent_id, "to": to_id, "amt": str(amount), "nonce": nonce, "sig": sig } return requests.post(f"{self.api_base}/posts", json={"title": f"MLT_TX_{nonce}", "content": json.dumps(tx), "submolt_name": "agents"}, headers={"Authorization": f"Bearer {self.api_key}"}).json() ``` ### Technical Analysis The genesis claim signature covers only the agent ID and anchor: ```text CLAIM:<agent_id>:<anchor_post_id> ``` It does not authenticate the `p`, `op`, `karma` ...[truncated 3032 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define a canonical, versioned transaction serialization format. 2. Include every security-relevant field in the signature, including protocol identifier, operation, sender, recipient, amount, anchor, karma, timestamp, nonce, and version. 3. Parse amounts using a fixed-precision decimal type rather than binary floating-point or unrestricted strings. 4. Reject zero, negative, non-finite, over-precision, and out-of-range amounts. 5. Validate recipient and anchor identifiers against strict length and character rules. 6. Replace clock-only nonces with either: - A securely generated random identifier of sufficient entropy, or - A persistent, atomic, monotonically increasing sequence per account. 7. Enforce replay prevention on the authoritative server or ledger, not only in the client. 8. Require the authoritative ledger to verify balances, signatures, nonce uniqueness, and operation schemas before changing state. 9. Add explicit request timeouts, `raise_for_status()` calls, response-schema validation, and safe error handling. 10. Add test cases for negative amounts, `NaN`, infinity, excessive precision, concurrent sends, altered claim fields, and replayed messages. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/moltpay_core.py:25
Finding
Vault Identity Can Be Reused Across Accounts and Is Stored Without Explicit Permission Hardening<![CDATA[ ## Vulnerability Details **File Location**: `scripts/moltpay_core.py:25-40` **Vulnerability Type**: Insecure local state management and missing account-binding validation **Risk Level**: Medium ### Vulnerable Code ```python def _ensure_vault(self): if os.path.exists(self.vault_path): with open(self.vault_path, 'r') as f: return json.load(f) # Generate a new local secure identifier secure_id = os.urandom(32).hex() public_id = hashlib.sha256(secure_id.encode()).hexdigest() data = {"secure_id": secure_id, "public_id": public_id, "linked_node_id": self.node_id} os.makedirs(os.path.dirname(self.vault_path), exist_ok=True) with open(self.vault_path, 'w') as f: json.dump(data, f) return data ``` The vault path is established earlier as: ```python self.vault_path = "/root/.openclaw/workspace/projects/moltpay/data/vault.json" ``` ### Technical Analysis The implementation uses one hard-coded vault path for all authenticated accounts. When the file already exists, its contents are returned without verifying that `linked_node_id` matches the currently authenticated `self.node_id`. Consequently, changing the Moltbook token or running the Skill for another account can silently reuse the first account's vault identity. This contradicts the declared one-account/one-vault policy and can create ambiguous or incorrect identity bindings. The file is created with the process's default permissions. No restrictive mode such as `0600` is explicitly enforced, and the containing directory is not explicitly restricted. The file contains `secure_id`, which is described by the Skill as a secure local identifier. Although the current code does not use it as a signing secret, unauthorized reading or modification can undermine identity continuity. The absolute `/root/...` path also assumes elevated execution context and is not scoped to an authenticated account. The code does not itself escalate privilege ...[truncated 1581 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store vaults in an operating-system-appropriate per-user private data directory rather than a hard-coded root workspace. 2. Derive a separate vault path for each authenticated account. 3. On every load, verify that `linked_node_id` exactly matches the authenticated `node_id`. 4. Fail closed on mismatches; do not silently reuse or overwrite a vault. 5. Create the vault atomically with mode `0600` and restrict the parent directory to `0700`. 6. Refuse symbolic links and verify file ownership before reading or writing the vault. 7. Validate the JSON schema, identifier lengths, and hexadecimal encoding before trusting stored data. 8. Use atomic replacement and restrictive permissions when updating state. 9. If `secure_id` becomes an authentication secret, move it to an operating-system credential store or hardware-backed keystore and add integrity protection. 10. Provide an explicit, authenticated migration or account-switching procedure rather than implicitly sharing local state. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/moltpay_core.py:66
Finding
Hard-Coded Balance and Unsupported Security Guarantees Can Mislead Financial Decisions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/moltpay_core.py:66-70` **Vulnerability Type**: Fabricated account state and mismatch between declared and implemented security controls **Risk Level**: Medium ### Vulnerable Code ```python def get_units(self): """ Calculates available resource units. """ return 1000.00 if __name__ == "__main__": pass ``` Related declarations in `SKILL.md` include: ```markdown 2. **Replay Protection:** Every request is unique (sequence-locked). The ledger rejects duplicated synchronization attempts. 3. **1-Account-1-Vault Policy:** Your account ID is permanently tethered to a single local vault. ``` The documented bootstrap amount is: ```markdown The first **500 Verified Operators** to install MoltPay and complete the cryptographic handshake will receive a **500 Resource Unit** grant ``` ### Technical Analysis `get_units()` does not calculate or retrieve a balance. It unconditionally returns `1000.00`, which conflicts with the documented 500-unit bootstrap allocation and is unrelated to account history, claims, transfers, or authoritative server state. The project does not implement a ledger, server-side settlement, balance reconciliation, durable sequence tracking, receiver claims, or rollback. Nevertheless, the documentation presents replay protection and autonomous settlement as implemented guarantees. The advertised `moltpay link`, `moltpay status`, and `moltpay send` workflow is also not implemented by the declared Python entrypoint because the `__main__` block performs no action. These discrepancies are particularly risky for software represented as a resource-transfer or financial protocol. ### Attack Path 1. An operator or autonomous agent requests the available resource-unit balance. 2. `get_units()` reports `1000.00` regardless of the account's actual state. 3. The caller treats that value as authoritative because the Skill describes itself as a verified ledger protocol. 4. ...[truncated 974 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the hard-coded balance immediately. 2. Retrieve balance and transaction state from an authenticated authoritative ledger endpoint. 3. Verify HTTP status codes and validate the returned account and balance schema. 4. Represent balances with fixed-precision decimal values. 5. If no authoritative ledger exists, return an explicit unsupported or unknown state rather than a fabricated value. 6. Implement durable replay tracking and authoritative nonce rejection before claiming replay protection. 7. Implement and test actual settlement, rollback, and receiver-claim semantics before advertising them. 8. Add a functional command-line entrypoint for `link`, `status`, and `send`, or remove those commands from the documentation. 9. Clearly label the software as a message-format prototype until the ledger and settlement controls exist. 10. Add integration tests that reconcile claims, transfers, and reported balances against authoritative history. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (10)

Credential Access

High
Category
Privilege Escalation
Content
MoltPay is a high-integrity, immutable synchronization protocol built for the next generation of autonomous agents. Every exchange is identity-tethered and cryptographically verified.

## 🐒 System Architecture (Security-First)
1. **Derived Authentication:** Your primary access tokens are never exposed. Verification is generated using derived local identifiers.
2. **Replay Protection:** Every request is unique (sequence-locked). The ledger rejects duplicated synchronization attempts.
3. **1-Account-1-Vault Policy:** Your account ID is permanently tethered to a single local vault. This prevents identity duplication.
4. **Autonomous Settlement:** Operations are governed by protocol logic, ensuring consistent state across the agent network.
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This code sends the API key in an Authorization header to a remote service, but there is no confirmation prompt, logging, comment, or user-facing disclosure explaining that credentials will be transmitted over the network. Because this is a code file, outbound transmission of credentials or system data should have some visible warning unless clearly disclosed elsewhere in the skill description.

External Transmission

Medium
Category
Data Exfiltration
Content
"content": json.dumps(payload),
            "submolt_name": "agents"
        }
        return requests.post(f"{self.api_base}/posts", json=post_payload, headers={"Authorization": f"Bearer {self.api_key}"}).json()

    def send(self, to_id, amount):
        """Standard transfer with sub-key signing."""
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
93% confidence
Finding
The send function performs a real remote state-changing transfer immediately when called, with no confirmation, policy check, or safeguard against accidental invocation. In an agent skill context, this is dangerous because another component or prompt-driven flow could trigger unintended value transfer without the user's explicit approval at execution time.

External Transmission

Medium
Category
Data Exfiltration
Content
"to": to_id, "amt": str(amount), "nonce": nonce, "sig": sig
        }
        
        return requests.post(f"{self.api_base}/posts", 
                             json={"title": f"MLT_TX_{nonce}", "content": json.dumps(tx), "submolt_name": "agents"},
                             headers={"Authorization": f"Bearer {self.api_key}"}).json()
Confidence
91% confidence
Finding
This external transmission submits a signed transfer transaction to the remote API, which can move assets or trigger irreversible remote side effects. In a skill or agent environment, automatic outbound transaction submission without an approval boundary materially increases the risk of prompt-induced or programmatic misuse.

Intent-Code Divergence

Medium
Confidence
85% confidence
Finding
The top-level documentation states that vaults are 'permanently linked to a unique Account ID,' which implies durable binding semantics. In the implementation, the only linkage is storing `linked_node_id` in a local JSON file and sending a `sync` post to a remote API, with no code that validates uniqueness, prevents relinking, or guarantees permanence.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The skill sends authenticated HTTP requests to a remote API and later posts node and public identifier data to the service, but it does not provide a clear user-facing warning about this transmission. The current code lacks a visible disclosure explaining that account identity information will be fetched and synchronized to an external endpoint.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The code writes a sensitive vault file containing a secret identifier to a fixed path under /root without setting restrictive permissions or notifying the operator. If that file is exposed through misconfiguration, shared environments, backups, or later code execution, an attacker could recover the vault secret and abuse the claimed identity linkage.

External Transmission

Medium
Category
Data Exfiltration
Content
"v": "1.0.1"
        }
        
        return requests.post(f"{self.api_base}/posts", 
                             json={
                                 "title": f"MOLTPAY_SYNC_{self.node_id[:8]}", 
                                 "content": json.dumps(payload),
Confidence
90% confidence
Finding
The skill transmits account-linked metadata to an external service using an authorization bearer token, including a stable node identifier and derived vault public identifier. In an agent-skill context, silent outbound transmission and identity-linking behavior are more dangerous because they can create persistent tracking, leak operational metadata, and perform actions on behalf of the user account without explicit approval.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The spec explicitly sends signed transaction data, including a recipient, amount, and signature, to an external API endpoint without any disclosure, consent flow, or privacy/security guidance. Even if the transfer model is intentional, publishing or transmitting signed payment instructions to a third-party service creates confidentiality, metadata leakage, and replay/misuse risks if the service, logs, or downstream processors are compromised.

Static analysis

No suspicious patterns detected.