文件包含
文件包含分为本地文件包含(Local File Inclusion,简LFI)和远程文件包含(Remote File Inclusion,简RFI)。
在php中常见的文件包含函数有:
当php包含一个文件时不会在意文件的类型,都会把文件当做php文件来解析。(file_get_contents()除外)。
本地文件包含
测试代码:
存在本地文件包含漏洞。
%00截断
条件:
playload:
这时就是本地任意文件包含。
可以读取服务器敏感信息。
路径长度截断和点号截断
测试代码:
Linux 需要文件名长于 4096,Windows 需要长于 256。
在windows下测试是240个点和240个斜杠加点都能截断。
在linux上只有2038个/.才能截断。(至少2038个)
php版本5.3之后被修复。
远程文件包含
远程文件包含需要设置
测试代码:
问号截断
测试代码:
php伪协议
file:// — 访问本地文件系统
file:///文件绝对路径和文件名
php://
php:// — 访问各个输入/输出流(I/O streams)
条件:
php://input
php://input 是个可以访问请求的原始数据的只读流,enctype=”multipart/form-data” 的时候 php://input 是无效的。POST请求的情况下,php://input可以获取到post的数据。
php://output
php://output 是一个只写的数据流, 允许你以 print 和 echo一样的方式 写入到输出缓冲区。
php://filter
该协议语法为:php://filter:/=参数功能resource=数据来源(必须)
read=读取
write=写入
常用功能:读取源码:
测试代码:
写入webshell
测试代码:
这里需要理解一下php base64解码的原理:
base64编码中只包含64个可打印字符,而PHP在解码base64时,遇到不在其中的字符时,将会跳过这些字符,仅将合法字符组成一个新的字符串进行解码.
一般步骤:
起初$content中一共有十四个字符。
我们传入的字符要与其进行拼接。
这里我们可以利用php://filter/write=convert.base64-decode 来首先对其解码。在解码的过程中,字符、空格等一共有7个字符不符合base64编码的字符范围将被忽略,所以最终被解码的字符仅有“phpexit”和我们传入的其他字符。
“phpexit”一共7个字符,因为base64算法解码时是4个byte一组,所以给他增加1个“c”一共8个字符。这样,”phpexita”被正常解码,而后面我们传入的webshell的base64内容也被正常解码。而后面我们传入的webshell的base64内容也被正常解码。结果就是<?php exit; ?>没有了。成功写入可执行的webshell。
方法二:
<?php exit; ?>实际上是一个XML标签,既然是XML标签,我们就可以利用strip_tags函数去除它,而php://filter刚好是支持这个方法。
测试代码:
我们最终的目的是写入一个webshell,而写入的webshell也是php代码,如果使用strip_tags同样会被去除。
php://filter允许使用多个过滤器,我们可以先将webshell用base64编码。在调用完成strip_tags后再进行base64-decode。“死亡exit”在第一步被去除,而webshell在第二步被还原。
playload:
POST传入:
txt=PD9waHAgcGhwaW5mbygpOyA/Pg==&filename=php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php
这个方法的条件就是不开启短标签。
其他格式:
zlib://
无版本要求
测试代码:
phar://
phar:// — PHP 归档
phar:// 数据流包装器自 PHP 5.3.0 起开始有效
条件:
压缩包必须是zip协议压缩的文件
测试代码同上
data://
data:// — 数据(RFC 2397)
条件:
测试代码同上
本地文件包含之姿势
日志文件
apache日志
测试系统:
centos:
日志路径:/var/log/httpd/access_log或/etc/httpd/logs/access_log
测试系统:
ubuntu16.04
PHP 5.6.40+ Apache/2.4.18
日志路径:/var/log/apache2/access.log
日志中的一条信息:
信息:访问者ip
请求的时间
请求方式以及url
状态码:200
发送给客户端的总字节数
客户端浏览器的信息
我们知道文件包含时,不管文件是什么类型都当作php来解析。
所以我们可以把webshell藏在请求中,然后就写入日志文件。我们再包含日志文件,就可以获得webshell。
我们试试shell藏在url中:
访问:
查看日志:
发现 :shell被url编码了。
尝试包含日志文件:
发现解析失败。
这时我们可以尝试把shell藏在User-Agent里:
查看日志:
发现这时成功写入shell。
我们再次包含日志文件就getshell了。
ssh 日志
利用条件
需要知道ssh-log的位置,且可读。默认情况下为 /var/log/auth.log
我们可以用ssh连接来向ssh日志中写入webshell:
查看日志文件:
成功写入shell。包含它就getshell了。
session 文件
利用条件:session文件路径已知,且其中内容部分可控
php的session文件的保存路径可以在phpinfo的session.save_path看到
session 存储位置:
/var/lib/php/session
测试代码:
session的文件名是“sess_”+当前PHPSESSID
故session文件路径为/var/lib/php/session/sess_2n3gliufi6ibj1b5ddovr6eub4
所以此时包含:
绕过姿势
在指定前缀名后,我们可以通过../来跳转目录。
编码绕过
服务器端常常会对于../等做一些过滤,可以用一些编码来进行绕过
问号绕过,#号绕过,%00截断,路径截断(./),长度截断(.)和协议。
常见敏感信息文件路径
windows
linux:
防御措施在很多场景中都需要去包含web目录之外的文件,如果php配置了open_basedir,则会包含失败
做好文件的权限管理
对危险字符进行过滤等等