[PHP底层]关于php://filter的分析
Posted Y4tacker
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[PHP底层]关于php://filter的分析相关的知识,希望对你有一定的参考价值。
写在前面
慢慢不太想写博客了,很多东西都忙不过来,学的很多,今天抽出空来还是督促自己写一篇,灵感来源于刚刚在网上看到的一篇wp,https://xz.aliyun.com/t/10446,不得不说现在比赛可真多哈,小伙伴们也别太经常打,多做点实际的东西
关于php://filter的处理的一些简单解释
这里面呢,有几个比较关键的,一个是需要能获取到文件流(也就是我们后面常常写的resource=xxx.txt),另一个是可选的,就是是否使用其他的编码过滤器,有了这两个东西我们就能配合伪协议去做一些神奇的东西了
php://filter常规流程分析
大家在网上看的最多的也是php://filter/read=convert.base64-encode/resource=1.php
那它发生了什么,我们具体来看看,在ext\\standard\\php_fopen_wrapper.c#php_stream_url_wrap_php
下一个断点
前六个是php://
没毛病,指针移六位
接下来,由于是filter我们进入分支
给pathdup赋值,内容是指针向右移动六位后,也就是/read=convert.base64-decode/resource=1.php
接下来开始准备获取文件流了,上图可以看到获取从/resource
开始的字符串,因此p就是/resource=1.php
接下来比较重要的地方是从指针+10的地方,也就是1.php
当中获取文件流(至于为什么加10,那是因为/resource=
长度是10)
跟进去,这里有一个zend_resolve_path的函数,这个支持用目录穿越的方式进行拼接组合路径,如这个函数名所说,会重新连接返回你一个绝对路径,这里就不带大家更深入为什么了,简单来说会获取你当前的目录路径再与1.php拼接后返回一个绝对路径,当然记住我说的支持路径穿越这很重要,这很重要
之后让*p
值为\\0
,也就是当前指针指向位置赋值为\\0
,我们知道c中字符串结尾是\\0
,因此不难想到这就是简单的清空字符串,接着对其重新赋值
因为这个php_strtok_r
函数跟进去以后比较复杂,那我简单搜索一下php_strtok_r
,虽然没搜到但看这个能猜到意思是用于分割字符串,结合后面的while可以看出,是一个不断遍历分割后字符串的过程
接下来,由于是read=
开头进入下面php_stream_apply_filter_list
函数,当然说一个题外话,这里不需要read=
或者write=
都行,php自己会判断,也就是为什么有第三个分支else
了
进去后又是这个函数,但结合后面不难猜到,这按照|
分隔符创建多个过滤器
现在整个流程也清楚了
关于exp的分析
可以看到exp为php://filter/resource=./convert.base64-decode/../1.php
,理解了我上面的过程,这个exp其实都不需要跟入动态调试分析了,这里直接简单说一下
首先是必须要能获取到文件流,还记得我说的吗这个函数中使用了zend_resolve_path去重新连接路径,我们现在resource=
后面是./convert.base64-decode/../1.php
,连接后也就是当前运行文件路径/./convert.base64-decode/../1.php
,最终就是当前运行文件路径/1.php
第二部分就是是否有过滤器,有就添加上去了,记得怎么获取的吗,filter
字符后面的部分用/
分割
也就是/resource=./convert.base64-decode/../1.php
因此就是遍历寻找正确的过滤器进行添加
resource=.
convert.base64-decode
..
1.php
整个流程结束
题外话
多提一句,至于是怎么判断有没有这个过滤器的,我的版本是php7.2.9,这里面是靠hash去判断的,还有就是之前很多时候我们对过滤器进行二次编码也是有原因的,如图所示
以上是关于[PHP底层]关于php://filter的分析的主要内容,如果未能解决你的问题,请参考以下文章