zoukankan      html  css  js  c++  java
  • PHP-Audit-Labs-Day1

    函数缺陷原理分析

    先看一段简单的源代码

    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()
        {
            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($this->file['name'], $this->whitelist)
    

    再看in_array函数的使用:

    in_array :(PHP 4, PHP 5, PHP 7)
    功能 :检查数组中是否存在某个值
    定义 : bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )
            在 $haystack 中搜索 $needle ,如果第三个参数 $strict 的值为 TRUE ,
            则 in_array() 函数会进行强检查,
            检查 $needle 的类型是否和 $haystack 中的相同。如果找到 $haystack ,则返回 TRUE,否则返回 FALSE。
    

    这个函数会在第二个参数中搜索第一个参数。关键点在于第三个参数$strict。这个参数只有True和False。默认的是False,即不开启PHP的强检查,也就是说我们可以成功的上传一个7shell.php文件,因为7shell.php是字符串,在与数字比较前会被类型转换成7,7在白名单的范围之内,成功上传。这个点导致了任意文件上传功能。

    修复方法

    1、开启第三个参数为True

    一道由in_array造成sql报错注入的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;
    }
    ?>
    
    # 搭建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}');
    

    一开始提示我们加一个id参数。

    id=1 and 1=1有回显,id=1 and 1=2异常。存在数字型注入。id=5有回显,id=6报错,一共存在5列。接着暴数据,但是从代码里面看到存在过滤字符。updatexml没有被过滤,使用报错注入,但是concat被禁了,无法拼接数据。这里用到了make_set()函数。
    我们先看make_set这个函数的定义

    make_set()的用法

    返回一个设定值(含子字符串分隔字符串。,。字符)由那些在设置位的相应位的字符串。str1对应于位0,str2至位1,以此类推。NULL值在str1,str2,...不添加到结果。

    make_set()应用实例


    9化成二进制是1001,倒过来也是1001,那么对应四位数,a对应1,取,b和c对应0,不取,d对应1,取,所以结果是a,d。10化成二进制是1010,倒过来就是0101,a对应0,不取,b对应1,取,c对应0,不取,d对应1,取。所以结果是b,d。
    再看

    1|4表示进行或运算,为0001 | 0100,得0101,倒过来排序,为1010,同上方法结果是a,c。
    但是还有一种就是str里面有null的。

    如果null对应到了1那么直接跳过。

    payload

    3化成二进制是11,倒过来也是11,updatexml报错注入的原理就是Xpath语法遇到特殊字符会报错,并且返回非法格式报错的内容,这里对应1,select database()也对应1。所以能把数据暴出来。

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

    这里碰到了一个小错误。在删掉limit1 之后报错Subquery returns more than 1 row,加个limit 1就好了。因为是白盒测试就直接读flag了。

    漏洞成因

    先看整个代码的逻辑,白名单是1到5,对id参数进行stop_hack过滤,然后将id的参数代入到$sql语句中,但是因为

    if (!in_array($id, $whitelist)) {
        die("id $id is not in whitelist.");
    }
    

    in_array没有开启强比较,我们的注入语句可以过去。就像这样

    <?php
    $id = "1 and  updatexml(1,make_set(3,'~',(select flag from flag limit 1)),1)";
    $whitelist  = range(1,5);
    if(!in_array($id,$whitelist)){
        echo "True";
    }
    else{
        echo "False";
    }
    

    id在比较的时候变成了1,1在$whitelist内得到False。

    接着执行我们的sql语句,就可以暴出数据

  • 相关阅读:
    Git 常用命令
    Python 常用算法记录
    Python基础Web服务器案例
    你真的懂SDWebImage?
    Core Data的那点事儿~
    看看 SDWebImage内部基本实现过程
    App上架流程 & 上架被拒10大原因
    KVO中你所不知道的"坑"
    math公式手写识别网址
    umi build出现的Path must be a string的问题解决
  • 原文地址:https://www.cnblogs.com/HelloCTF/p/13418951.html
Copyright © 2011-2022 走看看