Back to skill

Security audit

Bitkit Cli

Security checks for vulnerabilities and agentic risk

Overview

This is a real Bitcoin/Lightning wallet skill, but its install and agent workflows expose users to unnecessary risk of code execution and irreversible fund loss.

Install only after reviewing and pinning the installer or building from trusted source. Do not use --no-password for wallets holding real funds, keep seed phrases out of agent transcripts and logs, use small isolated wallets, set explicit fee and spending limits, and require approval before any send, pay, channel, or LSP funding action.

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

T03 · Remote Payload Retrieval and Execution

Error
Location
SKILL.md:17
Finding
Mutable Remote Installer Is Executed Directly Through a Shell<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:17` and `README.md:36-38` **Vulnerability Type**: Remote payload retrieval and immediate execution **Risk Level**: Critical ### Vulnerable Code `SKILL.md:17`: ```markdown **Install:** `curl -sSL https://raw.githubusercontent.com/synonymdev/bitkit-cli/main/install.sh | sh` ``` `README.md:36-38`: ```bash curl -sSL https://raw.githubusercontent.com/synonymdev/bitkit-cli/main/install.sh | sh ``` ### Technical Analysis The documented installation command retrieves a shell script from the mutable `main` branch and passes its contents directly to `sh`. The effective code executed by a user or agent is therefore determined at installation time, not audit time. The local `install.sh` reviewed in this artifact does not guarantee that the remote file will retain the same contents. This pattern does not provide an opportunity to inspect the downloaded script, does not pin a commit or release, and does not authenticate the script with a separately trusted signature or digest. HTTPS protects transport but does not protect against compromise of the repository, maintainer account, release workflow, or hosting account. Direct remote execution is not the minimum privilege or least-risk mechanism required to install the CLI. The script could instead be downloaded, authenticated, inspected, and executed as a separate operation. ### Attack Path 1. An attacker compromises the GitHub repository, a maintainer account, or a workflow capable of modifying `main`. 2. The attacker replaces `install.sh` with a payload that steals wallet data, modifies shell configuration, downloads further malware, or executes arbitrary commands. 3. A user or AI agent follows the installation instructions. 4. `curl` retrieves the attacker-controlled version. 5. The shell executes the payload immediately with all privileges of the invoking account. 6. If the command is run from a privileged shell or adapted to use `sudo`, the payload c ...[truncated 623 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all `curl | sh` installation instructions. 2. Pin installation to an explicit version and immutable release artifact rather than `main` or `latest`. 3. Use a staged installation process, for example: - Download the installer or archive to a local file. - Verify it against a digest distributed through an independent trusted channel. - Verify a release signature using a pinned publisher public key. - Inspect the installer before executing it. 4. Document that installation must not be performed with `sudo` unless a specific privileged operation is required and explained. 5. Publish reproducible-build metadata so users can compare release binaries with locally built artifacts. 6. Treat any verification failure, redirect anomaly, or missing verification tool as a fatal error. ]]>

T08 · Insecure Dependencies

Error
Location
install.sh:40
Finding
Installer Trusts Mutable Precompiled Binaries and Same-Origin Checksums<![CDATA[ ## Vulnerability Details **File Location**: `install.sh:40-64` **Vulnerability Type**: Insecure binary supply chain and fail-open integrity verification **Risk Level**: High ### Vulnerable Code ```sh # Determine latest tag tag="$(curl -sSL -o /dev/null -w '%{url_effective}' "https://github.com/${REPO}/releases/latest" | grep -o '[^/]*$')" archive="${BINARY}-${tag}-${target}.tar.gz" echo "Installing ${BINARY} ${tag} for ${target}..." # Download archive and checksums curl -sSL "${base_url}/${archive}" -o "${tmpdir}/${archive}" curl -sSL "${base_url}/checksums.sha256" -o "${tmpdir}/checksums.sha256" # Verify checksum cd "$tmpdir" if command -v sha256sum >/dev/null 2>&1; then grep "$archive" checksums.sha256 | sha256sum -c --quiet elif command -v shasum >/dev/null 2>&1; then grep "$archive" checksums.sha256 | shasum -a 256 -c --quiet else echo "Warning: cannot verify checksum (no sha256sum or shasum found)" >&2 fi # Extract and install tar xzf "$archive" dir="${BINARY}-${tag}-${target}" install -m 755 "${dir}/bitkit" "${install_dir}/bitkit" install -m 755 "${dir}/bk" "${install_dir}/bk" ``` ### Technical Analysis The installer dynamically resolves the newest release and retrieves both the executable archive and its checksum file from the same GitHub release channel. If that repository or release channel is compromised, an attacker can replace both files, causing checksum validation to succeed for a malicious executable. A checksum verifies accidental corruption only when its expected value is obtained from an independently trusted source. It does not establish publisher authenticity when the binary and checksum share the same compromise boundary. The verification logic is also fail-open: if neither `sha256sum` nor `shasum` exists, the installer emits a warning and proceeds to extract and install the executable. The script does not verify a publisher signature, pin a release digest, or constrain the installation to a version reviewed by the ...[truncated 1478 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require an explicit version rather than automatically installing `latest`. 2. Pin the expected SHA-256 digest in an independently reviewed installer version or trusted package manifest. 3. Sign release artifacts and checksums using a publisher key whose fingerprint is distributed independently. 4. Abort installation if no supported verification tool is available; never continue after a verification warning. 5. Validate that exactly one checksum entry matches the requested archive before invoking the checksum utility. 6. Use `curl --fail --show-error --location` so HTTP errors cannot silently produce invalid files. 7. Publish the complete source code and provide reproducible-build instructions and attestations for release binaries. 8. Prefer established package repositories that support signed metadata, immutable versions, and rollback protection. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:167
Finding
Agent Workflows Recommend Plaintext Wallet Seeds and Expose Mnemonics in JSON<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:167-190` **Vulnerability Type**: Plaintext storage and output of cryptocurrency wallet secrets **Risk Level**: High ### Vulnerable Code ```markdown #### `init` Create a new wallet. Idempotent -- re-running prints existing wallet info. ```bash bk init --no-password --json ``` ```json { "ok": true, "data": { "node_id": "02abc123...", "seed_phrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", "wallet_dir": "/root/.bitkit", "network": "mainnet", "pubky_id": "8pinxrz9tuxfz3qo5gkhdebuhtq6mrimh3matdncsrsno7kg45mo" } } ``` | Arg | Required | Description | |-----|----------|-------------| | `--no-password` | one of | Store seed as plaintext (for agents) | | `--password <pw>` | one of | Encrypt seed with AES-256-GCM + Argon2id | ``` The same insecure initialization pattern is used in the quick-start and end-to-end agent examples, including `SKILL.md:41`, `SKILL.md:1307`, and `SKILL.md:1342`. ### Technical Analysis The recommended agent initialization command explicitly disables seed encryption. The documentation states that this mode stores the seed in plaintext. A BIP39 seed phrase is sufficient to reconstruct the wallet and authorize transfers, so it must be treated as a high-value authentication secret. The documented JSON response also includes the seed phrase in machine-readable output. Agent environments commonly retain command output in transcripts, execution logs, observability platforms, CI logs, or debugging records. Redirecting stdout to `/dev/null` in some later examples does not eliminate the plaintext file risk and does not protect other invocations. This approach prioritizes unattended automation over safe secret management without imposing compensating controls such as an OS keychain, hardware-backed key storage, isolated signer, restricted wallet balance, or explicit log redaction. ### Attack Path 1. A ...[truncated 1003 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make encrypted seed storage the default and recommended mode for agents. 2. Do not return the mnemonic in routine JSON output. Provide it only through an explicit, one-time recovery workflow with prominent warnings and output isolation. 3. Do not pass wallet passwords directly through command-line arguments because they can appear in process listings and shell history. 4. Use protected secret input, an OS keychain, hardware-backed storage, or a dedicated signing service. 5. Apply restrictive permissions to every wallet directory and seed file and verify ownership before use. 6. Add automatic redaction for mnemonic-shaped values in logs and agent transcripts. 7. For unattended agents, use wallets with strict balance and spending limits and separate operational funds from treasury funds. 8. Document secure backup and seed-rotation procedures for suspected disclosure. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:1274
Finding
Messaging Workflow Pays Externally Supplied Invoices Without Required Validation or Approval<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:1274-1291` **Vulnerability Type**: Unsafe automated financial action based on untrusted message content **Risk Level**: High ### Vulnerable Code ```bash # 1. Get your own Pubky ID to share with other agents MY_ID=$(bk message whoami --json | jq -r '.data.pubky_id') # 2. Send a message to another agent bk message send "$PEER_PUBKY" "Hello, I need data analysis" --json # 3. Listen for their reply (they'll send an invoice) bk message listen "$PEER_PUBKY" --timeout 120 --json | while IFS= read -r MSG; do TYPE=$(echo "$MSG" | jq -r '.data.content' | jq -r '.type // empty' 2>/dev/null) if [ "$TYPE" = "invoice" ]; then BOLT11=$(echo "$MSG" | jq -r '.data.content' | jq -r '.bolt11') echo "Received invoice: $BOLT11" break fi done # 4. Pay the invoice bk pay "$BOLT11" --json ``` A corresponding automatic-payment pattern also appears in the Agent A workflow at `SKILL.md:1346-1358`. ### Technical Analysis The workflow treats a message whose JSON `type` equals `invoice` as sufficient authorization to invoke an irreversible payment. It does not require the agent to decode and validate the BOLT11 invoice before payment. Missing controls include verification of the encoded amount, expected payee, Bitcoin network, expiry, description, request correlation identifier, duplicate-payment status, maximum amount, and maximum routing fee. The `pay` invocation does not use the documented `--max-fee` restriction. End-to-end encryption protects message confidentiality and integrity in transit, but it does not establish that a peer remains trustworthy or that every invoice sent by that peer is authorized. A malicious or compromised expected peer can still provide an excessive, substituted, stale, or unrelated invoice. The shell example also relies on a variable assigned inside a piped `while` loop, which may execute in a subshell in common shells. Although that can cause the example to fail rather ...[truncated 1241 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Decode every BOLT11 invoice before payment and validate: - Exact expected amount and currency/network. - Expected payee identity. - Expiry and creation time. - Description or description hash. - A unique request or order identifier. 2. Enforce per-transaction, per-peer, and cumulative spending limits. 3. Always set a strict `--max-fee` value. 4. Require explicit human or policy-engine approval before irreversible payments, especially for new peers or amounts above a small threshold. 5. Persist payment hashes and reject duplicates or replayed invoices. 6. Authenticate peer identities through a separately established trust mechanism rather than relying only on a message-supplied identifier. 7. Reject malformed JSON, missing fields, unexpected message types, and invoices that do not exactly match an outstanding request. 8. Separate parsing from execution: first produce a payment preview, then authorize and execute it in a distinct step. 9. Correct the shell control flow so state is not assigned inside a pipeline subshell; however, this reliability fix must be combined with the authorization controls above. ]]>
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (19)

Docker Socket Access

High
Category
Privilege Escalation
Content
**Docker socket not found**: If you see `Cannot connect to the Docker daemon`, your Docker socket may not be at the default path. Set `DOCKER_HOST`:

```sh
export DOCKER_HOST=unix:///var/run/docker.sock
```

**Flaky channel timeouts**: Economy tests wait up to 30 seconds for channels to become usable. If a test times out on `wait_for_usable_channel`, it's usually a timing issue — retry once. Ensure `--test-threads=1` to avoid resource contention.
Confidence
90% confidence
Finding
Potential security issue detected. Manual review is recommended.

Chaining Abuse

High
Category
Tool Misuse
Content
## Installation

```bash
curl -sSL https://raw.githubusercontent.com/synonymdev/bitkit-cli/main/install.sh | sh
```

Or build from source:
Confidence
98% confidence
Finding
The `| sh` pattern creates an execution chain where remotely retrieved content is immediately executed, eliminating any opportunity for review or integrity validation. In an agent skill context this is more dangerous than in ordinary docs, because autonomous systems may follow installation guidance verbatim, turning documentation into a direct remote code execution path.

External Script Fetching

High
Category
Supply Chain
Content
echo "Installing ${BINARY} ${tag} for ${target}..."

  # Download archive and checksums
  curl -sSL "${base_url}/${archive}" -o "${tmpdir}/${archive}"
  curl -sSL "${base_url}/checksums.sha256" -o "${tmpdir}/checksums.sha256"

  # Verify checksum
Confidence
95% confidence
Finding
The installer fetches both the binary archive and its checksum file from the same remote source and then trusts that checksum for verification. If the GitHub release, redirect target, or network path is compromised, an attacker can supply both a malicious binary and matching checksum, defeating integrity verification and leading to arbitrary code execution once the installed binary is run.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The README presents fund-moving commands on mainnet by default, including address funding, channel ordering, invoice creation, and payment, without a clear warning that these actions can spend or receive real funds. For an agent-facing CLI, ambiguity about mainnet default behavior increases the chance of accidental financial loss from mistaken invocation or unsafe testing.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README explicitly instructs users and agents to initialize a wallet with `--no-password`, which creates an unencrypted seed, but it does not clearly warn that this exposes the wallet secrets at rest. In an agent context, this is especially dangerous because automation often runs on shared hosts, CI runners, containers, or machines with broader process/file access, making theft of funds substantially easier if the wallet directory is exposed.

External Transmission

Medium
Category
Data Exfiltration
Content
| Network | Chain Source | Default URL | Blocktank URL |
|---------|-------------|-------------|---------------|
| mainnet | Esplora | `https://blockstream.info/api` | `https://api1.blocktank.to/api` |
| regtest | Electrum | `tcp://127.0.0.1:60001` | `https://api.stag0.blocktank.to/blocktank/api/v2` |

Mainnet also uses [Rapid Gossip Sync](https://docs.rs/lightning-rapid-gossip-sync) for fast graph updates.
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill exposes extensive shell-driven capabilities for wallet creation, daemon control, networking, and irreversible fund movement, but does not declare any explicit tool scope or allowed-tools boundaries. In an agent setting, this increases the chance that a model can invoke high-risk shell actions without least-privilege constraints or operator review.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The quick start recommends `bk init --no-password` specifically 'for agent use', normalizing plaintext seed storage for a self-custody wallet. Because the seed is the recovery secret for all funds, any host compromise, log capture, filesystem exposure, or multi-tenant agent environment can lead to full wallet theft.

Missing User Warnings

Medium
Confidence
99% confidence
Finding
The example output includes a full mnemonic seed phrase in documentation without an adjacent warning that it is highly sensitive recovery material. In agent workflows, users often copy examples into prompts, logs, tests, or screenshots; normalizing visible seed output greatly increases the likelihood of accidental secret disclosure and total loss of funds.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The `send`, `bump-fee`, and `cpfp` sections describe irreversible on-chain fund-moving operations without strong cautions, confirmation guidance, or safe-preview defaults. In an autonomous agent context, these commands can rapidly move or drain funds to attacker-supplied addresses, and mistakes are generally not reversible.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The `pay` command supports BOLT11, LNURL, and Lightning Address flows but lacks an explicit warning that successful payment irreversibly spends funds and may reveal metadata to external services during resolution. This is especially dangerous for agent use because untrusted messages or content can embed payment requests that the agent may process automatically.

Session Persistence

Medium
Category
Rogue Agent
Content
**Location:** `<wallet_dir>/api-password` (e.g., `~/.bitkit/api-password`)
**Format:** 32 random bytes encoded as 64 hex characters
**Permissions:** File mode `0600` (owner read/write only)

```bash
# Read the password
Confidence
82% confidence
Finding
The daemon API password persists across restarts and is stored on disk, creating a reusable local secret that can be abused by malware, other local users with access, or compromised agent sessions. In a wallet-managing skill, persistent local API credentials materially increase the blast radius because they protect endpoints capable of reading balances and initiating financial actions.

Session Persistence

Medium
Category
Rogue Agent
Content
# Find install directory
  if [ -w /usr/local/bin ]; then
    install_dir="/usr/local/bin"
  elif [ -d "$HOME/.local/bin" ] || mkdir -p "$HOME/.local/bin" 2>/dev/null; then
    install_dir="$HOME/.local/bin"
  else
    echo "Cannot find writable install directory." >&2
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.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
install_dir="$HOME/.local/bin"
  else
    echo "Cannot find writable install directory." >&2
    echo "Run with sudo or create ~/.local/bin" >&2
    exit 1
  fi
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This shell script fetches release artifacts from GitHub and installs binaries into /usr/local/bin or ~/.local/bin, which modifies the user's system. Although it prints progress messages, it does not ask for confirmation or provide a clear warning that it will download remote code and place executables on PATH before doing so.

Tool Parameter Abuse

Low
Category
Tool Misuse
Content
```sh
cd bitkit-docker
docker compose down -v
rm -rf ./lnd ./lnurl-server/data
docker compose up -d
```
Confidence
15% 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).

External Script Fetching

Low
Category
Supply Chain
Content
## Installation

```bash
curl -sSL https://raw.githubusercontent.com/synonymdev/bitkit-cli/main/install.sh | sh
```

Or build from source:
Confidence
97% confidence
Finding
The installation instructions recommend fetching a remote script over the network and piping it directly to `sh`, which executes unreviewed code immediately with the user's privileges. If the hosting source, repository, network path, or account is compromised, users and agents could be induced to run arbitrary malicious code.

External Script Fetching

Low
Category
Supply Chain
Content
Bitcoin Lightning payment CLI for agents. Lowest LSP fees. Self-custody wallet with LNURL/Lightning Address support, typed exit codes, JSON envelope output, encrypted Pubky messaging, and daemon mode.

**Install:** `curl -sSL https://raw.githubusercontent.com/synonymdev/bitkit-cli/main/install.sh | sh`
**Binary names:** `bitkit` or `bk` (identical alias)
**Always use:** `--json` flag on every invocation for parseable output.
Confidence
97% confidence
Finding
The installation instruction pipes a remotely fetched script directly into `sh`, which prevents integrity review and makes the environment trust the current contents of a network resource at execution time. If the source repository, distribution path, TLS trust chain, or local network is compromised, arbitrary code can run with the user's privileges.

Excessive Permissions

Low
Category
Privilege Escalation
Content
**Location:** `<wallet_dir>/api-password` (e.g., `~/.bitkit/api-password`)
**Format:** 32 random bytes encoded as 64 hex characters
**Permissions:** File mode `0600` (owner read/write only)

```bash
# Read the password
Confidence
80% confidence
Finding
Skill requests more permissions than appear necessary for its stated functionality. Review if elevated access is justified.

Static analysis

No suspicious patterns detected.