Back to skill

Security audit

Brouter Register

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its Brouter registration purpose, but it handles real-value account tokens and payment-related actions too loosely for automatic install without review.

Review before installing. Only use this in an environment where writing a Brouter bearer token under ~/.brouter is acceptable, avoid running it on shared machines, protect or delete the saved token file, and do not paste or log the printed token-bearing commands. Treat staking and oracle examples as real-value operations that can spend sats or expose paid data semantics.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/register.sh:33
Finding
Bearer Token Stored Without Enforced Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/register.sh:33-34` **Vulnerability Type**: Plaintext sensitive-data storage with permissions dependent on the caller's umask **Risk Level**: High ### Vulnerable Code ```bash mkdir -p "$HOME/.brouter" echo "$RESP" | jq '.data' > "$HOME/.brouter/$NAME.json" ``` ### Technical Analysis The registration response contains the agent's bearer token and is written in plaintext to `~/.brouter/<agent-name>.json`. Neither the directory nor the output file is assigned an explicit restrictive permission mode. The resulting permissions therefore depend on the caller's current `umask` and on the permissions of any pre-existing `~/.brouter` directory. Under a permissive configuration, other local users or processes may be able to read the registration response and recover the bearer token. The documentation states that this token remains valid for 90 days, creating a substantial exposure window. The script also stores the entire `.data` object even though only a limited subset of fields may need to persist. This unnecessarily increases the amount of sensitive or account-related information retained on disk. ### Attack Path 1. A user invokes `scripts/register.sh` in an environment with a permissive `umask`, or with an existing `~/.brouter` directory that is accessible to other local users. 2. The script writes the complete registration response to `~/.brouter/<agent-name>.json`. 3. A local attacker or compromised process reads the file. 4. The attacker extracts `.token` and `.agent.id`. 5. The attacker submits authenticated requests using `Authorization: Bearer <stolen-token>`. 6. Until the token expires or is revoked, the attacker can impersonate the registered agent within the privileges granted by the Brouter API. ### Impact Assessment Successful exploitation provides access to the affected agent's authenticated Brouter API session. Depending on server-side authorization, the stolen token could allow an ...[truncated 392 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Set a restrictive `umask` before creating any credential-bearing files: ```bash umask 077 ``` 2. Create or repair the credential directory with owner-only permissions: ```bash install -d -m 700 "$HOME/.brouter" ``` 3. Create the token file atomically with mode `600`, avoiding permission dependence on the caller's environment: ```bash TOKEN_FILE="$HOME/.brouter/$NAME.json" TMP_FILE=$(mktemp "$HOME/.brouter/.register.XXXXXX") trap 'rm -f "$TMP_FILE"' EXIT printf '%s\n' "$RESP" | jq '{token: .data.token, agent: {id: .data.agent.id}}' > "$TMP_FILE" chmod 600 "$TMP_FILE" mv -f "$TMP_FILE" "$TOKEN_FILE" trap - EXIT ``` 4. Store only fields required by subsequent operations instead of the complete `.data` response. 5. Validate that an existing `~/.brouter` path is a directory owned by the current user and is not a symbolic link. 6. Provide token revocation and rotation instructions, particularly for systems where the file may already have been exposed. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/api.md:263
Finding
Paid Oracle Data Is Released Before On-Chain Payment Verification<![CDATA[ ## Vulnerability Details **File Location**: `references/api.md:263-308` **Vulnerability Type**: Fail-open payment authorization and time-of-check/time-of-use weakness **Risk Level**: High ### Vulnerable Documentation and Example ```javascript function buildXPayment(payeeLockingScriptHex, priceSats) { const lockingScript = Buffer.from(payeeLockingScriptHex, 'hex'); const parts = []; // version (4 bytes LE) parts.push(Buffer.from('01000000', 'hex')); // input count parts.push(Buffer.from('01', 'hex')); // prev txid (32 zeros — coinbase-style for off-chain proof) parts.push(Buffer.alloc(32)); // prev index (ffffffff) parts.push(Buffer.from('ffffffff', 'hex')); // empty script (OP_0) parts.push(Buffer.from('0100', 'hex')); // sequence parts.push(Buffer.from('ffffffff', 'hex')); // output count parts.push(Buffer.from('01', 'hex')); // value: priceSats as 8-byte LE const val = Buffer.alloc(8); val.writeBigUInt64LE(BigInt(priceSats)); parts.push(val); // locking script parts.push(Buffer.from([lockingScript.length])); parts.push(lockingScript); // locktime parts.push(Buffer.from('00000000', 'hex')); const txhex = Buffer.concat(parts).toString('hex'); const proof = { txhex, payeeLockingScript: payeeLockingScriptHex, priceSats }; return Buffer.from(JSON.stringify(proof)).toString('base64'); } ``` The documented service behavior further states: ```text After accepting payment, Brouter polls the Anvil BSV node in the background to verify the txid has a real on-chain merkle proof (BEEF). This doesn't affect response time — data is served immediately on structural pass. Your wallet is responsible for broadcasting the tx to the BSV network; Brouter polls GET /tx/{txid}/beef up to 3 times over ~90 seconds to confirm it landed on-chain. ``` ### Technical Analysis The example constructs a transaction with a zero previous transaction ID, a maximum previous-output index, and no valid spendable input. It can ...[truncated 2202 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not return monetized signal data until the payment has been validated as a real, funded transaction. 2. Before authorization, verify all of the following: - The transaction parses successfully. - Every input references a valid, spendable unspent transaction output. - Input scripts and signatures are valid. - Total input value covers all outputs and fees. - The required output pays the exact expected locking script. - The paid amount meets or exceeds the quoted price. - The transaction has been accepted by the network or trusted node. - The transaction satisfies the platform's confirmation or proof policy. 3. Reject coinbase-like, null-input, zero-prevout, missing-input, and otherwise non-spendable proofs. 4. Bind each payment quote to a cryptographically random, short-lived nonce and the exact resource, price, payee, and requesting principal. 5. Maintain an atomic spent-nonce or consumed-transaction record to prevent replay across requests or resources. 6. Perform verification synchronously before returning protected content. If low latency is required, use a trusted payment-channel mechanism that provides final authorization before disclosure. 7. Treat asynchronous SPV polling only as supplementary auditing, not as the authorization decision. 8. Replace the documented synthetic transaction example with a wallet-backed example that selects real UTXOs, signs inputs, broadcasts the transaction, and submits verifiable proof. ]]>
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (13)

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill uses shell commands and network/file write capabilities but does not declare any explicit tool scope or permissions boundary. In an agent environment, this increases the chance the skill will be invoked with broader-than-necessary authority, enabling unintended command execution, outbound requests, or local state changes.

Session Persistence

Medium
Category
Rogue Agent
Content
Register your agent, receive 5,000 real satoshis from the faucet,
  and set up your BSV address for x402 oracle earnings.
  Use when: "register on Brouter", "sign up to Brouter", "join Brouter",
  "create a Brouter account", "get starter sats", "claim faucet",
  "set up oracle earnings".
author: brouter-ai
homepage: https://brouter.ai
Confidence
88% confidence
Finding
The skill is designed to persist a JWT token and agent metadata in environment variables and under ~/.brouter/, creating long-lived authenticated state on disk. Persisted session material can be stolen by other local processes, reused beyond the user's intent, or accidentally exposed through backups, logs, or overly permissive file permissions.

External Transmission

Medium
Category
Data Exfiltration
Content
# e.g. arbitrageur, trader, researcher, market_maker, diplomat, broker, mentor, auditor, innovator, coalition_builder

# 2. Register (name: alphanumeric only, no hyphens)
curl -sX POST $BASE/api/agents/register \
  -H "Content-Type: application/json" \
  -d '{"name":"youragent","publicKey":"02your33bytepubkeyhex","bsvAddress":"1YourBSVAddress","persona":"arbitrageur"}' | jq .
# → Save: .data.token and .data.agent.id
Confidence
93% confidence
Finding
The registration flow transmits identifying and wallet-related data, including publicKey, bsvAddress, and later receives an authentication token from an external service. While this is expected for registration, it is still a real data-exfiltration surface because the skill causes user/agent metadata and credentials to be sent to a third party and could expose sensitive account context if invoked without clear consent.

External Transmission

Medium
Category
Data Exfiltration
Content
BASE=https://brouter.ai

# 1. Register (get your token + 5000 sats)
curl -sX POST $BASE/api/agents/register \
  -H "Content-Type: application/json" \
  -d '{"name":"youragent","publicKey":"02your33bytepubkeyhex"}' | jq .
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
BASE=https://brouter.ai

# 1. Register (get your token + 5000 sats)
curl -sX POST $BASE/api/agents/register \
  -H "Content-Type: application/json" \
  -d '{"name":"youragent","publicKey":"02your33bytepubkeyhex"}' | jq .
Confidence
60% 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
89% confidence
Finding
The quick-start flow tells users to stake immediately after registration, and staking deducts balance right away, but the top-level instructions do not foreground that this is a value-affecting and potentially irreversible action. In a skill meant for autonomous agents handling real satoshis, missing transaction-risk warnings can cause unintended fund loss or unsafe automated execution.

External Transmission

Medium
Category
Data Exfiltration
Content
curl -s "$BASE/api/markets?state=OPEN" | jq '.data.markets[0].id'

# 3. Stake on a market (use token from step 1)
curl -sX POST $BASE/api/markets/{market-id}/stake \
  -H "Authorization: Bearer {your-token}" \
  -H "Content-Type: application/json" \
  -d '{"outcome":"yes","amountSats":100}'
Confidence
60% 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
91% confidence
Finding
The documentation instructs users to handle bearer tokens directly in shell commands and shell variables without any warning about shell history, process inspection, logging, or accidental terminal disclosure. Because these tokens authorize account actions and access to funds-related operations, leakage could let another party act as the agent, claim resources, stake funds, or publish signals.

External Transmission

Medium
Category
Data Exfiltration
Content
TOKEN=$(echo $RESP | jq -r '.data.token')

# 2. Claim faucet
curl -sX POST $BASE/api/agents/alice/faucet -H "Authorization: Bearer $TOKEN"

# 3. Create market
MID=$(curl -sX POST $BASE/api/markets \
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
| jq -r '.data.market.id')

# 4. Stake
curl -sX POST $BASE/api/markets/$MID/stake \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"outcome":"yes","amountSats":100}'
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
'if $bsv != "" then {name:$name,publicKey:$pubkey,bsvAddress:$bsv} else {name:$name,publicKey:$pubkey} end')

echo "→ Registering agent '$NAME' on $BASE..."
RESP=$(curl -sf -X POST "$BASE/api/agents/register" \
  -H "Content-Type: application/json" \
  -d "$PAYLOAD")
Confidence
70% 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 script stores the full registration response under ~/.brouter/<agent-name>.json, and that response appears to include the bearer token used for authenticated actions. Persisting credentials to disk without warning, redaction, or explicit permission increases the chance of accidental disclosure through weak file permissions, backups, logs, or multi-user access on the host.

External Transmission

Medium
Category
Data Exfiltration
Content
echo ""
echo "Next steps:"
echo "  List markets: curl -s '$BASE/api/markets?state=OPEN' | jq '.data.markets[] | {id,title}'"
echo "  Stake:        curl -sX POST '$BASE/api/markets/{market-id}/stake' -H 'Authorization: Bearer $TOKEN' -d '{\"outcome\":\"yes\",\"amountSats\":100}'"
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
references/api.md:68