Back to skill

Security audit

Mutinynet CLI

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Mutinynet testnet faucet CLI, but its login flow can send a GitHub OAuth token to a user-configurable faucet URL and stores a reusable faucet token without explicit file permission controls.

Review this skill before installing. Use it only with a trusted faucet URL, preferably the default https://faucet.mutinynet.com, and avoid running login when MUTINYNET_FAUCET_URL may be set by someone else. Treat ~/.mutinynet/token and MUTINYNET_FAUCET_TOKEN as secrets, avoid shared machines unless permissions are locked down, and be prepared to revoke the GitHub authorization if you used an untrusted endpoint.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
src/main.rs:11
Finding

GitHub OAuth Access Token Can Be Disclosed to an Attacker-Controlled Faucet Endpoint

Content
View full analysis
Result<()> { // Fetch the GitHub client ID from the faucet let resp = get_json(&format!("{faucet_url}/auth/github/client_id"))?; let client_id = resp["client_id"] .as_str() .context("Failed to get client_id from faucet")?; // Start GitHub device flow let body = format!("client_id={client_id}&scope=user:email"); let device_resp = post_form("https://github.com/login/device/code", &body)?; let device_code = device_resp["device_code"] .as_str() .context("Missing device_code")?; let user_code = device_resp["user_code"] .as_str() .context("Missing user_code")?; let verification_uri = device_resp["verification_uri"] .as_str() .context("Missing verification_uri")?; let interval = device_resp["interval"].as_u64().unwrap_or(5); println!("Go to: {verification_uri}"); println!("Enter code: {user_code}"); println!(); println!("Waiting for authorization..."); // Poll for the access token let access_token = loop { std::thread::sleep(std::time::Duration::from_secs(interval)); let poll_body = format!( "client_id={client_id}&device_code={device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code" ); let poll_resp = post_form("https://github.com/login/oauth/access_token", &poll_body)?; if let Some(token) = poll_resp["access_token"].as_str() { break token.to_string(); } ...[truncated 3246 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
src/main.rs:74
Finding

Faucet Bearer Token Is Stored Without Explicit Owner-Only File Permissions

Content
View full analysis
Result<()> { let path = token_path(); if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; } fs::write(&path, token)?; Ok(()) } ``` ### Technical Analysis The faucet JWT is written to `~/.mutinynet/token` using `std::fs::write`, while its parent directory is created using `std::fs::create_dir_all`. The implementation does not explicitly set owner-only permissions on either the credential directory or token file. On Unix-like systems, permissions for a newly created file depend on the process umask. A permissive umask can therefore result in a token file readable by other local users. If the file already exists with overly broad permissions, rewriting it does not correct those permissions. The fixed path is also written without checks ensuring that the target is a regular file owned by the current user. Although no concrete symlink exploit is established solely by this code, rejecting symlinks and using an atomic protected-file creation pattern would reduce local file-manipulation risk. ### Attack Path 1. The CLI runs on a shared or multi-user system under a permissive umask, or `~/.mutinynet/token` already exists with broad read permissions. 2. The user completes `mutinynet-cli login`. 3. `save_token` writes the faucet JWT without enforcing an owner-only mode. 4. Another local user reads `~/.mutinynet/token`. 5. The local attacker supplies the copied value through `--token` or `MUTINYNET_FAUCET_TOKEN`. 6. The attacker submits authenticated faucet requests as the victim until the token expires or is revoked. ### Impact Assessment A local attacker who can read the file may impersonate the victim to authenticated Mutinynet fauce ...[truncated 554 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (6)

YARA rule 'ransomware_behavior': Ransomware-like patterns (mass encryption, ransom notes) [malware]

Critical
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · SKILL.md (reported line 3)May include surrounding context.

md
---
name: mutinynet-cli
description: Interact with the Mutinynet Bitcoin testnet faucet. Get testnet bitcoin on-chain, pay lightning invoices, open lightning channels, and generate bolt11 invoices. Use when the user needs signet/testnet bitcoin, wants to test lightning payments, or mentions Mutinynet.
compatibility: Requires cargo (Rust toolchain) for installation and network access to faucet.mutinynet.com
metadata:
  author: benthecarman
  version: "0.1.1"
  repository: https://github.com/benthecarman/mutinynet-cli
license: MIT
allowed-tools: Bash(mutinynet-cli:*)
---

# Mutinynet Faucet CLI

A CLI tool for interacting with the Mutinynet Bitcoin testnet faucet.

## Install

Download a prebuilt binary from [Gi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · src/main.rs (reported line 167)May include surrounding context.

rust
println!();
    println!("Waiting for authorization...");

    // Poll for the access token
    let access_token = loop {
        std::thread::sleep(std::time::Duration::from_secs(interval));

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · src/main.rs (reported line 191)May include surrounding context.

rust
println!();
    println!("Waiting for authorization...");

    // Poll for the access token
    let access_token = loop {
        std::thread::sleep(std::time::Duration::from_secs(interval));

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill instructs users to run GitHub device-flow authentication but does not clearly warn that an access token will be obtained and stored locally or supplied via environment variable. This can lead to accidental credential exposure, misuse on shared systems, or unsafe handling of the token by users who assume the command is low-risk testnet-only tooling.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The CLI persists the faucet JWT to disk in the user's home directory without setting restrictive permissions or clearly warning the user that a reusable authentication secret is being stored. If another local user, process, backup system, or malware can read that file, they can reuse the token to act as the authenticated user against the faucet service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The login flow exchanges a GitHub access token with the faucet service via an HTTP request, transmitting sensitive authentication material to a third party. While the code prints the GitHub device-flow steps, it does not explicitly warn the user that their GitHub-derived token will be sent to the faucet service.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.