热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

phpmkdir函数_探究PHP中的Mkdir函数

原标题:探究PHP中的Mkdir函数一、起因在复现分析Wordpress-5.0.0RCE的时候,因为在写图片的过程中,根据图片的dirn

原标题:探究PHP中的Mkdir函数

一、起因

在复现分析Wordpress-5.0.0 RCE 的时候,因为在写图片的过程中,根据图片的dirname创建目录,而后根据basename写入图片。在目录创建成功的前提下,应该是可以写入文件的。但是情况却不是如此,过程中我要在写目标图片前,必须还要再写一个辅助图片。其实这个辅助图片不是很重要,而重要的是这个辅助图片的目录创建。

此过程中例如需要写入目标文件为:

首先需要先写一张

为什么会这样?假设直接写目标文件,过程中会首先创建目录:

其实这个过程是没有创建任何目录的,因为判断是directoryalready ,到下一步写入图片这里是Imagick::writeImage,在这里就会出问题。invaildfile path.报错。因为这里不存在/var/www/html/wordpress/wp-content/uploads/2019/03/1.jpg?这个目录,这涉及到系统调用,因系统的不同相对于的系统处理函数处理的方式也不同。

列如在kali 下Imagick::writeImage写入./1?/../1.png,./1?这个目录是会报错的。具体系统调用如下:

首先判断了这个文件的状态,而后调用openat打开这个文件并不存在。AT_FDCWD表示打开的文件位置相对于当前目录。这是我在做的时候遇到的情况。

但是在WORDPRESS IMAGE 远程代码执行漏洞分析 一文中,甚至其他另一篇。都没提到两次写图片。难道因为window和linux的不同吗?就这个问题我进行了一次对mkdir的探究。发现其实有很有趣。

二、PHP内核 && 系统差异 之mkdir2.1 Linux &&PHP 7.3.2-3

mkdir(‘./1?/../1′,777,true);

mkdir(‘./1?/../1′,777,false);

当第三参数为$recursive为true时可以写目录,先说一下这个参数的含义$recursive用来循环创建目录。什么意思呢,当false时只能创建1级目录,即目录连接符最后的一个目录。而当true时是可以创建多级目录至到最后一个目录。列如./a/b/c当abc都不存在时,会通过系统函数mkdir循环创建目录,abc都会被创建,但若为false会因为走到a处目录不存在,则不回去创建最后一个c。

但是第一个mkdir即使为true却也没有创建1?目录,这里我们从php内部mkdir执行情况和 系统mkdir执行情况来探究。

2.1.1PHP_FUNTCION(mkdir)

PHP内调过程如下图:

ee0fbc19775bf5f22ae625b8cd23654b.png

我们在出现分支的地方细分

/php-src/main/streams/plain_wrapper.c

02686edfc29873d59d5cd901f7440282.png

2.1.1.1 $recursive =fasle

其中出现的分支的地方在判断$recursive若是不需要循环创建则直接进入php_mkdir

/php-src/ext/standard/file.c

跟进php_mkdir_ex

d542afbc9a44f1efcd48ffc7f056cbb2.png

首先会检查open_basedir,接着会进入VCWD_MKDIR,VCWD_MKDIR是个宏命令,有三种不同定义:

97f54c63a309cca5be75af1dcf749744.png

在这里我刚开始并没有考虑太多,跟着gdb的流程走,直接执行mkdir,会直接调用系统的_mkdir.mkdir(“./1?/../1″,01411)=-1ENOENT(Nosuch file or directory)

会直接报错。在预料之类,linux系统下mkdir是不允许这样创建目录的,会效验每一层目录的有效性。回到第一次出现分叉的时候。

2.1.1.2 $recursive =true

a471f01ee77aaf6fc712433c2d3eb597.png

这里会进入expand_filepath_with_mode,这里其实很熟悉,之前也是在看路径处理的时候看到过这个函数,它是一个展开函数,会通过递归的方式展开需要被创建的目录。在其过程会先把相对目录和当前脚本执行目录评价起来,若是绝对目录则忽略.

其中我们的相对目录为 ./1?/../1会变成/var/www/html/WordPress/wp-content/themes/4/5/6/./1?/../1

当前我所在的目录为/var/www/html/WordPress/wp-content/themes/4/5/6

然后通过递归的方式 去掉../,./,//.并且对应目录前移,会变成/var/www/html/WordPress/wp-content/themes/4/5/6/1

然后在传递给系统的mkdir函数。

在这个函数里面存在win32 和linux的不同分支,但在具体处理之前win32判断了目录名不能存在*,?

e810c86ca26cc61e92e3ce9e08cb9ab8.png

注意一下此处!

附上strace ,也是验证上诉分析过程

1. mkdir(“/var/www/html/WordPress/wp-content/themes/4/5/6/1″,01411)=0

2.1.2 Mkdir In Linux

在linux中单纯的mkdir是会层层验证目录,而后在创建一级目录。mkdir 也可以带参 -p,代表系统层面循环的创建目录。

当执行mkdir -p 时 :

strace-f-e trace=mkdir mkdir-p./1?/../1

mkdir(“1?”,0777)=0

mkdir(“1″,0777)=0

我们能看到它并不像php内部那样,展开而后处理 。它会层层按照输入的目录创建。

2.2 window&& PHP 7.0.12

这里是我为什么要探究的一个重要问题点所在,在前面我提到的那篇文章中作者在window下实验当$recursive为false才能创建成功,正好是反着的。作者的解释的false的时候不会去层层判断,但是真的是这样吗?

而后我也做了一个验证性的实验,在window上用 php 5.6做了这个测试,但是结果让我疑惑了,无论在false还是true的情况都不会创建目录.而且报错也很有意思,在false的情况下报错 no error 但是就是无法创建。在true的情况下报错 invaild path。

难道是php-cli 问题?我又用cgi测了一遍,发现同样是这样。有意思,而后我通过邮件联系了那篇文章作者,询问其版本号。很快,得到了他的答复,php-7.0.12。

于是下载php-7.0.12源码 重新编译加debug,此处省略1000字…

在编译完成后我迫不及待的试了一下,同样如此和我的php5.6 一摸一样,无论在cli 模式 或者 cgi 模式下都是无法复现作者文中的情况。这到底问题出在哪呢?

先调了再说,VS调试php 网上基本上没有详细的介绍,有的都是Vscode。我不知道如何启动并调试,只好想了个attach的办法。在mkdir前面写上sleep(10),但是这样做,其实是有一点鸡肋的,php内核初始化过程你其实抓不到的,但是用在这里够了,还是在php_plain_files_mkdir这个地方下断,刷新页面,attach到启动的php-cgi 上。

2.2.1PHP_FUNCTION(mkdir)

2.2.1.1 $recursive== false

还是先分析false的情况,前面都一样,不同的是在php_mkdir_ex中VCWD_MKDIR调用的函数不一样

这次走到不一样的调用上

跟进virtual_mkdir

e1720ec9eb244db9d1250995789bd0a6.png

同样调用了virtual_file_ex,前面有一点没提到,在expand展开路径的过程中最后其实也是进入的这个函数,前面说过在处理的过程中若是win32的情况会判断路径存不存在*,?.若是存在则会直接返回1,不会进入后面写路径。为什么那篇文章的作者会在false的情况下写成功呢?

2.2.1.1 $recursive== true

这里前面说过这里会进行expand过程,但是同样会判断路径名中存不存在*,?,会报错 Invaild Path。

2.2.2 mkdir inwindow

这里因为没有都没有执行到写目录。此处我们还无法探究window系统mkdir 函数是如何执行的。

三、程安全与非线程安全

重新梳理一下,现在是三种不一样的情况:

linux /true 可写

window/7.0.12 :

1. false 可写

2. true/false 都不可写

window出现了两种情况。仔细在走一遍window/false的情况,现在我唯一没有考虑到是VCWD_MKDIR选择情况。前面都是跟着调试流程走的,这是唯一可能出现分叉的地方,重新看一下它的两种种宏定义:

若非那片文章作者,是走的第二个define,于是我把第一个define先注释掉了,换上了第二个define,再重新编译一边,结果竟然出现了和那篇作者一样的情况。但是这里有一个小小不同,写入的目录是相对于php-cgi.exe解释器的,不是相对于WWW的网站根目录下的,当你看了下面的分析以后,应该会给你一个答案,那么很显然问题现在出现在VIRTUAL_DIR定义的情况,在它没有定义的情况下,才会走到第二个define,我看看VIRTUAL_DIR

是在哪被定义的

/php-src/Zend/zend_virtual_cwd.h

de578fd0a78aff5875cfeeceafc881dc.png

熟悉php内核的朋友不会陌生ZTS,这是php 线程安全的标志。用来应对那些使用线程来处理并发请求的Web服务器,列如window下的IIS,worker_mpm模式下的apahce,生活在线程里面的php需要考虑线程间的读写同时也要保证线程间是安全,所以php需要自己提供ZTS层来管理线程间的操作。当定义了ZTS时候,就也同时定义了虚拟目录(VIRTUAL_DIR)。为什么会存在虚拟目录这一说法呢,其实很简单你通过对应virtual_file_ex可以看出来,这个函数的目的在于针对相对路径替换出完整的绝对路径。举很简单的例子,php脚本中写的相对路径,其相对路径一定是针对于该脚本的。在执行脚本的过程中,会进入相应的php 内核里面的php_execute_,其中有一步是VCWD_CHDIR_FILE(filename),这是用来根据要执行的脚本位置去切换当前目录,同样这个宏定义有两个不同的函数,一个是在虚拟目录下切换目录,一个是非线程安全环境下单线程切换目录,不同是在线程安全下切换目录,并不是直接调用系统的_chdir,而是将执行脚本的目录存储在TSRMG中,并给定一个cwd_globals_id,要用的时候再去取,比如创建目录,写文件。因为在多线程环境不能直接修改当前进程的目录,只能预定义一个变量保存各线程的当前目录。可以看到在线程安全的模式下,若是给的相对路径,都会出现当前目录和相对目录的拼接。且都在win32的环境都会检测目录是否包含*,?.

四、结论汇总

我有注意到那篇的文章作者是在window 上用的phpstudy,我也去看了一下phpstudy的是否有7.0.12的版本,存在一个php-7.0.12-nts+Apache确实也是非线程安全。也印证上面我修改php7.0.12 重新编译的结果,但是一个很有趣的东西是,window的系统调用API_mkdir是存在和php内部一样的路径展开功能,即他是允许这样写的./1?/../1可以在当前目录下写入文件夹1的,这和linux不一样,linux的系统函数是逐层判断。在php7.1之后,改变了系统创建目录的API,从_mkdir变成了CreateDirectoryW,但是不变的是还是可以存在路径展开的功能。即便你这样写:@@#@$@#$^%$&&**/@!#@!$!%/../../evil也是可以创建目录evil的,可以算是一个小技巧。但是条件是在window下php为非线程安全模式和PHP_FUNCTION(mkdir)第三个参数为false的情况下是可以这样写目录的。可以算是一个小tips吧。结合相应的应用特点,是可以用到的,而且php版本一般都是非线程安全的,在nginx下都是多进程处理php,即非线程安全。apache只有在worker_mpm才是多线程的,一般也不常用。一般都是prefork_mpm + php_mod,即便是fastcgi也是多进程。利用环境还是比较常见的。

*本文作者:alphalab,转载请注明来自FreeBuf.COM返回搜狐,查看更多

责任编辑:



推荐阅读
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 目录浏览漏洞与目录遍历漏洞的危害及修复方法
    本文讨论了目录浏览漏洞与目录遍历漏洞的危害,包括网站结构暴露、隐秘文件访问等。同时介绍了检测方法,如使用漏洞扫描器和搜索关键词。最后提供了针对常见中间件的修复方式,包括关闭目录浏览功能。对于保护网站安全具有一定的参考价值。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • JavaScript和HTML之间的交互是经由过程事宜完成的。事宜:文档或浏览器窗口中发作的一些特定的交互霎时。能够运用侦听器(或处置惩罚递次来预订事宜),以便事宜发作时实行相应的 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • 用Vue实现的Demo商品管理效果图及实现代码
    本文介绍了一个使用Vue实现的Demo商品管理的效果图及实现代码。 ... [详细]
  • 本文总结了在编写JS代码时,不同浏览器间的兼容性差异,并提供了相应的解决方法。其中包括阻止默认事件的代码示例和猎取兄弟节点的函数。这些方法可以帮助开发者在不同浏览器上实现一致的功能。 ... [详细]
  • {moduleinfo:{card_count:[{count_phone:1,count:1}],search_count:[{count_phone:4 ... [详细]
  • asp中如何嵌入python的简单介绍
    本文目录一览:1、如何在IIS中执行Python脚本 ... [详细]
  • 【爬虫】关于企业信用信息公示系统加速乐最新反爬虫机制
    ( ̄▽ ̄)~又得半夜修仙了,作为一个爬虫小白,花了3天时间写好的程序,才跑了一个月目标网站就更新了,是有点悲催,还是要只有一天的时间重构。升级后网站的层次结构并没有太多变化,表面上 ... [详细]
author-avatar
手机用户2502931567
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有