本环境采用phpstudy一键搭建

环境配置

将压缩包解压到www目录下,因为压缩包内有一个名为runtime的空文件夹,可能解压时会被忽略,注意没有它会报错

在phpstudy中新建一个网站,我们需要在这个/www/zzzphp下新建一个网站。因为这个cms会有一个目录修改策略将zzzphp修改成zzzp6p,会影响我们的后续操作,所以直接在/zzzphp下建站就可以避免

然后就可以访问/www/zzzphp/install页面进行安装

一路默认到基本信息配置

点击安装完成就好了

代码审计

php涉及到删除的函数是unlink,他会返回true或false判断是否删除成功

我们搜索那些地方用到了该函数,于是发现了两个比较有嫌疑的函数

dellfiles丢弃

一、函数功能

这个名为delfiles的函数用于删除指定路径下具有特定扩展名的文件。

二、参数说明

  • $path:表示要进行操作的目录路径。
  • $ext:表示要删除的文件的扩展名。

三、代码详解

  1. if (!is_dir( $path ) ) return FALSE;
    • 检查传入的$path是否是一个有效的目录。如果不是目录,则返回FALSE,表示函数执行失败。
  2. if ( substr( $path, strlen( $path ) - 1 )!= '/' ) return NULL;
    • 检查目录路径的末尾是否有斜杠(/)。如果没有,则返回NULL。这一步通常是为了确保路径格式的一致性,方便后续的文件操作。
  3. $handle = opendir( $path );
    • 使用opendir函数打开指定的目录,并将返回的目录句柄存储在$handle变量中。
  4. while ( false!== ( $file = readdir( $handle ) ) ) {... }
    • 使用readdir函数遍历目录中的文件和子目录。每次循环都会读取一个目录项,并将其存储在$file变量中。只要readdir函数没有返回false,循环就会继续执行。
  5. if ( $file!= '.' && $file!= '..' ) {... }
    • 排除当前目录(.)和父目录(..)。在遍历目录时,这两个特殊的目录项通常需要被排除,因为它们不是普通的文件或子目录。
  6. $path2 = $path. $file;
    • 构建完整的文件路径,将当前遍历到的文件与传入的目录路径拼接起来。
  7. if ( preg_match( "/\.(". $ext. ")$/i", $file ) ) {... }
    • 使用正则表达式preg_match函数检查当前文件的名称是否以指定的扩展名结尾。正则表达式中的/\.(". $ext. ")$/i表示匹配以点(.)和传入的扩展名$ext结尾的文件名称,不区分大小写(i标志)。
  8. unlink( $path2 );
    • 如果文件符合扩展名要求,则使用unlink函数删除该文件。

总结:这是一个可以删除文件的函数,但他会有一个拓展名匹配验证,可能出现某些拓展名删除不了的情况

我们去查找他的用法,但发现只在一处调用,且皆固定拓展名,于是放弃跟踪

del_file保留

一、函数功能概述

这个名为del_file的函数主要用于尝试删除给定的文件。如果文件不能直接删除,它会尝试将文件重命名为一个随机名称。同时,对于包含特定字符串和特定扩展名的文件有特殊的处理逻辑。

二、参数说明

  • $file:表示要进行操作的文件路径。

三、代码逐步分析

  1. if (is_null($file)) return FALSE;
    • 首先检查传入的文件路径参数$file是否为null。如果是,直接返回FALSE,表示无法进行后续操作。
  2. $file = is_file($file)? $file : $_SERVER['DOCUMENT_ROOT']. $file;
    • 判断$file所代表的路径是否是一个有效的文件。如果是,则保持$file不变;如果不是,将服务器根目录(通过$_SERVER['DOCUMENT_ROOT']获取)与传入的路径拼接起来,得到一个新的文件路径。
  3. if (is_file($file)) {...}
    • 再次确认经过处理后的路径是否确实指向一个文件。如果是,进入以下处理逻辑。
  4. if (ifstrin($file, 'runtime')) {...}
    • 检查文件路径中是否包含字符串'runtime'。如果包含,使用unlink函数直接删除该文件。
  5. else {...}
    • 如果文件路径不包含'runtime',执行以下逻辑。
    • $ext = file_ext($file);:获取文件的扩展名。
    • if (in_array($ext, array('php', 'db', 'mdb', 'tpl'))) return FALSE;:检查文件扩展名是否在指定的扩展名数组中。如果是,函数返回FALSE,表示不进行删除操作。
    • if (!unlink($file)) {...}:尝试使用unlink函数删除文件。如果删除失败,执行以下逻辑。
    • $r = @rename($file, randname());:使用rename函数尝试将文件重命名为一个随机名称(通过randname()函数生成)。@符号用于抑制可能出现的错误信息。

总结:存在部分后缀过滤不能删,删除失败会被强制重命名,但可以尝试删除某些js文件或网站txt文件

查找那些地方调用过他,在delfile函数中使用到

其中的path通过post传递,再传递给file变量供del_file函数使用。在此函数中也存在校验,文件必须来自upload等三个目录下才能删除。跟踪delfile

通过act=delfile调用的delfile文件,act通过get传参得来

整理信息

实现此cms文件删除的逻辑是访问save.php,传递act值(get传递)调用delfile函数,添加path(post传递)为del_file函数服务,最终实现删除。

如果看不懂可以反过来查看整个逻辑,即从函数调用-函数声明

拓展

具有mcv架构源码的执行逻辑是直接找到关于删除函数的文件,构造url调用它就可以了,可以参考前几期的梦想cms。但是本cms仅仅是正常的,类似平常写的代码。我们则需要找到哪里调用了这个删除函数,需要构造url成它(哪里调用的地方)才行。

操作

因为save.php在后台,我们先登陆进去,遇到验证码查看不清楚的话,右键打开成新页面就好了

在/zzzphp目录下新建一个1.txt

添加一句输出方便得到路径

构造url,path参数必须携带四组目录中的一种不然不会有回显

知道路径后再次构造删除掉我们写入的1.txt

已经成功删除掉,我就不贴图了。但并不是所有文件都可以删,请牢记!