新建一个 php 文件:a.php

<?php
$a = "a.txt";
include("php://filter/resource=" . $a);

在同一目录下新建一个文件:a.txt(内容为 <?php phpinfo();?> 的 base64 编码)

PD9waHAgcGhwaW5mbygpOz8+

在对应文件的相关函数下个断点:

资料】

贴一下关键代码:

php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const char *path, const char *mode, int options,zend_string **opened_path, php_stream_context *context STREAMS_DC) /* {{{ */
{...if (!strncasecmp(path, "php://", 6)) {}if (!strncasecmp(path, "temp", 4)) {     }if (!strcasecmp(path, "memory")) {}if (!strcasecmp(path, "output")) {}if (!strcasecmp(path, "input")) {}if (!strcasecmp(path, "stdin")) {} else if (!strcasecmp(path, "stdout")) {} else if (!strcasecmp(path, "stderr")) {} else if (!strncasecmp(path, "fd/", 3)) {} else if (!strncasecmp(path, "filter/", 7)) {/* Save time/memory when chain isn't specified */if (strchr(mode, 'r') || strchr(mode, '+')) {mode_rw |= PHP_STREAM_FILTER_READ;}if (strchr(mode, 'w') || strchr(mode, '+') || strchr(mode, 'a')) {mode_rw |= PHP_STREAM_FILTER_WRITE;}pathdup = estrndup(path + 6, strlen(path + 6));p = strstr(pathdup, "/resource=");if (!p) {zend_throw_error(NULL, "No URL resource specified");efree(pathdup);return NULL;}if (!(stream = php_stream_open_wrapper(p + 10, mode, options, opened_path))) {efree(pathdup);return NULL;}*p = '\0';p = php_strtok_r(pathdup + 1, "/", &token);while (p) {if (!strncasecmp(p, "read=", 5)) {php_stream_apply_filter_list(stream, p + 5, 1, 0);} else if (!strncasecmp(p, "write=", 6)) {php_stream_apply_filter_list(stream, p + 6, 0, 1);} else {php_stream_apply_filter_list(stream, p, mode_rw & PHP_STREAM_FILTER_READ, mode_rw & PHP_STREAM_FILTER_WRITE);}p = php_strtok_r(NULL, "/", &token);}efree(pathdup);if (EG(exception)) {php_stream_close(stream);return NULL;}return stream;} else {/* invalid php://thingy */php_error_docref(NULL, E_WARNING, "Invalid php:// URL specified");return NULL;}...return stream;
}

我们先来测试一下它的逻辑叭,我们设置变量 a 为 a/…/a.txt,那么就相当于我们 include 了一个 php://filter/resource=a/…/a.txt,在 filter 的判断断个点,逐步调过去:

首先是判断读或写,这里不用管,继续往下看。

这里有两个字符串的操作,并分别赋值给了 pathdup 和 p,对了,path 最开始是我们传入 include 的值,也就是 php://filter/resource=a/…/a.txt,在前面有一个 php:// 是否存在的判断,如果存在则将 path 的指针向后移 6 位,这里再将 path 指针向后移 6 位的地址给 pathdup,也就是将 filter 之后一位的地址赋给 pathdup,而 p 则是用 strstr 函数来获取到第一个 /resource= 的地址,然后会判断 p 是否被赋值,如果没有的话就报错,有的话就继续往下走。

接下来是第一个关键点,这里调用了 php_stream_open_wrapper 来判断文件是否存在,它这里对于文件的判断是将 p 指针向后移动 10 位,也就是取 /resource= 之后的那一段,也就是我们这里的 a/…/a.txt,虽然我们没有创建 a 这个文件夹,但是可以目录穿越直接穿回来,所以这里已经把文件读取出来将流赋给了 stream 变量。

然后 *p = ‘0’ 将 p 清空,就到了最有意思的一段了,也是第二个关键点。

大概讲一下这里的意思叭,它会把 pathdup 指针向后移动一位之后的字符串以 / 作为分割,每个被分割的部分都会被丢入 php_stream_apply_filter_list 函数中进行判断,也就是说,这里我们本来 pathdup 是 /resource=a/…/a.txt,会被分割为 resource=a & … & a.txt,每个都会被丢入 php_stream_apply_filter_list 中,那么这个函数是干什么的呢,我们可以跟进一下:


我们把中间最重要的一段单独提出来:

if (read_chain) {if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) {php_stream_filter_append(&stream->readfilters, temp_filter);} else {php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p);}}

不难发现,它会尝试以这个字符串为过滤器名去创建这个过滤器,如果创建成功,则会对我们之前读取的文件流进行过滤器的相关操作,而如果不存在的话,则会弹一个警告:

所以到这里我们也就理解了它的搞法,不难构造出 php://filter/resource=a/convert.base64-decode/…/…/a.txt:

不局限于 base64 编码,也可以尝试些别的。

PS:先前测的时候发现似乎从 5.x 到 8.x 都是这样的……
参考文献

【Web安全】php://filter 的浅略底层分析相关推荐

  1. 浅略/逐行分析园区网接入交换机配置(以Ruijie交换机为例)

    以Ruijie交换机(二层)为例: 设备上电后的默认配置为: (注:该设备使用了del config.text清除过了配置) version RGOS 10.4(2b12)p6 Release(196 ...

  2. java web开发中Filter使用Annotation配置 (转载)

    为什么80%的码农都做不了架构师?>>>    为了在java中得到request和response对象,搜索到了 <如何在Java的普通类中获取Session以及reques ...

  3. JAVA WEB篇4——Filter、Listener

    JAVA WEB篇4--Filter.Listener 1.Filter Filter本意为"过滤"的含义,是JavaWeb的三大组件之一,三大组件为:Servlet.Filter ...

  4. 1+X Web前端证书中级备考攻略

    1+X Web前端证书中级备考攻略 更新一下在毕业前拿到了高级证书 前言 我参加的是2020年11月的考试,那次考的相对比较简单,取得了不错的成绩,这个证书毕竟刚出没多久,网上资料也不太多,本文就整理 ...

  5. Doris浅略介绍 +部署+使用

    Doris 学习日常记录 (记录学习过程和遇到的坑,仅是个人学习使用) Doris浅略介绍 DORIS 组成 安装Doris步骤 由于IP地址变换,导致的 doris FE 重启失败问题 Doris ...

  6. ①、iOS-RAC的开发用法-底层分析以及总结

    iOS RAC系列 ①.iOS-RAC的开发用法-底层分析以及总结 ②.iOS-RAC-核心类分析-RACPassthroughSubscriber订阅者-RACScheduler调度者-RACDis ...

  7. 浅谈GWAS分析后的富集分析操作(GO/KEGG)

    浅谈GWAS分析后的富集分析操作(GO/KEGG) 作者:刘济铭 ######################## 在我们完成全基因组关联分析后,常常筛选得到特定性状的基因集,接下来,通常我们需要开展 ...

  8. Android底层隐私数据,Android Intent传递数据底层分析详细介绍_Android_脚本之家

    Android  Intent传递数据底层分析详细介绍 我们知道在Activity切换时,如果需要向下一个ActivityB传递数据,可以借助Intent对象的putExtra方法. 但是不知各位有没 ...

  9. 【Python基础避坑】函数内存底层分析,全局变量/局部变量,参数传递,浅拷贝/深拷贝

    老高说,基本功不扎实会在工作中遇到很多的坑,非常同意- 函数定义示例 1.含有返回值 # -*-coding:utf-8-*- def add(a, b):'''两数相加'''sum = a + br ...

最新文章

  1. 巨杉数据库通过“Mpp数据库基础能力认证”,权威技术认证金融级数据库
  2. solr文档索引最佳实践
  3. SpringMVC与Struts2区别与比较总结
  4. 《软件工程》课之-调查问卷的心得体会
  5. Bash shell脚本练习(一)
  6. 语义分割江湖的那些事儿——从旷视说起
  7. SCUT - 243 - 宝华复习 - 二分 - 桶计数
  8. css 鼠标悬浮样式_【技术】CSS设置链接鼠标(失效)不能点样式
  9. 【网络信息安全】网络安全基础
  10. shell编程入门步步高(二、基础概念)
  11. 【原创】Linux下追加磁盘空间的方法
  12. awvs12 Server Exception_使用WebSocket搭建服务器server
  13. 【BDTC 2018】PingCAP申砾:做一个真正通用的数据库产品
  14. 新品上市|A股场内衍生品大盘点
  15. RESTful接口开发规范以及注意事项
  16. C#winform窗体背景音乐播放总结
  17. python爬虫模拟登录学校教务系统(青果教务系统)并查询个人成绩
  18. preHandle执行多次问题
  19. 大数据相关职位的知识储备与系统学习路线规划以及所需时间
  20. u盘插上计算机未响应,插U盘没反应的一种情况与解决

热门文章

  1. 成功解决ERROR: Failed building wheel for pycocotools
  2. 成功解决ERROR: Unable to find the development tool `make` in your path; please make sure that you have t
  3. Py之pyquery:pyquery的简介、安装、使用方法之详细攻略
  4. 成功解决matplotlib\cbook\deprecation.py:107: MatplotlibDeprecationWarning: Passing one of 'on', 'true',
  5. DL框架之Keras:深度学习框架Keras框架的简介、安装(Python库)、相关概念、Keras模型使用、使用方法之详细攻略
  6. JAVA_OA(bug篇)(一):SpringMVC的bug1
  7. 深入浅出计算机组成原理学习笔记:总线-计算机内部的高速公路(第42讲)
  8. appium+python自动化33-解锁九宫格(TouchAction)
  9. libpcap抓取数据包
  10. 自定义获取url方法