PHP代码审计之文件包含

目录

 

漏洞简介

漏洞实现

文件包含之伪协议

漏洞实例

漏洞防御


漏洞简介


什么是文件包含?

在之前接触的python中,我们会通过import来导入一些公共资源

在PHP中,也有类似的函数,为了更好地使用代码的重用性,引入了文件包含函数,可以通过文件包含函数将文件包含进来,直接使用包含文件的代码

文件包含分文本地文件包含(Loacl File Inclusion,LFI)和远程文件包含(Remote File Inclusion,RFI)

远程文件包含需要在php.ini中设置allow_url_include = on和allow_url_fopen= on

文件包含漏洞

什么是文件包含漏洞?

首先得说明,文件包含本身并不是问题,在程序中写死的文件包含不会形成漏洞,几乎所有漏洞的成因都是因为相关参数可控

那么文件包含漏洞是怎么产生的呢?

在包含文件时候,为了灵活包含文件,将被包含文件设置为变量,通过动态变量来引入需要包含的文件时,攻击者利用包含的特性,加上应用本身对文件(包含)控制不严格,最终造成攻击者进行任意文件包含(任意文件包含才是漏洞),这样就导致了文件包含漏洞

通常文件包含漏洞出现在PHP语言中

PHP文件包含的函数

  • include()函数:使用include引用外部文件时,只有代码执行到include代码段时,调用的外部文件才会被引用并读取,当引用的文件发生错误时,系统只会给出个警告错误,而整个php文件会继续执行
  • require()函数:在php文件被执行之前,php解析器会用被引用的文件的全部内容替换require语句,然后与require语句之外的其他语句组成个新的php文件,最好后按新的php文件执行程序代码
  • include_once()函数:使用include_once会在导入文件前先检测该文件是否在该页面的其他部分被引用过,如果有,则不会重复引用该文件,程序只能引用一次
  • require_once()函数:功能与 require()相同,区别同样在于当重复调用同一文件时,程序只调用一次

漏洞实现


本地文件包含漏洞

先说明一下,文件包含,被包含的文件,不一定需要是动态脚本语言,包括txt文件在内都会被直接当成动态语言来执行,下面给出一段代码

PHP代码审计之文件包含_第1张图片

修改代码为用户可控,如下

PHP代码审计之文件包含_第2张图片

基于apache日志的文件包含

首先开启错误日志,在httpd.conf文件中,相关参数如下

ErrorLog "logs/error.log"
CustomLog "logs/access.log" common

PHP代码审计之文件包含_第3张图片

第一步,输入如下url,让apache记录错误

PHP代码审计之文件包含_第4张图片

尝试包含此文件,不过需要知道这个文件的绝对路径,发现并没有成功,为什么?

PHP代码审计之文件包含_第5张图片

这里面的<>被编码成%3C和%3E,在文件包含中无法对该文件解码,所以只能输出源文件

怎么解决,抓包修改即可

PHP代码审计之文件包含_第6张图片

PHP代码审计之文件包含_第7张图片

PHP代码审计之文件包含_第8张图片

还可以尝试使用可以直接写进User Agent里面也可以被写进日志里

并且此处需要日志所在文件夹的权限,才可以成功

其他形式

开发者限制了包含文件的后缀,代码如下

PHP代码审计之文件包含_第9张图片

经过查询资料,发现可以使用%00截断,但00截断有以下三个条件

  • PHP版本 < 5.3 (不包括5.3) 
  • PHP配置magic_quotes_gpc = off
  • PHP对所接收的参数,如以上代码的$_GET['file']未使用addslashes函数

在5.4版本以上该参数被移除,并未找到该参数

此处我先用大于php5.3.29的做实验,配置参数

PHP代码审计之文件包含_第10张图片

PHP代码审计之文件包含_第11张图片

下面是5.2.17版本,设置如下,然后重启服务

PHP代码审计之文件包含_第12张图片

PHP代码审计之文件包含_第13张图片

远程文件包含

什么是远程文件包含?

当包含的文件在远程服务器上时,就形成了远程文件包含

构成条件

  • 需要php.iniallow_url_include = on以及allow_url_fopen=on
  • 所包含远程服务器的文件后缀不能与目标服务器语言相同。(比如目标服务器是php脚本语言解析的,那么包含的远程服务器文件后缀不能是php)

首先看下远程服务器的phpinfo信息

PHP代码审计之文件包含_第14张图片

本机信息如下

PHP代码审计之文件包含_第15张图片

修改配置文件如下,重启服务 

PHP代码审计之文件包含_第16张图片

直接尝试包含php文件,发现并不是本机的phpinfo信息,如下图

PHP代码审计之文件包含_第17张图片

这是因为,我们包含的并不是简单的,而是远程主机执行完这段代码的源代码,如下图

PHP代码审计之文件包含_第18张图片

解决方案就是,只要不使用php其他都好,我们可以修改成txt文件,成功执行如下图

PHP代码审计之文件包含_第19张图片

文件包含之伪协议

文件中可以包含不同的伪协议,我将在下面演示其中的一些

  • data:text/plain or data: text/plain; base64
  • php://input
  • php://filter
  • file://
  • zip://

data:text/plain

输出直接显示在相应的URL中,显示参数:data:text/plain

然后你需要执行如下所示的php代码

PHP代码审计之文件包含_第20张图片

data:text/plain; BASE64

有另一种方法来使用data:text/plain; base64,不过此时你需要使用base64编码来执行PHP代码,base64php代码如下所示

此处我编码的是会报错

PHP代码审计之文件包含_第21张图片

php://input

php://input访问请求的原始数据的只读流(read-only stream),会将post请求中的数据作为php代码执行

php://filter

php://filter可以读取php文件的代码base64编码的输出并将其返回给你

file://

file://用于访问本地文件系统,不受allow_url_fopen orallow_url_include的影响,你可以使用file:// absolute / path / to / file来获取

zip://

zip://可以访问zip文件中的文件,但它需要一个绝对路径。你可以使用zip://[archive absolute path] # [compressed file name]在本地创建一个文件并将其压缩到一个zip压缩文件中

此处参考文章

参考文章:https://www.jianshu.com/p/065deed3142e

 

漏洞实例


可尝试审计phpMyAdmin4.8.1 :https://pan.baidu.com/s/1w-G8mCf0jOw6ptekku7HRQ  提取码:9k6a

代码审计工具:https://pan.baidu.com/s/1DuRkZMy4Ya9RkkFmWNpQqQ  提取码:0zj9

PHP代码审计之文件包含_第22张图片

打开工具,新建项目,加载源码,全局搜索include,如下图

PHP代码审计之文件包含_第23张图片

发现前几个include都是包含的固定文件,只有这个是接受传参的,在index.php文件中,直接跟进源码

PHP代码审计之文件包含_第24张图片

这里有四个条件

  1. 通过is_string()函数,判断参数是不是字符串(可满足)
  2. 通过preg_match()函数做正则匹配,判断是否以index开头(可满足)
  3. 通过in_array()函数判断传参字符串是否在$target_blacklist数组中
  4. 检查是否符合自建类的checkPageValidity()函数

继续根基第三个条件的数组只要我们输入不是一下这两个文件名即可

PHP代码审计之文件包含_第25张图片

继续跟进checkPageValidity()函数,找到了此函数的定义,发现有三种情况会返回true,而且都是要求$page在$whitelist数组中,如下图

PHP代码审计之文件包含_第26张图片

接下来看第一个条件,判断$whitelist是否为空,如果为空,重新赋值

PHP代码审计之文件包含_第27张图片

继续跟进$goto_whitelis数组,发现这个数组相当于一个白名单,如下图

PHP代码审计之文件包含_第28张图片

接下来来到第一个返回true的位置,判断很简单就看传参的字符串是否在数组里面,在这里我们完全可以输入一个白名单的文件名,但是没有什么意义,包含一个已被写好的文件,无法达成任何目标

PHP代码审计之文件包含_第29张图片

接着分析,第二个返回true的位置,首先看看上面的代码做了什么处理

mb_substr()函数:截取字符串。语法:mb_substr($a,0,3),意思是截取字符串a的前三位

mb_strpos()函数:判断位置的函数,首先把传参$page连接一个问号,在查找问号的位置并返回,只会返回第一个问号的位置,如果我们传参里面有问号的话。。。。。

在这里如果我们能传入一个问号,再通过相对路径那岂不是可以包含任意文件了?可惜,get传参传不了问号会报错,因为在url中问号代表接下来是传参的意思,而自身无法作为传参值,有人可能会想可不可以编码,首先有编码需要有解码,没解码的话你的传参只是字符串将不再具有任何意义,而我们知道get型传参是会进行一侧解码的,所以只通过%3f是无法绕过的

继续看下个判断

PHP代码审计之文件包含_第30张图片

接着看最后一个判断,如果还是不可以,那可就GG了,注意看第三个位置除了标红的地方,其他代码和上个位置一样

urldecode()函数:进行一次url解码

突然好像感觉抓住了什么,url中的%3f会被直接识别成问号,那么在进行一次编码呢?反正这里会在进行一次解码操作。

很明显二次编码后为%25%3f,通过url解码一次为%3f,代码执行到第三个位置这里,会再进行一次解码,那么问号不久出来了吗?

PHP代码审计之文件包含_第31张图片

如下图,白名单中有个sql.php,假设所在文件的我们访问文件的上一级存在一个1.txt

我们尝试构造payload:target=sql.payload%253f/../1.txt

payload传到后台之后是:sql.php%3f../1.txt

传参是字符串,满足了第一个被包含的第一个条件

传参不是index开头,满足了第二个条件

传参不是impot.php和export.php,满足了第三个条件

我们去看一下返回true的第三个条件

首先url解码-->>sql.php?/../1.txt

拼接问号-->>sql.php?/../1.txt?-->由mb_strpos()函数返回位置数字 7

由mb_substr()函数截取前7为字符-->>sql.php

由in_array()函数判断sql.php是否在白名单中

我们是从白名单拿出来的肯定在啊,所以这里就构成了任意文件包含,实验结果如下

PHP代码审计之文件包含_第32张图片

如何获取webshell?

但是有个问题这里的1.txt是我上直接写在服务器里面的,这里的phpmyadmin并没有上传功能,

解决方法

1.进行一次查询,查询之后会有日志文件,进入后台后,在变量栏目里开启日志记录

查询语句如下

SELECT ''

PHP代码审计之文件包含_第33张图片

PHP代码审计之文件包含_第34张图片

日志文件如下,尝试包含,此处只能使用相对位置

PHP代码审计之文件包含_第35张图片

PHP代码审计之文件包含_第36张图片

第二个思路,我们可以创建一个新表,这里还是带着php文件头吧,如下图

PHP代码审计之文件包含_第37张图片

PHP代码审计之文件包含_第38张图片

接下来我们去找一下这个表,尝试包含

PHP代码审计之文件包含_第39张图片

PHP代码审计之文件包含_第40张图片

其他方式

还看到一个sessionid的,不过我没找到文件

参考文章:https://www.jianshu.com/p/0d75017c154f

漏洞防御


1、无需情况下设置allow_url_include和allow_url_fopen为关闭

2、对可以包含的文件进行限制,可以使用白名单的方式,或者设置可以包含的目录,如open_basedir

3、建议假定所有输入都是可疑的,尝试对所有输入提交可能可能包含的文件地址,包括服务器本地文件及远程文件,进行严格的检查,参数中不允许出现../之类的目录跳转符。

4、严格检查include类的文件包含函数中的参数是否外界可控

你可能感兴趣的:(web渗透学习笔记)