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语句,就可以暴出数据

  • 相关阅读:
    疫情环境下的网络学习笔记 python 5.8 数据库入门终章
    疫情环境下的网络学习笔记 python 5.7 navicat数据库,例题,sql注入
    疫情环境下的网络学习笔记 python 5.6 暂时看看
    疫情环境下的网络学习笔记 python 5.5 MYSql 表关系,外键
    疫情环境下的网络学习笔记 python 5.4 数据库基础
    疫情环境下的网络学习笔记 python 4.30 初识数据库
    疫情环境下的网络学习笔记 python 4.29 网络小项目
    XJOI 夏令营501-511测试11 游戏
    XJOI 夏令营501-511测试11 统计方案
    CF1197D Yet Another Subarray Problem
  • 原文地址:https://www.cnblogs.com/HelloCTF/p/13418951.html
Copyright © 2011-2022 走看看