PT-2026-25515 · Crates.Io · Zeptoclaw
Published
2026-03-05
·
Updated
2026-03-05
CVSS v3.1
10
Critical
| Vector | AV: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 thepython3 -P -c "..." - to bypass the
LITERAL BLOCKED PATTERNS, file name wildcards can do the work, such ascat /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
Affected Products
Zeptoclaw