PHP变量覆盖总结

   · ☕ 3 分钟
🏷️
  • #ctf
  • #php
  • 最近做ctf遇到了很多次变量覆盖的问题,总结一下

    register_global

    全局变量注册,本特性已自 PHP 5.3.0 起废弃并将自 PHP 5.4.0 起移除

    当在php.ini开启register_globals= On时,代码中的参数会被用户提交的参数覆盖掉。

    看例子

    1
    2
    3
    4
    5
    6
    7
    
    <?php
    echo "Register_globals: " . (int)ini_get("register_globals") . "<br/>";
    if ($auth) {
        echo "覆盖!";
    }else{
        echo "没有覆盖";
    }
    

    当访问http://127.0.0.1/1.php时输出没有覆盖

    但是当请求http://127.0.0.1/1.php?auth=1时会覆盖掉$auth输出覆盖

    extract()

    从数组中将变量导入到当前的符号表
    直接看代码

    1
    2
    3
    4
    5
    6
    7
    
    <?php
    $auth=false;
    extract($_GET);
    
    if ($auth){
        echo "over";
    }
    

    同样请求http://127.0.0.1/1.php?auth=1时会覆盖掉$auth输出over

    $$

    $$符号在php中叫做可变变量,可以使变量名动态设置。举个例子

    1
    2
    3
    4
    5
    6
    
    <?php
    $a='hello';
    $$a='world';
    echo "$a ${$a}";
    echo "$a $hello";
    ?>
    

    可以看到在这里${$a}等同于$hello,接着我们再来看怎么来进行变量覆盖

    1
    2
    3
    4
    5
    
    $auth=0;
    foreach ($_GET as $key => $value) {
        $$key=$value;
    }
    echo $auth;
    

    在第二行中遍历了全局变量$_GET,第三行将key当作变量名,把value赋值。
    那么我们传入http://127.0.0.1/1.php?auth=1时会将$auth的值覆盖为1

    import_request_variables

    将 GET/POST/Cookie 变量导入到全局作用域中,如果你禁止了 register_globals,但又想用到一些全局变量,那么此函数就很有用。那么和register_globals存在相同的变量覆盖问题。

    1
    2
    3
    4
    5
    6
    
    $auth = '0';
    import_request_variables('G');
     
    if($auth == 1){
      echo "over!";
    }
    

    同样传入http://127.0.0.1/1.php?auth=1时会将$auth的值覆盖为1,输出over!

    parse_str()

    将字符串解析成多个变量

    1
    2
    3
    4
    
    $a='aa';
    $str = "a=test";
    parse_str($str);
    echo ${a};
    

    可以看出来将$str解析为$a='test',与parse_str()类似的函数还有mb_parse_str(),不在赘述。

    我们来看一道ctf题,在我上一篇文章ISCC的web4中也提到了.

    源代码

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    <?php 
    error_reporting(0); 
    include("flag.php"); 
    $hashed_key = 'ddbafb4eb89e218701472d3f6c087fdf7119dfdd560f9d1fcbe7482b0feea05a'; 
    $parsed = parse_url($_SERVER['REQUEST_URI']); 
    if(isset($parsed["query"])){ 
        $query = $parsed["query"]; 
        $parsed_query = parse_str($query); 
        if($parsed_query!=NULL){ 
            $action = $parsed_query['action']; 
        } 
    
        if($action==="auth"){ 
            $key = $_GET["key"]; 
            $hashed_input = hash('sha256', $key); 
            if($hashed_input!==$hashed_key){ 
                die("no"); 
            } 
    
            echo $flag; 
        } 
    }else{ 
        show_source(__FILE__); 
    }?>
    

    在第五行将请求的URI通过parse_url()解析后赋值给$parsed变量。

    在第八行将我们提交的query参数使用parse_str解析,这时就产生了变量覆盖的问题,我们可以通过query提交参数去覆盖变量。

    接着往下看$hashed_input!==$hashed_key成立输出flag,此时$hashed_input我们可控,那么我们可以覆盖掉$hashed_key来使条件成立

    $hashed_input = hash('sha256', $key);是sha256加密,那么我们可以传入key=a,此时$hashed_input等于ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb

    然后覆盖$hashed_key,传入query=&hashed_key=ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb覆盖掉。

    这时payload为

    1
    
    http://39.100.83.188:8066/?query=&hashed_key=ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb&action=auth&key=a
    

    拿到flag

    总结

    代码审计中变量覆盖还算好找,希望自己细心,也希望这篇文章对大家有用处。

    您的鼓励是我最大的动力
    alipay QR Code
    wechat QR Code

    目录