zoukankan      html  css  js  c++  java
  • PHP代码审计分段讲解(1)

    PHP源码来自:https://github.com/bowu678/php_bugs

    快乐的暑期学习生活+1

    01 extract变量覆盖

    <?php
    
    $flag='xxx'; 
    extract($_GET);
     if(isset($shiyan))
     { 
        $content=trim(file_get_contents($flag));
        if($shiyan==$content)
        { 
            echo'ctf{xxx}'; 
        }
       else
       { 
        echo'Oh.no';
       } 
       }
    
    ?>
     

    变量覆盖漏洞:自定义的参数值替换原有变量值的情况

    extract()函数介绍:https://www.runoob.com/php/func-array-extract.html

    源代码使用extract($_GET)接收了GET请求传输进来的数据,并且将键名和键值转换为变量名和变量的值,可以看到有$flag变量是在extract之前就定义了的,file_get_contents() 函数把整个文件读入一个字符串中。正常的代码流程读取$flag文件并且去除两边的空格,将结果存储在$content里面,然后使用我们传入的$shiyan变量与$content进行比较,如果相同的话就输出flag,否则输出 Oh no。

    如果$flag文件存在的话,因为是题目服务器上的文件,我们没有权限知道里面的内容,在其读取到$content变量里面的时候,更无法与其进行比较,进而获取flag

    突破点在于:extract()函数在flag变量值的下面,所以我们可以以GET方式对$flag重新赋值,进而读取一个不可能存在的文件,使$content为空,传入$shiyan变量也为空,即可获取flag

    如图:

    extract变量覆盖.png

     

    02 绕过过滤的空白字符

    <?php
     
    $info = ""; 
    $req = [];
    $flag="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
     
    ini_set("display_error", false); //为一个配置选项设置值
    error_reporting(0); //关闭所有PHP错误报告
     
    if(!isset($_GET['number'])){
       header("hint:26966dc52e85af40f59b4fe73d8c323a.txt"); //HTTP头显示hint 26966dc52e85af40f59b4fe73d8c323a.txt
     
       die("have a fun!!"); //die — 等同于 exit()
     
    }
     
    foreach([$_GET, $_POST] as $global_var) {  //foreach 语法结构提供了遍历数组的简单方式 
        foreach($global_var as $key => $value) { 
            $value = trim($value);  //trim — 去除字符串首尾处的空白字符(或者其他字符)
            is_string($value) && $req[$key] = addslashes($value); // is_string — 检测变量是否是字符串,addslashes — 使用反斜线引用字符串
        } 
    } 
     
     
    function is_palindrome_number($number) { 
        $number = strval($number); //strval — 获取变量的字符串值
        $i = 0; 
        $j = strlen($number) - 1; //strlen — 获取字符串长度
        while($i < $j) { 
            if($number[$i] !== $number[$j]) { 
                return false; 
            } 
            $i++; 
            $j--; 
        } 
        return true; 
    } 
     
     
    if(is_numeric($_REQUEST['number'])) //is_numeric — 检测变量是否为数字或数字字符串 
    {
     
       $info="sorry, you cann't input a number!";
     
    }
    elseif($req['number']!=strval(intval($req['number']))) //intval — 获取变量的整数值
    {
     
         $info = "number must be equal to it's integer!! ";  
     
    }
    else
    {
     
         $value1 = intval($req["number"]);
         $value2 = intval(strrev($req["number"]));  
     
         if($value1!=$value2){
              $info="no, this is not a palindrome number!";
         }
         else
         {
     
              if(is_palindrome_number($req["number"])){
                  $info = "nice! {$value1} is a palindrome number!"; 
              }
              else
              {
                 $info=$flag;
              }
         }
     
    }
     
    echo $info;
     

    代码比较长,但是不慌,从上往下看,用到函数的时候再到函数里再去看构造

    if(!isset($_GET['number'])){
       header("hint:26966dc52e85af40f59b4fe73d8c323a.txt"); //HTTP头显示hint 26966dc52e85af40f59b4fe73d8c323a.txt
     
       die("have a fun!!"); //die — 等同于 exit()
     
    }
     
    foreach([$_GET, $_POST] as $global_var) {  //foreach 语法结构提供了遍历数组的简单方式 
        foreach($global_var as $key => $value) { 
            $value = trim($value);  //trim — 去除字符串首尾处的空白字符(或者其他字符)
            is_string($value) && $req[$key] = addslashes($value); // is_string — 检测变量是否是字符串,addslashes — 使用反斜线引用字符串
        } 
    } 
     

    在这里使用GET方式传入number参数的值,同时也可以通过GET或者POST的方式传入其他参数的值

    if(is_numeric($_REQUEST['number'])) //is_numeric — 检测变量是否为数字或数字字符串 
    {
     
       $info="sorry, you cann't input a number!";
     
    }
     

    使用is_numeric函数检测传入的$number是否为数字,不能传入数字

    elseif($req['number']!=strval(intval($req['number']))) //intval — 获取变量的整数值
    {
     
         $info = "number must be equal to it's integer!! ";  
     
    }
     

    intval函数获取变量的整数值

    strval — 获取变量的字符串值

    这一步需要传入的是整数,也就与第一个if条件相矛盾

    这两部分都可以通过在number参数前加%00进行绕过

    else
    {
     
         $value1 = intval($req["number"]);
         $value2 = intval(strrev($req["number"]));  
     
         if($value1!=$value2){
              $info="no, this is not a palindrome number!";
         }
         else
         {
     
              if(is_palindrome_number($req["number"])){
                  $info = "nice! {$value1} is a palindrome number!"; 
              }
              else
              {
                 $info=$flag;
              }
         }
     
    }
     
    echo $info;
     

    这一部分第一步需要$value1和$value2相等,strrev函数令number取反,所以$number需要是一个回文数字,如12321,第二步需要绕过is_palindrome_number函数,使其返回值为false,其函数为:

    function is_palindrome_number($number) { 
        $number = strval($number); //strval — 获取变量的字符串值
        $i = 0; 
        $j = strlen($number) - 1; //strlen — 获取字符串长度
        while($i < $j) { 
            if($number[$i] !== $number[$j]) { 
                return false; 
            } 
            $i++; 
            $j--; 
        } 
        return true; 
    } 
     

    该函数将$number数字转换为字符串,然后进行首尾依次比较,首尾相同则返回true,否则返回false。

    这里看上去是矛盾的,既要令$number是回文,又要令$number不是回文。

    分析可知,在绕过else里面的限制时,我们需要的是一个能逃逸出intval和is_numeric函数,却逃逸不出is_palindrome_number函数的字符

    在P师傅的博客中,是通过查看PHP底层源代码进行分析的:

    https://www.leavesongs.com/PENETRATION/some-sangebaimao-ctf-writeups.html

    发现了f字符能够进行实现上述的逃逸,看源代码当然很好,但是这里我们采用FUZZ的方法,python3脚本

    import requests
    
    for i in range(256):
        url = "http://127.0.0.1/php_bugs-master/02.php?number=%00%{:02X}12321".format(i)
        rep=requests.get(url)
        if "x" in rep.text:
            print(url)
     

    运行之后结果为:

    绕过空白的过滤符.png

    所以能够逃逸出来的字符为%0C 和 %2B,使用以上payload读取flag

    好像这道题是SCTF2016的一道代码审计题目,挺不错的 :D

  • 相关阅读:
    IIS应用程序池自动化回收脚本
    gitlab修改克隆地址
    docker安装的gitalb备份及数据迁移
    docker安装gitlab
    docker安装的gitlab备份脚本
    sql server 2016 维护计划执行,提示执行失败。有关详细信息,请参阅维护计划和sql server 代理作业历史记录日志。
    SSH使用证书登录
    docker搭建 rabbitmq集群
    VMware Workstation Pro设置nat模式上网
    docker-compose搭建discuz论坛
  • 原文地址:https://www.cnblogs.com/Cl0ud/p/13228066.html
Copyright © 2011-2022 走看看