关注我们

php代码审计之变量覆盖

adminadmin 代码审计 2018-05-30 536 0

 变量覆盖漏洞是指可以用我们自定义的参数值替换程序原有的变量值,从而产生漏洞。大多都是由于函数使用不当导致,经常导致变量覆盖漏洞的函数有:extract()函数、parse_str()函数、import_request_variables()函数,另外有些利用$$的方式注册变量没验证已有变量导致覆盖也是经常出现的。下面就对这四种情况做简要分析:

    (1)extract()函数:

    extract()函数从数组中将变量导入到当前的符号表,也就是将数组中的键值对注册成变量。其最多有三个参数,语法为:

  1. extract(array,extract_rules,prefix)  

参数描述
array必需。规定要使用的数组。
extract_rules

可选。extract() 函数将检查每个键名是否为合法的变量名,同时也检查和符号表中已存在的变量名是否冲突。对不合法和冲突的键名的处理将根据此参数决定。

可能的值:

  • EXTR_OVERWRITE - 默认。如果有冲突,则覆盖已有的变量。

  • EXTR_SKIP - 如果有冲突,不覆盖已有的变量。

  • EXTR_PREFIX_SAME - 如果有冲突,在变量名前加上前缀 prefix

  • EXTR_PREFIX_ALL - 给所有变量名加上前缀 prefix

  • EXTR_PREFIX_INVALID - 仅在不合法或数字变量名前加上前缀 prefix

  • EXTR_IF_EXISTS - 仅在当前符号表中已有同名变量时,覆盖它们的值。其它的都不处理。

  • EXTR_PREFIX_IF_EXISTS - 仅在当前符号表中已有同名变量时,建立附加了前缀的变量名,其它的都不处理。

  • EXTR_REFS - 将变量作为引用提取。导入的变量仍然引用了数组参数的值。

prefix

可选。请注意 prefix 仅在 extract_type 的值是 EXTR_PREFIX_SAME,EXTR_PREFIX_ALL,EXTR_PREFIX_INVALID 或 EXTR_PREFIX_IF_EXISTS 时需要。如果附加了前缀后的结果不是合法的变量名,将不会导入到符号表中。

前缀和数组键名之间会自动加上一个下划线。

可以看出,第一个参数是必须的,是否导致变量覆盖漏洞由第二个参数决定。有如下三种情况会产生漏洞:

一:只传入一个参数时,默认为EXTR_OVERWRITE模式,表示如果有冲突,则覆盖已有的变量; 

二:第二个参数为EXTR_OVERWRITE时;

三:第二个参数为EXTR_IF_EXISTS时,表示仅在当前符号表中已有同名变量时,覆盖它们的值,其它的都不处理。

测试代码如下:

  1. <?php  
    $a=1;  
    $b=array('a'=>'2');  
    extract($b);  
    print_r($a);  
    ?>

测试结果如下:

php代码审计之变量覆盖


原本变量$a的值为1,经过extract()函数并且只传入一个参数后,$a的值被成功覆盖成了2。


    (2)parse_str()函数:

    parse_str()函数把查询字符串解析到变量中,并且不会验证当前变量是否已经存在。parse_str()函数语法为:


  1. parse_str
    (string,
    array
    )

第一个参数是必需的,代表 要解析注册成变量的字符串。第二个参数是一个数组,当其存在时,注册的变量会放到这个数组里,但是如果数组原来就存在相同的键,则会覆盖掉原来的键值。

测试代码如下:


  1. <?php  
    $a=1;  
    parse_str('a=2');  
    print_r($a);  
    ?>

测试结果如下:

php代码审计之变量覆盖



变量$a的值经过parse_str()函数从原来的1被覆盖成了2。


    (3)import_request_variables()函数:

    import_request_variables()函数把GET、POST、Cookie变量导入到全局作用域中,用在register_globals被禁止时,版本需要PHP 4 >= 4.1.0和 PHP 5 < 5.4.0。import_request_variables()函数语法为:

  1. bool import_request_variables ( string $types [, string $prefix ] )
  2. types 参数指定需要导入的变量。可以用字母‘G’、‘P’和‘C’分别表示 GET、POST 和 Cookie。这些字母不区分大小写,所以你可以使用‘g’、‘p’和‘c’的任何组合。prefix 参数作为变量名的前缀,置于所有被导入到全局作用域的变量之前。

  3. 测试代码如下:

  4. <?php  
    $a=1;  
    import_request_variables('GP');  
    print_r($a);  
    ?>

    测试结果如下:

  5. php代码审计之变量覆盖


变量$a的值被覆盖成了2。


    (4)$$变量覆盖:

    相信大家都见过这段代码:

  1. foreach(array('_COOKIE', '_POST', '_GET') as $_request){  
        foreach($$_request as $_key => $_value){  
            $$_key = addslashes($_value);  
        }  
    }

当$_key为COOKIE、POST、GET中的参数,比如提交?a=1,则$key的值为a,而还有一个$在a的前面,结合起来则是$a=addslashes($_value);所以会覆盖已有的变量$a的值。测试代码如下:


  1. <?php  
    $a=1;  
    foreach(array('_COOKIE', '_POST', '_GET') as $_request){  
        foreach($$_request as $_key => $_value) {  
            echo $_key.'<br/>';  
            $$_key = addslashes($_value);  
        }  
    }  
    echo $a;  
    ?>

测试结果如下:

php代码审计之变量覆盖

成功把变量$a的值覆盖成了2。


(5)全局变量覆盖
当register_global=ON时,变量来源可能是各个不同的地方,比如页面的表单,Cookie等。

  1. <?php  
    echo "Register_globals: ".(int)ini_get("register_globals")."<br/>";  
      
    if ($auth){  
       echo "private!";  
    }  
    ?>

当register_globals=OFF时,这段代码不会出问题。
但是当register_globals=ON时,提交请求URL:http://www.a.com/test.php?auth=1,变量$auth将自动得到赋值。得到的结果为
Register_globals:1

private!

小记:如果上面的代码中,已经对变量$auth赋了初始值,比如$auth=0,那么即使在URL中有/test.php?auth=1,也不会将变量覆盖,也就是说不会打印出private!

通过$GLOBALS获取的变量,也可能导致变量覆盖。

  1. <?php  
    echo "Register_globals:".(int)ini_get("register_globals")."<br/>";  
    if (ini_get('register_globals')) foreach($_REQUEST as $k=>$v) unset(${$k});  
    print $a;  
    print $_GET[b];  
    ?>

变量$a未初始化,在register_globals=ON时,再尝试控制“$a”的值(http://www.a.com/test1.php?a=1&b=2),会因为这段代码而出错。

而当尝试注入“GLOBALS[a]”以覆盖全局变量时(http://www.a.com/test1.php?GLOBALS[a]=1&b=2),则可以成功控制变量“$a”的值。这是因为unset()默认只会销毁局部变量,要销毁全局变量必须使用$GLOBALS。

而在register_globals=OFF时,则无法覆盖到全局变量。

小记:register_globals的意思是注册为全局变量,所以当On的时候,传递过来的值会被直接注册为全局变量而直接使用,当为OFF的时候,就需要到特定的数组中去得到它。unset用于释放给定的变量a


    以上就是导致变量覆盖漏洞的五种情况。变量覆盖漏洞最常见漏洞点是在左边梁注册时没有验证变量是否存在,以及在赋值给变量的时候,所以尽量使用原始的变量数组,如$_GET、$_POST,或者在注册变量前一定要验证变量是否存在。


喜欢3评论已闭