8.1 钻GPC等转义的空子

GPC会自动把我们提交上去的单引号等敏感字符给转义掉,这样我们的攻击代码就没法执行了,GPC是PHP天生自带的功能,所以是我们最大的天敌。不过不要担心,GPC并不是把所有变量都进行了过滤,反而人们容易忽视而又用得多的$_SERVER变量没有被GPC过滤,包括编码转换的过程中,部分情况下我们也是可以干掉GPC的转义符号,是不是有点小激动?下面我们来仔细了解下。

8.1.1 不受GPC保护的$_SERVER变量

GPC上面我们已经介绍过,是用来过滤request中提交的数据,将特殊字符进行转义来防止攻击,在PHP5之后用$_SERVER取到的header字段不受GPC影响,所以当GPC开启的时候,它里面的特殊字符如单引号也不会被转义掉,另外一点是普通程序员很少会考虑到这些字段被修改。而在header注入里面最常见的是user-agent、referer以及client-ip/x-forward-for,因为大多的Web应用都会记录访问者的IP以及referer等信息。同样的$_FILES变量也一样不受GPC保护。

测试代码如下:


< php

echo 'GPC'.get_magic_quotes_gpc
();

echo '<br /> client-ip = '.$_SERVER["HTTP_CLIENT_IP"]


echo '<br />$_GET[a] = '.$_GET['a']

测试截图见图8-1。

图 8-1

8.1.2 编码转换问题

本书前面第4章介绍过宽字节注入,这就是一种非常典型的编码转换问题导致绕过GPC的方式。我们之前的举例说明,给一个查询页面ID参数请求/1.php?id=-1%df’and 1=1%23时,这时MySQL运行的SQL语句为:


select * from user where id= 1 運’ and 1=1#

这是由于单引号被自动转义成\’,前面的%df和转义字符\反斜杠(%5c)组合成了%df%5c,也就是“運”字,这时候单引号依然还在,于是成功闭合了前面的单引号。

这个例子讲的是PHP与MySQL交互过程中发生编码转换导致的问题,而其实只要发生编码转换就有可能出现这种问题,也就是说在PHP自带的编码转换函数上面也会存在这个问题,比如mb_convert_encoding()函数。

我们来证实一下,代码如下:


<meta http-equiv="Content-Type" content="text/html charset=utf-8"/>

<
php

$sql="where id='".urldecode
"-1%df%5c' -- " ."'"

print_r
mb_convert_encoding $sql "UTF-8" "GBK" ));? >

这里要注意的是,把网页和文件编码都设置成UTF-8,不然浏览器会自动转码,这段代码是把UTF-8编码转换成GBK,运行这段代码,输出如下:


where id='-1 ' -- '

可以看到也成功闭合了前面的单引号。

这种方式造成的SQL注入也有不少先例,比如ecshop就出过多次这个问题,我们来看看出现这个问题的核心代码,代码位置在includes/cls_iconv.php文件的chinese类中的Convert()函数:


function Convert $source_lang $target_lang $source_string = ''

{

/******
省略 ****/

    if
(( $this->iconv_enabled || $this->mbstring_enabled && !( $this-> config['source_lang'] == 'GBK' && $this->config['target_lang'] == 'BIG-5' ))

       {

           if
$this->config['target_lang'] = 'UNICODE'

           {

               $string = $this->_convert_iconv_mbstring
$this-> SourceText $this->config['target_lang'] $this-> config['source_lang'] );

               /*
如果正确转换 */

               if
$string

               {

                    return $string


               }

           }

           else

           {

                $string = ''


                $text = $SourceText


                while
$text

                {

                    if
ord substr $text 0 1 )) > 127

                    {

                        if
$this->config['source_lang'] = 'UTF-8'

                        {

                            $char = $this->_convert_iconv_mbstring
substr $text 0 2 ), 'UTF-8' $this->config ['source_lang'] );

                        }

                        else

这个函数的作用是将UTF-8的编码转换成GBK,本函数调用到$this->_convert_iconv_mbstring()函数,我们跟进去看看,代码如下:


function _convert_iconv_mbstring $string $target_lang $source_lang

{

    if
$this->iconv_enabled

    {

        $return_string = @iconv
$source_lang $target_lang $string );

        if
$return_string == false

        {

            return $return_string


        }

    }

    if
$this->mbstring_enabled

    {

        if
$source_lang == 'GBK'

        {

            $source_lang = 'CP936'


        }

        if
$target_lang == 'GBK'

        {

            $target_lang = 'CP936'


        }

        $return_string = @mb_convert_encoding
$string $target_lang $source_lang );

        if
$return_string == false

可以看到最终调用iconv()函数或者mb_convert_encoding()函数来进行转码,如果调用这个函数之后没有再次过滤,则会存在注入问题。