This document outlines security vulnerabilities identified in the cmdjail codebase. This analysis ignores the inherent risks of the CmdMatcher and the execution of the intentCmd itself, and instead focuses on flaws that could allow an attacker to bypass the intended restrictions.
This is the most critical vulnerability. The way cmdjail evaluates rules before execution creates a classic command injection scenario if the rules are not perfectly strict.
-
The Flaw:
cmdjailvalidates the entire command string against a rule. If the rule matches, the entire, un-sanitized command string is passed tobash -c. A permissive rule (e.g., one that only checks for a command prefix) can allow an attacker to append a second, malicious command. -
Attack Scenario:
- An administrator creates a seemingly safe rule to allow users to check the status of a service:
# .cmd.jail # Allow checking the status of 'myservice' + r'^systemctl status myservice
- The attacker provides the following intent command:
cmdjail -- 'systemctl status myservice; /bin/bash' - Bypass:
- The
RegexMatcherchecks if the stringsystemctl status myservice; /bin/bashmatches the pattern^systemctl status myservice. It does. - The rule is considered a match, and
cmdjailallows the command. main.goexecutesbash -c "systemctl status myservice; /bin/bash".- The shell first runs the status command and then, because of the semicolon (
;), executes/bin/bash, giving the attacker a full, unrestricted shell.
- The
- An administrator creates a seemingly safe rule to allow users to check the status of a service:
-
Mitigation: The application should parse the command string into a command and its arguments before matching. The matching logic should then be performed on the parsed components, not the raw string. This prevents shell metacharacters from being interpreted.
The checkCmdSafety function in config.go is intended to prevent the user from manipulating the jail file, the binary, or the log file. However, its implementation using strings.Contains is easy to bypass.
-
The Flaw: The check looks for exact substrings like
.cmd.jailorcmdjail. An attacker can trivially obfuscate these strings in a way that the check misses but the shell correctly interprets. -
Attack Scenario:
- The jail file allows the
catcommand:# .cmd.jail + r'^cat
- The attacker wants to read the contents of the jail file to discover all the rules. A direct attempt is blocked:
$ cmdjail -- 'cat .cmd.jail' [error] attempting to manipulate: .cmd.jail. Aborted - Bypass: The attacker uses simple shell quoting to break up the string:
cmdjail -- 'cat ".cmd."jail' - The
checkCmdSafetyfunction checks the stringcat ".cmd."jailfor the substring.cmd.jail. It doesn't find it, so the check passes. The command is then allowed by thecatrule and executed by the shell, which concatenates the quoted parts and reads the file.
- The jail file allows the
-
Mitigation: Safety checks should operate on canonical, absolute file paths. The application should resolve any file paths in the intent command to their absolute form and then perform checks. Simple substring matching is insufficient.
In interactive shell mode, the application logic for handling exit or quit is sound—the command must be explicitly allowed by the jail file. However, there is a universal bypass.
-
The Flaw: The interactive shell in
runShellreads fromos.Stdinusing abufio.Scanner. When the user pressesCtrl+D, the input stream (stdin) is closed. This causesscanner.Scan()to returnfalse, cleanly terminating the loop and exiting the shell with code 0, regardless of the jail file rules. -
Attack Scenario:
- An administrator has a strict jail file that does not include an allow rule for
exitorquit, intending to trap the user in thecmdjailshell.# .cmd.jail + 'whoami + 'date
- The user enters the shell. Typing
exitis blocked as expected. - Bypass: The user simply presses
Ctrl+D. Thecmdjailprocess exits immediately.
- An administrator has a strict jail file that does not include an allow rule for
-
Impact: While this doesn't lead to arbitrary code execution, it bypasses the intended control to keep the user within the restricted shell.
This vulnerability applies if a CmdMatcher script validates a resource (like a file) that the intentCmd will later use.
-
The Flaw: There is a delay between when the
CmdMatcherscript checks a resource and when theintentCmdactually uses it. An attacker can alter the resource in that window. -
Attack Scenario:
- A jail file uses a script to ensure
catis only used on "safe" files in/tmp.# .cmd.jail # validator.sh checks if the file path given is in /tmp/safedir + /usr/local/bin/validator.sh
- The attacker runs a command to read a safe file but prepares a background process to exploit the race condition.
cmdjail -- 'cat /tmp/safedir/legit.txt' - Bypass:
CmdMatcherrunsvalidator.sh. The script inspects the command, sees/tmp/safedir/legit.txt, and exits 0 (success).- In the nanoseconds after
validator.shexits but beforecmdjailexecutes thecatcommand, the attacker's background process replaces/tmp/safedir/legit.txtwith a symbolic link to/etc/shadow. cmdjailexecutescat /tmp/safedir/legit.txt, which now reads the contents of/etc/shadow.
- A jail file uses a script to ensure
-
Mitigation: This is notoriously difficult to fix. The best approach is to avoid check-then-act patterns on shared resources. The matcher script should, if possible, perform the entire action itself rather than just validating parameters for another command.