前言
这周第一次尝试了正经的 CTF 比赛, 虽然吧一题也没做出来 ,但总归也是有所收获。
路漫漫其修远兮,吾将上下而求索。Go Fight !
文件包含
文件包含,简单来说就是开发人员在开发中为了方便以及提高代码的重用性和灵活性,会将在多个地方重复使用到的代码或者函数等单独写到一个文件中,在需要用到的地方直接调用此文件,而无需重复编写,包含后的文件即相当于将被包含的整个文件内容复制到了包含处。
当 PHP 包含一个文件时,会将该文件当做 PHP 代码执行,而不会在意文件时什么类型。
漏洞成因
程序开发人员一般希望代码更灵活,所以往往将被包含的文件设置为变量,用来进行动态调用,但正是由于这种灵活性,使得客户端可以控制变量的值,如果服务器端未对变量值进行合理地校验或者校验被绕过,那么就会导致文件包含漏洞。通常文件包含漏洞出现在 PHP 语言中。
PHP 中文件包含的函数
- include()
当使用该函数包含文件时,只有代码执行到include()
函数时才将文件包含进来,发生错误时只给出一个警告,继续向下执行。 - include_once()
include_once()
语句和include()
语句类似,唯一区别是如果该文件已经被包含过,则不会再次包含。如同此语句名字暗示的那样,只会包含一次。include_once()
可以用于在脚本执行期间同一个文件有可能被包含超过一次的情况下,想确保它只被包含一次以避免函数重定义,变量重新赋值等问题。 - require()
除了处理失败的方式不同之外,require()
和include()
几乎完全一样。require()
在出错时产生 (E_COMPILE_ERROR) 级别的错误,换句话说将导致脚本中止。而include()
只产生警告 (E_WARNING),脚本会继续运行。 - require_once()
require_once()
语句和require()
语句完全相同,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含。 _once 的含义与 include_once() 相同。
PHP 文档中相关内容可查看链接:PHP:include 及 PHP:include_once
文件包含漏洞可分为两类,本地文件包含与远程文件包含。它们的原理是相同的,不同点就是前者只能包含服务器内存在的文件,后者则可包含远程服务器内的文件。
本地文件包含漏洞
本地文件包含,Local File Inclusion,LFI。当包含的文件在服务器本地时,就形成了本地文件包含。
下面做个简单的测试来直观描述一下,在包含值可被直接控制的情况下是怎么样的。
1 | 网页源码很简单如下,file_include.php: |
为了更加方便直观,我们可以在网页源码目录下随便新建几个不同类型的文件以供测试,结果如下。
PS:文件包含可以包含任意文件,即便被包含的文件并不是与当前编程语言相关,甚至为图片,只要文件被包含,其内容就会被包含文件所包含,并以当前服务器脚本语言执行。如果包含的文件内容不符合当前脚本语言语法的,会直接将文件内容输出。
当然,可以包含的文件肯定不只局限于该目录下,例如,我们可以读取系统配置文件 C:\WINDOWS\system.ini
,and so on.
在上面所举的例子中,服务器端没有对动态调用的变量值进行任何的过滤,即用户可以随意控制变量的值,这种情况在现实中应该是不太可能出现的。那么如果开发者在开发中就限制了所能包含的文件的后缀名的话,又要如何绕过呢。
1 | 例如,可以将先前的源码稍作修改如下: |
可以看到,现在我们只能包含指定后缀名的文件。But don’t worry,我们可以使用 %00
截断的方式来进行绕过。
但是使用该方法有一些前提条件,如下:
由于本人安装的 PHP 版本大于5.3,要实操一遍挺麻烦的,,,So,这里就直接附上相关博客来说明该方法了。
谈一谈文件包含漏洞 或 文件包含漏洞学习总结
远程文件包含漏洞
远程文件包含,Remote File Inclusion,RFI。当包含的文件在远程服务器上时,就形成了远程文件包含。
要实现远程文件包含有两点需要注意:
需要 PHP 配置文件 php.ini
内的 allow_url_include = on
以及 allow_url_fopen=on
由于我还没弄明白怎么整一个远程服务器的环境……所以这里又没有实操……
相关知识点的分析和具体操作仍可参考上述两篇博客。
伪协议
PHP 带有很多内置 URL 风格的封装协议,这些协议常在CTF题目中与文件包含漏洞相结合。现以本文最初编写的页面为环境,来举例部分伪协议在文件包含漏洞中的应用。PHP 官方文档中相关内容的描述可查看该页面,PHP:支持的协议和封装协议
1` data://
该协议有两种使用方法,参数为需要执行的 PHP 代码。
data:text/plain,需要执行的 PHP 代码
data:text/plain;base64,需要执行的base64编码后的 PHP 代码
这里有一点需要特别注意的地方,base64编码后如果有加号需要手动的进行url编码(即将 + 替换成 %2b),否则无法识别。例如:
2` php://input
这个协议的利用方法是将需要执行的 PHP 代码写在 POST 中提交,不用键值对的形式,只写代码即可。
NOTICE:
在验证该协议的使用中遇到了一些问题,暂时还不知道原因,先在此记录下来。
① 使用 burpsuite 抓本地包时,域名需使用本机的 IPv4 地址,如果域名使用 127.0.0.1 或者 localhost 的话无法抓取到本地包;该点可参考博客 关于BurpSuite抓不到本地包的问题解决方法汇总
② 此处不知为何无法使用火狐的 hackbar 插件来提交 POST 参数;下图左半部分内容来自博客 php伪协议实现命令执行的七种姿势,右半部分内容为本人验证时的操作结果。
3` php://filter
使用该伪协议可以实现读取 PHP 文件源码并以 base64 编码格式输出的功能。
该功能的使用方法为php://filter/read=convert.base64-encode/resource=想要读取内容的PHP文件
4` file://
该协议用于访问本地文件系统。在CTF中通常用来读取本地文件,不受 allow_url_fopen
与 allow_url_include
的影响。使用方法为 file://文件的绝对路径
5` zip://
该协议可以访问压缩文件中的文件,和 file://
一样都需要传入文件的绝对路径。
使用方法为 zip://压缩包绝对路径#压缩文件内的文件名
,这里需要注意,需要手动对中间的 # 号进行url编码(即将 # 替换成 %23)