PHP 文件包含与文件读取
1.basic
1.1 php文件读取(File-open)
光是文件读取并没什么特别的,如果读取的文件名是我们可以控制的参数,那就会造成任意文件读取漏洞,这样我们就可以进一步的渗透,可以试着的文件我下面稍微整理了下
配置文件
- /etc/php/7.1/apache2/php.ini
- robots.txt
- .htaccess
session文件
- /tmp/sess_[your phpsessid value]
- /var/lib/php/sessions/
日志文件
- /var/log/apache/access.log
其他系统文件
/proc/net/arp
/proc/self/fd/4
/proc/self/environ
this requires PHP to be run as CGI on a
system that has the /proc pseudo-filesystem and PHP script is required to
have access to the aforementioned pseudo-file/var/lib/locate.db
- /var/lib/mlocate/mlocate.db
伪协议
- php://filter/read=convert.base64-encode/resource=index.php
- php://input + POST代码
1.2 php文件包含(File-Inclusion)
把要包含的文件打开
可以理解为先进行文件读取
解析在PHP tags中的php代码
可以理解为再进行一次代码执行
把整个文件的内容返回
包括tags内解析后的内容,也包括PHP tags外的内容,不过不会被解析而是会直接返回。
这里我们可以看到文件读取是文件包含的子集!不同于文件读取(仅仅读取内容),如果读取的文件的内容是我们可控的,那么就造成了任意代码执行漏洞。那么可以控制内容的文件有哪些呢,首先这个得依据情况而定,比较通用的方法是
利用伪协议,可以做一些绕过
包含上传临时文件
包含 session
一些 session 变量可能名字或值可能可控,比如上传进度Session
1.3 漏洞函数
File Inclusion
include, include_once, require, require_once
File open
有些读取函数有特别的用处,比如显示源码,会对文件内容有一个操作,这样有时只能有信息泄露了
1 | highlight_file,show_source,readfile,file_get_contents ,fopen,file,...... |
1.4 相关配置
allow_url_fopen
default: On
针对fopen等读取文件的函数,一般该配置是开启的,读取的URI可控的话可以造成SSRF,不过只能是以 GET 请求的方式,如果请求的报文中还能有可控的部分,那SSRF可以进一步渗透了。而且由于文件读取函数读取URI的操作和curl插件或php内置的http解析方式(如parse_url)不太一样,可以构造 URI 来进行一些绕过解析!(详见orange blackhat 2017 的ppt – A new Era of SSRF)
allow_url_include
default: Off
对打开的URL作为文件处理(Off 默认),php://input 伪协议也需要它开启才可用。
两个如果都开启的话LFI开启则升级为RFI,如果包含了我们自己VPS上的构造好的文件,那就可以 getshell 了。而且 allow_url_fopen 一般会打开,所以 allow_url_include 一般不会打开的,
另外,data:// 伪协议需要两个都开启
1.5 出现场景比较
File Inclusion, SSRF, CRLF, 命令注入 出现的场景很像,可以都尝试,分析清后台调用的函数。
- SSRF是Server Side再发起一个http(默认)请求 curl等
- include和require不可省略协议名(如http)
- 在 curl 中可以用0代表127.0.0.1,在include/require中即使开启了 allow_url_include 也不可以用 http://0/
2. Tecnique
2.1 php://filter 伪协议读取源码
上面刚说过,如果文件里有PHP tags的时候,包含过程会自动解析其中的php代码,但如果我们想获取源码的话,就不能直接包含了,php://filter/read
在我们包含前可以先将读取的内容做一次 base64 加密,这样就不可能由php tags了,拿到后,我们再解析回来就行了
1 | php://filter/read=convert.base64-encode/resource=index.php |
2.2 php://input 伪协议 POST 构造代码执行漏洞
需要开启 allow_url_include
php://input 返回的就是 post 一个请求时,post的数据。我们可以 post 一行命令来写shell
2.3 open_basedir绕过
在 php.ini中设置可被访问的目录
绕过
glob:// 伪协议是 PHP自带的文件目录管理协议,我们可以通过 glob:// 伪协议,跨出 open_basedir 列出任意目录文件。
2.4 后缀名截断
%00截断(magic_quotes_gpc=off,PHP小于5.3.4)
%00截断目录遍历:/var/www/%00 (magic_quotes_gpc=off,unix文件系统)构造长目录截断在Windows下256字节、Linux下4096字节时达到最大值
././././././././././././././././passwd
////////////////////////passwd点号截断
?file=../../../../../../../../../boot.ini/………[…]…………RFI可以用?,#等截断
2.5 Windows下,未知文件名利用FindFirstFile特性
FindFirstFile特性,类似于linux中的通配符,同样符合匹配模式的,windows指挥返回按后序字符ascii码排序的第一个文件,不过linux的通配符在include语句中不能用
windows | linux | |
---|---|---|
多个字符 | >> | * |
单个字符 | < | ? |
2.6 zip或phar 伪协议+上传伪造图片
部分山传场景中,只能上传图片,同时又有 LFI 。那么我们可以上传一个实际上是phar或zip压缩文件的假图片,zip:// 和 phar:// 伪协议可以读取这种压缩文件里的文件,在进行一次包含的话,就巧妙地 getshell 了
1 | # zip |
类似的还有compress.zlib 伪协议,不过不能直接访问到里面的文件
2.7 上传进度Session
配置 session.upload_progress.enabled = On
首先,post一个文件时,带上PHPSESSID,同时在表单中带上一个名为 PHP_SESSION_UPLOAD_PROGRESS 的变量,值为任意,在上传临时文件被删除前,如果,那就会产生一个上传进度session变量,这个 session 变量的名字为:
1 | # session.upload_progress.prefix + PHP_SESSION_UPLOAD_PROGRESS |
PHP_SESSION_UPLOAD_PROGRESS 的值我们是可控的,如果像上面写为 php 代码,抢在删除前包含后就可以被执行了。下面的链接里有以脚本可以用来测试能不能执行。
hitcon 2018 one-line-php-challenge
n1ctf2018 easy php(非预期)