T09 · Insecure Skill Coding Practices
- Location
run.py:141- Finding
Unauthenticated Server Response Controls Autonomous Mainnet Payment
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
RunBox openly provides paid remote code execution, but it can automatically spend real Stellar USDC through an insecure HTTP payment flow with weak limits and user control.
Review before installing. Use only a dedicated low-balance Stellar wallet, prefer testnet, avoid submitting sensitive code or secrets, and do not allow automatic mainnet payment unless you trust the endpoint and accept that server responses can trigger irreversible USDC transfers.
run.py:141Unauthenticated Server Response Controls Autonomous Mainnet Payment
run.py:174User Code and Session Bearer Token Are Transmitted Over Cleartext HTTP
requirements.txt:1Wallet-Sensitive Dependencies Are Installed Without Reproducible Version Pinning
The endpoint used for the rent request is derived from an environment variable and defaults to plain HTTP, so a user or attacker controlling configuration or network position can redirect requests to an untrusted server. In this skill, that server dictates payment instructions, meaning the client may autonomously send real USDC to an attacker-controlled destination before executing code.
rent_url = f"{RUNBOX_ENDPOINT}/api/exec/rent"
print("Requesting RunBox session...", file=sys.stderr)
r = requests.post(rent_url, json={"language": language}, timeout=15)
if r.status_code == 200:
return r.json()
After making a blockchain payment, the client retries session creation against the same configurable endpoint without authenticating that the service is legitimate. If the endpoint was malicious or tampered with, the tool can both lose funds and disclose payment proof to an attacker while obtaining a fake session token.
# Retry with payment proof
# For x402, the client would normally include a signed auth entry.
# Here we include the tx hash as proof for the server to verify.
retry = requests.post(
rent_url,
json={"language": language},
headers={"X-Payment-Hash": tx["hash"], "X-Payment-Network": network},
The code execution request sends both user code and a bearer session token to a configurable endpoint that also defaults to insecure HTTP. If redirected or intercepted, an attacker can steal the session token, read submitted code, and impersonate the client against the remote execution service.
def run_code(session_token: str, language: str, code: str) -> dict:
"""Execute code using a valid session token."""
r = requests.post(
f"{RUNBOX_ENDPOINT}/api/exec/run",
json={"language": language, "code": code},
headers={"Authorization": f"Bearer {session_token}"},
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
rent_url = f"{RUNBOX_ENDPOINT}/api/exec/rent"
print("Requesting RunBox session...", file=sys.stderr)
r = requests.post(rent_url, json={"language": language}, timeout=15)
if r.status_code == 200:
return r.json()
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# Retry with payment proof
# For x402, the client would normally include a signed auth entry.
# Here we include the tx hash as proof for the server to verify.
retry = requests.post(
rent_url,
json={"language": language},
headers={"X-Payment-Hash": tx["hash"], "X-Payment-Network": network},
The script sends the user's code and bearer session token to a configurable remote endpoint, defaulting to plain HTTP. If the endpoint is malicious or traffic is intercepted, sensitive code, execution data, and reusable session credentials can be exposed or altered; in this skill, remote execution is the core feature, so endpoint trust and transport security are especially critical.
def run_code(session_token: str, language: str, code: str) -> dict:
"""Execute code using a valid session token."""
r = requests.post(
f"{RUNBOX_ENDPOINT}/api/exec/run",
json={"language": language, "code": code},
headers={"Authorization": f"Bearer {session_token}"},
The README explicitly instructs an agent to autonomously spend funds from a configured Stellar wallet whenever a user asks to run code, asserting that 'the user has already funded your Stellar wallet.' In an agent-skill context, this creates a real risk of unauthorized or unintended financial transactions because the agent may initiate blockchain payments without explicit per-transaction user consent or spending limits.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
git clone https://github.com/daraijaola/Runbox.git
cd Runbox
cp .env.example .env # fill in STELLAR_RECEIVE_ADDRESS and SESSION_JWT_SECRET
pnpm install
pnpm --filter @workspace/api-server run build
pnpm --filter @workspace/api-server run start
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
git clone https://github.com/daraijaola/Runbox.git
cd Runbox
cp .env.example .env # fill in STELLAR_RECEIVE_ADDRESS and SESSION_JWT_SECRET
pnpm install
pnpm --filter @workspace/api-server run build
pnpm --filter @workspace/api-server run start
The skill promotes automatic invocation and states 'No human approval' while omitting a prominent warning that real funds may be spent from the configured Stellar wallet. This is especially dangerous because the skill is user-invocable and designed for autonomous payment, so users and operators may unknowingly authorize financial transactions and remote code submission merely by making common requests.
The code submits a live Stellar payment automatically, and mainnet is the default network. A user invoking code execution may unknowingly authorize real cryptocurrency spending, and a malicious or compromised service can repeatedly induce charges or manipulate pricing targets within the payment flow.
The system-prompt example instructs the agent to use STELLAR_SECRET_KEY to pay autonomously as part of normal task handling. Embedding payment behavior into a reusable system prompt is dangerous because it encourages agents to treat spending as routine tool use, making prompt injection, ambiguous requests, or malicious user tasks more likely to trigger real-money transactions.
The setup instructions tell users to export STELLAR_SECRET_KEY directly for automated use without prominent guidance on secure secret handling, storage, rotation, or scope minimization. In agent environments, exposing a signing key this way increases the chance of accidental disclosure through logs, process inspection, misconfigured toolchains, or prompt-induced misuse.
The skill explicitly requires a wallet secret and communicates with a remote endpoint, yet it declares no tool scope or permission boundaries. In an agent ecosystem, this means the skill can be auto-invoked with access to sensitive environment data and outbound network behavior without clear operator consent, increasing the chance of unintended secret use and autonomous spending.
The invocation guidance is broad enough to match many ordinary requests such as 'calculate', 'test this', or 'write and run a Python script', causing the agent to trigger a paid remote execution service in situations where it could answer locally. Because invocation can autonomously spend funds and send user-provided code/content to an external server, overbroad triggers materially increase the risk of unnecessary charges and data exfiltration.
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def run_code(session_token: str, language: str, code: str) -> dict:
"""Execute code using a valid session token."""
r = requests.post(
f"{RUNBOX_ENDPOINT}/api/exec/run",
json={"language": language, "code": code},
headers={"Authorization": f"Bearer {session_token}"},
The skill prints the reusable session token directly to stderr, exposing a bearer credential to shell history capture, logs, CI output, terminal recorders, or other observers. Because the token authorizes remote code execution, anyone who sees it can reuse the paid session until expiry.
The skill loads a Stellar secret key from the environment and uses it to sign real USDC payments. Combining remote code-execution functionality with autonomous wallet spending greatly increases blast radius: compromise of the endpoint, invocation flow, or surrounding agent can directly trigger financial loss.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
rent_url = f"{RUNBOX_ENDPOINT}/api/exec/rent"
print("Requesting RunBox session...", file=sys.stderr)
r = requests.post(rent_url, json={"language": language}, timeout=15)
if r.status_code == 200:
return r.json()
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
rent_url = f"{RUNBOX_ENDPOINT}/api/exec/rent"
print("Requesting RunBox session...", file=sys.stderr)
r = requests.post(rent_url, json={"language": language}, timeout=15)
if r.status_code == 200:
return r.json()
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# Retry with payment proof
# For x402, the client would normally include a signed auth entry.
# Here we include the tx hash as proof for the server to verify.
retry = requests.post(
rent_url,
json={"language": language},
headers={"X-Payment-Hash": tx["hash"], "X-Payment-Network": network},
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# Retry with payment proof
# For x402, the client would normally include a signed auth entry.
# Here we include the tx hash as proof for the server to verify.
retry = requests.post(
rent_url,
json={"language": language},
headers={"X-Payment-Hash": tx["hash"], "X-Payment-Network": network},
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
# Retry with payment proof
# For x402, the client would normally include a signed auth entry.
# Here we include the tx hash as proof for the server to verify.
retry = requests.post(
rent_url,
json={"language": language},
headers={"X-Payment-Hash": tx["hash"], "X-Payment-Network": network},
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
# Retry with payment proof
# For x402, the client would normally include a signed auth entry.
# Here we include the tx hash as proof for the server to verify.
retry = requests.post(
rent_url,
json={"language": language},
headers={"X-Payment-Hash": tx["hash"], "X-Payment-Network": network},
No suspicious patterns detected.