PHP文件包含漏洞小结

PHP 文件包含漏洞记录。

基本概念

相关函数

php中引发文件包含漏洞的函数通常是以下四个函数:

  1. include()
  2. include_once()
  3. require()
  4. require_once()

include()包含如果出错,如文件不存在,提出警告然后继续执行。

require()包含文件出错,直接退出。

require_once()include_once()require()include()功能相似,只是为了防止重复包含。

当使用这几个函数包含文件时,不管文件是什么类型,都会被解析成php。

场景

  1. 具有相关的文件包含函数。
  2. 文件包含函数中存在变量,比如include $file
  3. 攻击者可以控制变量,比如$file = $_GET[file]

分类

LFI(本地文件包含)

能够打开并包含本地文件的漏洞。

RFI(远程文件包含)

能够包含远程服务器的文件并执行,危害性很,但是需要利用条件。

  1. allow_url_fopen = On
  2. allow_url_include = On

注:
当包含远程文件也是php时,php文件会在远程服务器执行。

php.ini中,allow_url_fopen默认为Onallow_url_include在php5.2后默认为Off

包含姿势

测试代码:

  1. allow_url_fopen = On
  2. allow_url_include = Off

PHP伪协议

php://input

利用条件:

  1. allow_url_include = On
  2. 对allow_url_fopen不做要求。

姿势:

php://filter

利用条件:
无特别要求。

姿势:
http://localhost/test/FileInclude/index.php?file=php://filter/read=convert.base64-encode/resource=info.txt

可以读取base64加密后的文件源码。

注:
少了read关键字。
http://localhost/test/FileInclude/index.php?file=php://filter/convert.base64-encode/resource=info.txt

phar://

利用条件:

  1. php > 5.3.0

姿势:
假如有个zip压缩包,其中的文件为info.txt

相对路径:

当然也可以使用绝对路径。

zip://

利用条件:

  1. php >= 5.3.0

姿势:
与phar相同。

使用zip协议,需要指定绝对路径。同时将#编码为%23

如果时相对路径,那么包含失败

data:URL schema

利用条件:

  1. php >= 5.2
  2. allow_url_fopen = On
  3. allow_url_include = On

姿势一:
http://localhost/test/FileInclude/index.php?file=data://text/plain,<?php phpinfo();?>

执行系统命令:
http://localhost/test/FileInclude/index.php?file=data://text/plain,<?php system('whoami');?>

姿势二:
http://localhost/test/FileInclude/index.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

PD9waHAgcGhwaW5mbygpOz8+的base64解码为'<?php phpinfo();?>,而+的url编码为%2b

执行系统命令:
http://localhost/test/FileInclude/index.php?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==

其中PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==的base64解码为:<?php system('whoami');?>

包含session

利用条件:

  1. session文件路径已知,且其中部分内容可控。

姿势:
php的session文件保存路径可以在phpinfo的session.save_path看到。

常见的php_session存放位置:

  1. /var/lib/php/sess_PHPSESSID
  2. /tmp/sess_PHPSESSID
  3. /tmp/sessions/sess_PHPSESSID

session文件的命名格式为sess_[PHPSESSID]。PHPSESSID可以在发送请求的cookie中查到。

要包含并利用的话,需要能控制部分sesssion文件的内容。暂时没有通用的办法。有些时候,可以先包含进session文件,观察里面的内容,然后根据里面的字段来发现可控的变量,从而利用变量来写入payload,并之后再次包含从而执行php代码。

参考文章:

包含日志

访问日志

利用条件:

  1. 知道服务器日志的存储路径,且日志文件可读。

姿势:
很多时候,web服务器会将请求写入到日志文件中,比如说apache。在用户发起请求时,会将请求写入access.log,当发生错误时将错误写入error.log。默认情况下,日志保存路径在 /var/log/apache2/。

但如果是直接发起请求,会导致一些符号被编码使得包含无法正确解析。可以使用burp截包后修改。

包含日志后执行:

在一些场景中,log的地址是被修改掉的。你可以通过读取相应的配置文件后,再进行包含。

SSH log

利用条件:

  1. 知道ssh-log的位置且可读。默认为/var/log/auth.log

姿势:
用ssh远程连接,密码无所谓:

然后就可以写入php代码:

利用:

包含environ

利用条件:

  1. php以cgi的方式运行,这样environ才会保持UA头。
  2. environ文件存储位置已知,且environ文件可读。

姿势:
proc/self/environ中会保存user-agent头。如果在user-agent中插入php代码,则php代码会被写入到environ中。之后再包含它,即可。

参考:

  1. shell via LFI - proc/self/environ method
  2. The proc/self/environ Injection

包含fd

与包含environ类似。
参考:

  1. LFI Cheat Sheet:/proc/self/environ LFI Method

包含临时文件

php中上传文件,会创建临时文件。在linux下使用/tmp目录,而在windows下使用c:\winsdows\temp目录。在临时文件被删除之前,利用竞争即可包含该临时文件。

由于包含需要知道包含的文件名。一种方法是进行暴力猜解,linux下使用的随机函数有缺陷,而window下只有65535中不同的文件名,所以这个方法是可行的。

另一种方法是配合phpinfo页面的php variables,可以直接获取到上传文件的存储路径和临时文件名,直接包含即可。参考:LFI WITH PHPINFO() ASSISTANCE

类似利用临时文件的存在,竞争时间去包含的,参考:XMAN夏令营-2017-babyweb-writeup

包含上传文件

利用条件:

  1. 运乎之妙,存乎一心。知道上传文件在哪,叫啥。

姿势:
配合上传漏洞。

其他

一个web服务往往会用到多个其他服务,比如ftp服务,数据库等等。这些应用也会产生相应的文件,但这就需要具体情况具体分析咯。这里就不展开了。

绕过姿势

接下来聊聊绕过姿势。平常碰到的情况肯定不会是简简单单的include $_GET[‘file’];这样直接把变量传入包含函数的。在很多时候包含的变量/文件不是完全可控的,比如下面这段代码指定了前缀和后缀:

指定前缀

测试代码:

目录遍历

测试代码:

可以使用../进行目录遍历:

编码绕过

服务器端常常会对于../等做一些过滤,可以用一些编码来进行绕过。下面这些总结来自《白帽子讲Web安全》。

  • 利用url编码:
    • ../
      • ..%2f
      • %2e%2e%2f
      • %2e%2e/
    • ..\
      • %2e%2e%5c
      • ..%5c
      • %2e%2e\
  • 二次编码,服务器有可能解码一次
    • ../
      • %252e%252e%252f
    • ..\
      • %252e%252e%255c
  • 容器/服务器的编码方式
    • ../
      • ..%c0%af
        • 注:
      • %c0%ae%c0%ae/
        • 注:java中会把”%c0%ae”解析为”\uC0AE”,最后转义为ASCCII字符的”.”(点)
        • Apache Tomcat Directory Traversal
    • ..\
      • ..%c1%9c

指定后缀

URL

url格式:
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
姿势一:

利用协议
长度截断
0字节截断

防御方案

Refference