注1:问题来自多字节编码。例如在GBK里,0xbf27并不是一个合法的双字节字符,因此addslash()会把它转义成0xbf5c27,碰巧0xbf5c是一个合法的双字节字符,由此可以注入一个0x27 (')。addslash()和mysql_escape_string无药可救。mysql_real_escape_string()可以根据字符集正确地转义,但是需要在建立数据库联接的时候指明“SET CHARACTER SET 'GBK'”。
}
提交:http://localhost/phpBB2/privmsg.php?folder[]=
回显:
Warning: htmlspecialchars() expects parameter 1 to be string, array given in /www/phpbb2/privmsg.php on line 61
4,bypass GPC或addslashes注入
在GPC或者addslashes的影响下,变量被放在一对单引号中,进行SQL注入攻击时首先就是要闭合单引号才能继续构造查询语句,这时单引号会被转义成\'导致攻击失败,由于urldecode函数对变量的解码处理导致我们给变量赋值%2527,浏览器会把%25解码为%,然后%27被带入urldecode函数处理,再解码为',这样的话,单引号就被顺利带入并绕过了GPC或者addslashes,实例wordpress的wp-admin/admin-ajax.php页面注入漏洞:
define('DOING_AJAX', true);
check_ajax_referer();
if ( !is_user_logged_in() )
die('-1');
...漏洞出在check_ajax_referer(); 函数,进一步看下去
function check_ajax_referer() {
$cookie = explode('; ', urldecode(empty($_POST['cookie']) ?
$_GET['cookie'] : $_POST['cookie'])); // AJAX scripts must pass
cookie=document.cookie
foreach ( $cookie as $tasty ) {
if ( false !== strpos($tasty, USER_COOKIE) )
$user = substr(strstr($tasty, '='), 1);
if ( false !== strpos($tasty, PASS_COOKIE) )
$pass = substr(strstr($tasty, '='), 1);
}
if ( !wp_login( $user, $pass, true ) )
die('-1');
注意这一段句:
$cookie = explode('; ', urldecode(empty($_POST['cookie']) ?
$_GET['cookie'] : $_POST['cookie'])); // AJAX scripts must pass
用urldecode函数处理cookie,而处理后的用户信息最终被带入数据库语句查询
function get_userdatabylogin($user_login) {
global $wpdb;
...
if ( !$user = $wpdb->get_row("Select * FROM $wpdb->users
Where user_login = '$user_login'") )
return false;
这样攻击者构造用户信息为%2527 and 1=1就会在urldecode函数作用下顺利闭合单引号,实现SQL注入攻击
if ( !$user = $wpdb->get_row("Select * FROM $wpdb->users
Where user_login = ‘’ and 1=1") )
实例2PHP168SQL注入漏洞
if(!$keyword)
{
extract($db->get_one("SELECT keywords AS keyword FROM {$pre}article WHERE aid='$id'"));
}
if($keyword){
$SQL.=" AND ( ";
$keyword=urldecode($keyword);
$detail=explode(" ",$keyword);
unset($detail2);
foreach( $detail AS $key=>$value){
$detail2[]=” BINARY title LIKE ‘%$value%’ “;
}
$str=implode(” OR “,$detail2);
$SQL.=” $str ) “;
}else{
$SQL.=” AND 0 “;
}
$ORDER=’ list ‘;
}
if(!$webdb[viewNoPassArticle]){
$SQL.=’ AND yz=1 ‘;
}
$SQL=” WHERE $SQL ORDER BY $ORDER DESC LIMIT $rows”;
$which=’*';
$listdb=list_article($SQL,$which,$leng);
$keyword经过urldecode后进入SQL查询语句,漏洞由此产生
5.安全使用intval函数
关于这个函数的功能不再多说什么了,手册上写的很明白
intval最常用的是在程序中过滤进入数据库的变量,将其转换为整型,防止SQL注入攻击的产生
但是使用不当的话则会起不到检查的作用,下面就结合一个实例来说明这个问题
国内某个CMS系统,经过ZEND加密了,解密后某文件代码如下:
$id = isset( $_GET['id'] ) ? $_GET['id'] : 0;
if ( intval( $id ) )
{
$sql = "SELECT url FROM ".$tablepre."feed WHERE id={$id} AND uploader='{$SESSION['uid']}'";
代码很简单,获取GET来的id用intval函数判断,如果是整型则带入数据库查询,看似逻辑上没有什么问题,但实际上这段代码没有起到任何的check作用,为什么呢?看如下脚本:
<?
$var="20070601";
if (intval($var))
echo "it's safe";
echo '$var='.$var;
echo "<br>";
$var1="1 union select 1,1,1 from admin";
if (intval($var1))
echo "it's safe too";
echo '$var1='.$var1;
?>
运行以上脚本可以看到,两个判断的结果都是safe的,但实际上只有$var是安全的$var1变量后已经附带的有额外的SQL查询语句了,那么intval是如何判断的呢?这就需要看intval函数在PHP中是如何实现的,代码如下:
PHP_FUNCTION(intval)
{
zval **num, **arg_base;
int base;
switch (ZEND_NUM_ARGS()) {
case 1:
if (zend_get_parameters_ex(1, &num) == FAILURE) {
WRONG_PARAM_COUNT;
}
base = 10;
break;
case 2:
if (zend_get_parameters_ex(2, &num, &arg_base) == FAILURE) {
WRONG_PARAM_COUNT;
}
convert_to_long_ex(arg_base);
base = Z_LVAL_PP(arg_base);
break;
default:
WRONG_PARAM_COUNT;
}
RETVAL_ZVAL(*num, 1, 0);
convert_to_long_base(return_value, base);
}
intval函数只判断参数的第一个字符是否为整型,这样如果放在if中,只要满足变量第一个字符为整型,则返回值为ture,所以说用if来判断intval后的变量是不安全的。