• PHP代码审计基础-初级篇


    对于php代码审计我也是从0开始学的,对学习过程进行整理输出沉淀如有不足欢迎提出共勉。对学习能力有较高要求,整个系列主要是在工作中快速精通php代码审计,整个学习周期5天 ,建议花一天时间熟悉php语法。

    我是直接phpstudy2019一键搭建小白首选

    所谓工欲善其事必先利其器,找一款适合自己的php开发工具自行百度搭建,我用的是phpstrom .

    初级篇主要讲

    1. php历史版本漏洞。

    2. 变量配置缺陷。

    3. 函数缺陷。

    4. 弱语言缺陷。

    PHP配置漏洞

    我们要熟知php各版本漏洞

    Register_globals  全局变量注册开关

    在该选项为on情况下,会直接把用户GET,POST等方式提交的参数注册成全局变量并初始化值为参数对应的值

    Php小于4.2.3默认打开 5.3废弃 5.4移除

    Allow_url_include  是否允许包含远程文件

    Php 5.2 后默认设置off

    Magic_quotes_gpc 魔术引号自动过滤

    会自动在get post,cookie 变量对单引号,双引号,反斜杠,空字符的前面加反斜杠

    缺陷不会过滤$_SERVER 变量 

    Php5.3后废弃,5.4移除 小于4.2.3 默认打开

    Magic_quotes_runtime  魔术引号自动过滤

    和上面一样 却是对从数据库取出的数据进行转义  防止二次注入 同样在5.4取消

    Safe_mode  安全模式

    在开启时做命令执行操作会提示执行命令失败,和一些敏感操作. 可以查看配置是否开启。

    Open_basedir  可访问目录

    默认web目录,查看配置是否出现全目录可访问情况

    Disable_functions 禁用函数

    禁用敏感函数如exec  可以通过dl()函数来绕过自定义php扩展方法实现命令执行,这个函数并不安全。

    Display_errors  错误显示

    生产环境关闭错误回显 display_errors=off

    PHP漏洞函数

     变量覆盖

          extract()

    该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。条件:若有EXTR_SKIP则不行。

    <?php
    $a = "Original";
    $my_array = array("a" => "Cat", "b" => "Dog", "c" => "Horse");
    extract($my_array);
    echo "$a = $a; $b = $b; $c = $c";
    ?>
     
    # 结果:$a = Cat; $b = Dog; $c = Horse

          parse_str()

    解析字符串并注册成变量

    $b=1;
    Parse_str('b=2');
    Print_r($b);
    # 结果: $b=2

          import_request_variables()

    将 GET/POST/Cookie 变量导入到全局作用域中,全局变量注册。在5.4之后被取消,只可在4-4.1.0和5-5.4.0可用。

    //导入POST提交的变量值,前缀为post_
    import_request_variable("p", "post_");
    //导入GET和POST提交的变量值,前缀为gp_,GET优先于POST
    import_request_variable("gp", "gp_");
    //导入Cookie和GET的变量值,Cookie变量值优先于GET
    import_request_variable("cg", "cg_");

        $$变量覆盖

    ## 提交参数chs,则可覆盖变量"$chs"的值。$key为chs时,$$key就变成$chs

    <?
    $chs = '';
    if($_POST && $charset != 'utf-8'){
        $chs = new Chinese('UTF-8', $charset);
        foreach($_POST as $key => $value){
            $$key = $chs->Convert($value);
        }
        unset($chs);
    }

    绕过过滤的空白字符

    " "%00" (ASCII  0 (0x00)),空字节符。
    制表符
    "	" (ASCII  9 (0x09)),水平制表符。
    空白字符:
    "
    " (ASCII 10 (0x0A)),换行符。
    "v" "x0b" (ASCII  11 (0x0B)),垂直制表符。
    "f" "%0c" 换页符
    "
    " "%0d"(ASCII  13 (0x0D)),回车符。
    空格:
    " " "%20" (ASCII  32 (0x20)),普通空格符。

    Trim 函数过滤 缺少了f(%0c)

    从而绕过%0c过滤

    截断

       

         Iconv 异常字符截断问题

    ## 因iconv遇到异常字符就不转后面的内容了,所以可以截断。
    ## 这里chr(128)到chr(255)都可以截断。
    $a='1'.char(130).'2';
    echo iconv("UTF-8","gbk",$a); //将字符串的编码从UTF-8转到gbk
    echo iconv('GB2312', 'UTF-8', $str); //将字符串的编码从GB2312转到UTF-8

      eregi、ereg可用%00截断

    功能:正则匹配过滤 条件:要求php<5.3.4可以把非法的数据放在%00后面进行绕过我们可以看一个ctf题就是考察了这个知识

    ## http://127.0.0.1/Php_Bug/05.php?password=1e9%00*-*
    #GET方式提交password,然后用ereg()正则限制了password的形式,只能是一个或者多个数字、大小写字母,继续strlen()限制了长度小于8并且大小必须大于9999999,继续strpos()对password进行匹配,必须含有-,最终才输出flag
    #因为ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配。
    #对于另一个难题可以使用科学计数法表示,计算器或电脑表达10的的幂是一般是e,也就是1.99714e13=19971400000000,所以构造 1e8 即 100000000 > 9999999,在加上-。于是乎构造password=1e8%00*-*,成功得到答案

    <?php
    if (isset ($_GET['password'])) {
        if (ereg ("^[a-zA-Z0-9]+$",$_GET['password']) === FALSE)
        {
            echo '<p>You password must be alphanumeric</p>';
        }
        else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
        {
            if (strpos ($_GET['password'], '*-*') !== FALSE)
            {
                die('Flag: ' . $flag);
            }
            else
            {
                echo('<p>*-* have not been found</p>');
            }
        }
        else
        {
            echo '<p>Invalid password</p>';
        }
    }

         move_uploaded_file 用截断

    5.4.x<= 5.4.39, 5.5.x<= 5.5.23, 5.6.x <= 5.6.7 原来在高版本(受影响版本中),PHP把长度比较的安全检查逻辑给去掉了,导致了漏洞的发生

    获取文件名不一致%00   导致截断后面的文件名

         inclue用?截断

    <?php $name=$_GET['name']; $filename=$name.'.php'; include $filename; ?>

    当输入的文件名包含URL时,问号截断则会发生,并且这个利用方式不受PHP版本限制,原因是Web服务其会将问号看成一个请求参数。 测试POC: http://127.0.0.1/test/t1.php?name=http://127.0.0.1/test/secret.txt? 则会打开secret.txt中的文件内容。本测试用例在PHP5.5.38版本上测试通过。

         系统长度截断

    这种方式在PHP5.3以后的版本中都已经得到了修复。 win260个字符,linux下4*1024=4096字节

       mysql长度截断

    mysql内的默认字符长度为255,超过的就没了。 由于mysql的sql_mode设置为default的时候,即没有开启STRICT_ALL_TABLES选项时,MySQL对于插入超长的值只会提示warning

         mysql中utf-8截断

    insert into dvwa.test values (14,concat("admin",0xc1,"abc"))写入为admin  

    弱类型比较

    以下等式会成立

    '' == 0 == false
     
    '123' == 123
     
    'abc' == 0
     
    '123a' == 123
     
    '0x01' == 1
     
    '0e123456789' == '0e987654321'
     
    [false] == [0] == [NULL] == ['']
     
    NULL == false == 0
     
    true == 1

        ==、>、<的弱类型比较

    这里用到了PHP弱类型的一个特性,当一个整形和一个其他类型行比较的时候,会先把其他类型转换成整型再比。

    ##方法1
     
    ##$a["a1"]="1e8%00";
     
    ##这里用%00绕过is_numeric,然后1e8可以比1336大,因此最后能$v1=1
     
    ##方法2
     
    ##$a["a1"]=["a"];
     
    ##使用数组,可以,因为数组恒大于数字或字符串
     
    ##方法3
     
    ##$a["a1"]=1337a;
     
    ##1337a过is_numeric,又由>转成1337与1336比较
     
    <?php
     
    is_numeric(@$a["a1"])?die("nope"):NULL;   
     
    if(@$a["a1"]){
     
        var_dump($a);
     
        ($a["a1"]>1336)?$v1=1:NULL;
     
    }
     
    var_dump($v1);

          switch 弱类型

    // 第一种:弱类型,1e==1
    // $x1=1e
    // 第二种:利用数组名字bypass
    // $x1=1[]
    // 传入后为string(3) "1[]",但在switch那里为1
    if (isset($_GET['x1']))
    {
        $x1 = $_GET['x1'];
        $x1=="1"?die("ha?"):NULL;
        switch ($x1)
        {
            case 0:
            case 1:
                $a=1;
                break;
        }
    }

          md5比较(0e相等、数组为Null)

    md5('240610708') //0e462097431906509019562988736854
     
    md5('QNKCDZO') //0e830400451993494058024219903391
     
    0e 纯数字这种格式的字符串在判断相等的时候会被认为是科学计数法的数字,先做字符串到数字的转换。
     
    md5('240610708')==md5('QNKCDZO'); //True
     
    md5('240610708')===md5('QNKCDZO'); //False
     
     
     
    这样的对应数值还有:
     
    var_dump(md5('240610708') == md5('QNKCDZO'));
     
    var_dump(md5('aabg7XSs') == md5('aabC9RqS'));
     
    var_dump(sha1('aaroZmOk') == sha1('aaK1STfY'));
     
    var_dump(sha1('aaO8zKZF') == sha1('aa3OFF9m'));
     
    var_dump('0010e2' == '1e3');
     
    var_dump('0x1234Ab' == '1193131');
     
    var_dump('0xABCdef' == ' 0xABCdef');

    可以用于登录判断

    240610708

    技巧:找出在某一位置开始是0e的,并包含“XXX”的字符串

    Md5 加密数组都是空 可以利用绕过   

        json传数据{“key”:0}

    PHP将POST的数据全部保存为字符串形式,也就没有办法注入数字类型的数据了而JSON则不一样,JSON本身是一个完整的字符串,经过解析之后可能有字符串,数字,布尔等多种类型。

    第一个application/x-www-form-urlencoded,是一般表单形式提交的content-type第二个,是包含文件的表单。第三,四个,分别是json和xml,一般是js当中上传的.

    {“key”:”0″}

    这是一个字符串0,我们需要让他为数字类型,用burp拦截,把两个双引号去掉,变成这样:

    {“key”:0}

         十六进制与十进制比较

    == 两边的十六进制与十进制比较,是可以相等的。

    至此已经介绍完了php常规函数缺陷,版本漏洞,语言特性导致的漏洞。需要熟记这些缺陷的利用方法。

  • 相关阅读:
    好文!悟透JavaScript
    关于“四舍六入五成双/四舍六入五留双/四舍六入五单双”等口诀的实例研究
    第四个:怎么应付在背地里说你坏话的人
    为什么日全食时开始下雨,10分钟后雨又停了。初中物理知识。
    吃萝卜去口气(口臭)
    今天12点34分56秒是一个神奇的时刻
    sharepoint2010人性化的地方员工离职AD账号禁用(个人网站自动提醒上级经理功能)
    指定为添加到网站中的项目显示“新”图标的天数。
    解决sharepoint2010的多行文本框的插入图片—【从sharepoint】的disabled问题
    sharepoint2010的IE6不支持的解决方法
  • 原文地址:https://www.cnblogs.com/feizianquan/p/11637318.html
Copyright © 2020-2023  润新知