• PHP代码审计01之in_array()函数缺陷


    前言

    从今天起,结合红日安全写的文章,开始学习代码审计,题目均来自PHP SECURITY CALENDAR 2017,讲完这个题目,会再用一道有相同问题的CTF题来进行巩固。下面开始分析。

    漏洞分析

    下面我们看第一题,代码如下:

    <?php
    class Challenge{
        const UPLOAD_DIRECTORY = './solutions/';
        private $file;
        private $whitelist;
        public function __construct($file)
        {
            $this->file = $file;
            $this->whitelist=range(1,24);
        }
        public function __destruct()
        {
            // TODO: Implement __destruct() method.
                //这里要特别注意!!!
            if (in_array($this->file['name'],$this->whitelist)){
                move_uploaded_file(
                    $this->file['tmp_name'],
                    self::UPLOAD_DIRECTORY.$this->file['name']
                );
            }
        }
    }
    $challenge=new Challenge($_FILES['solution']);
    ?>
    

    这一关考察的是任意文件上传漏洞,导致这个漏洞发生的是上方代码中,对in_array()函数使用不规范导致的。这里详细说一下in_array()函数的用法。先看一下PHP手册对这个函数的解释,是检查数组中存在某个值,重点是我圈起来的,如果没有设置第三个参数,那么就使用宽松的检查,问题就出现在这里。

    现在看上方代码第12行,这里使用了in_array()函数来检查文件名,但是没有设置第三个参数!,只会进行弱类型比较,不会检查数据类型。比如上面白名单规定,只能上传1~24的文件名,我们上传3shell.php,因为3在白名单中,所以它会将3shell转换成3从而绕过了白名单,达到了任意文件上传的目的。
    为了加深对in_array()的理解,这里写一段简单的代码。

    <?php
    $id =3 and 1=1;
    $whitelist = range(1, 5);
    if (!in_array($id, $whitelist)) {
        echo "你想搞事";
    } else {
        echo "你通过了";
    }
    ?>
    

    这里in_array()也是没有设置第三个参数,会进行弱类型比较,会将3 and 1=1转化为3从而绕过了白名单,输出你通过了。当我设置第三个参数为true时,此时会进行强类型检查。所以我们将上文第三行代码修改为:if (!in_array($id, $whitelist,true)),再执行就会输出:“你想搞事”。

    现在是不是对in_array()函数有了一个大概的了解呢?那让我们做一道同类型CTF题目来加深巩固一下。

    CTF练习

    这道题目也是in_array()函数没有设置第三个参数,导致白名单被绕过,然后被SQL注入。下面我们具体看一下相关代码。
    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的相关代码。
    config.php

    <?php
    $servername = "localhost";
    $username = "root";
    $password = "XFAICL1314";
    $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;
    }
    ?>
    

    然后是搭建CTF使用的sql语句。

    create database day1;
    use day1;
    create table users (
    id int(6) unsigned auto_increment primary key,
    name varchar(20) not null,
    email varchar(30) not null,
    salary int(8) unsigned not null );
    ​
    INSERT INTO users VALUES(1,'Lucia','Lucia@hongri.com',3000);
    INSERT INTO users VALUES(2,'Danny','Danny@hongri.com',4500);
    INSERT INTO users VALUES(3,'Alina','Alina@hongri.com',2700);
    INSERT INTO users VALUES(4,'Jameson','Jameson@hongri.com',10000);
    INSERT INTO users VALUES(5,'Allie','Allie@hongri.com',6000);
    ​
    create table flag(flag varchar(30) not null);
    INSERT INTO flag VALUES('HRCTF{1n0rrY_i3_Vu1n3rab13}');
    

    题目解析

    用上面代码我们在本地将环境搭建好。然后开始分析,先看index.php文件代码。再第16行通过$_GET方法接收用户的输入,并用stop_hack()来过滤用户的输入,然后下方直接拼接到sql语句中进行查询。然后再向下看,这里这里用in_array()来进行一个简单的检查,我们发现它没有设置第三个参数,进行弱类型检查。现在我们来验证一下,眼见为实。根据我们上方白名单规则,我们现在id只能输入1~5。现在我们输入3来看一下,发现查询到了信息。

    现在我们输入8,它不在白名单中,看看返回什么。

    上面是正常的输入,所以白名单是有效的,下面我们构造payload,比如我们输入:1',发现程序报错,绕过了白名单的检查。可以直接报错注入。

    而关于报错注入的函数,大约有四个,分别是:floor()、extractvalue()、updatexml()、exp()
    虽然绕过了白名单,但是还有过滤函数stop_hack()现在我们定位到这个函数看看:

    发现过滤了一些危险函数,我们查看后发现,这里没有过滤updatexml()函数,可以用它,但是concat函数被过滤了,我们需要找到可以替换得函数了。这里我们使用make_set()函数,它的用法是make_set()函数是先将x转化成二进制,例如: 11的二进制为1011,将二进制顺序颠倒变成1101,每一位数再与后面的字符串相对应,为1的截取,为0的丢弃。如下图:

    所以我们构造payload,获取数据,为了避免占用篇幅这里直接是获取到flag的payload。

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

    小结

    通过这篇文章的讲解,是不是对in_array()理解更深了一些呢?下一篇文章会对filter_var函数缺陷导致的漏洞进行学习和分析,一起努力吧!
    我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1pxmil6lphjna

  • 相关阅读:
    kali环境下如何安装最新版cobalt strike
    win10结束进程时拒绝访问的处理办法
    Windows下Postgresql数据库的下载与配置方法
    ubuntu下安装及配置git的方法
    运行和控制Nginx
    nginx安装(详解)
    ubuntu安装nginx
    在Windows安装运行Kafka
    linux下安装使用虚拟环境
    《操作系统真象还原》线程
  • 原文地址:https://www.cnblogs.com/lxfweb/p/13729406.html
Copyright © 2020-2023  润新知