五指cms代码审计

01-五指CMS

01-看文档

image

image

目的:了解基本架构,看主要目录

02-审计

2.1-路由分析

发现存在入口文件,那么直接从入口文件入手。(那些定义环境什么的基本没用,因为基本不存在可控参数),直接看定义函数load_class()

image

load_class(),加载类函数,根据注释以及大概浏览,发现没有可控参数的传入,直接进入入口加载的文件。根据传入参数application,可以定位到其加载了application.class.php文件

image

application.class.php文件

image

该文件是路由文件,指明了M-模块名,F-文件名,V-方法名

image

看一下setconfig()函数,说是设置路由,通过大概浏览,发现是对定义的路由变量,进行赋值以及过滤,但不是源输入口,所以看下面其他函数

image

下面有一个run()函数,对应着入口文件index.php中调用的函数,是理清路由的重点。

image

一开始调用了load_file()函数且默认传入参数为空,定位到函数位置,分析作用:

image

第一个判断,可以基本猜测与管理员有关。通过判断参数_su,来控制admin目录的设置。

image

image

image

找到函数返回值:返回对应类文件。

可以分析出基本路由:入口文件index.php加载application.class.php文件中的类,执行其中的run()方法,方法中通过调用load_file()方法,浏览代码内容后,发现通过传入M、F、V来控制传入对应的文件,进行处理,即MVC中Controller部分。

Model路径:wuzhicms/coreframe/app/$app(M) . $_admin_dir(默认/admin)/$filename(F).php

因为存在_su​参数,来控制访问管理员目录,所以可以得出管理页面访问:index.php?_su=wuzhicms&m=core&f=index

开始审计代码

1-CSRF

image

找到对应路由方法,发现只对提交的参数进行判断,并未对身份进行验证,所以存在CSRF实现任意用户添加image

image

依照这个思路,可以大胆猜测对方在其他存在身份验证的地方也可能没有进行身份验证。

2-目录遍历

搜索敏感函数

全局搜索glob(​,查找是否存在glob()函数的调用。

image

上面代码就是调用了glob()函数,匹配目录,同时使用template​​进行模板渲染,即存在结果返回前段显示

寻找是否参数可控,函数方法乍一看没有传参,实际上这里是类方法,调用了$this->dir​,而$this->dir​在__construct()函数中可控,并且进行了简单过滤

image

怎么触发呢?考虑到是模板,那么访问指定页面传入参数,就会自动触发类中的构造函数。

image

根据路由,构造Payload:index.php?m=template&_su=wuzhicms&f=index&v=listing&dir=.....///.....///.....///.....///.....///.....///

image

  1. 全局搜索敏感危险函数
  2. 寻找存在变量可控的相关函数
  3. 追溯整个调用链,看是否能够结果回显

3-任意文件删除

全局搜索unlink()函数,该函数是PHP中自带的文件删除函数。以下面为例,找到一个自定义方法中调用了unlink()函数,并且未进行过滤操作

image,此时的思路就是去找哪里调用了这个方法,以及参数是否可控

其一:path是从数据库中提取的,我们无法控制,所以不能造成任意文件删除

image

其二:这里的$path​是由$url​控制,而$url​是可控的传入参数,里面调用了remove_xss()函数,过滤xss代码,

image

image

remove_xss()​过滤代码中未对. /​进行过滤,这就意味着可以进行目录遍历,然后实现文件删除

image

分析路由:需要进入else中语句,所以不能传入$id​,直接传入$url

Payload:http://www.testwuzhi.com/index.php?m=attachment&f=index&v=del&url=../1.txt&_su=wuzhicms

image

image

当前目录:

image

也可以通过对应文件找到其页面中的功能点,从而利用抓包修改

image

image

4-sql注入

删除用户交互

删除会员组
分析执行链

根据路由找到对应文件

image

image

找到数据库交互位置,且参数可控,开始追溯整个执行链。

通过db->delete()​追溯到db.class.php文件中的delete()​函数,接着通过master_db->delete()​追溯到mysqli.class.php文件中的delete()​函数。此时发现函数中的sql语句直接拼接,不存在预处理什么的。再追溯到本文件中定义的query()​函数的定义(因为不是直接使用mysql语句,所以只能是自定义的语句)

关于执行过程中,例如array2sql()​函数,暂时不用去分析其作用,如果后面执行链无法输出结果,前面怎么分析都是浪费时间

image

image

分析query()执行函数,里面调用了halt()函数,追溯其作用发现halt()函数是输出结果,依据if判断,可以知道如果sql查询失败了会执行halt,也就是报错信息输出,利用了mysqli_error()可以用于报错注入。

也就是说只要调用了自定义的query()函数,就可能存在报错注入的风险。

image

image

image

分析可能存在报错漏洞,整理执行链:member/admin/group.php中的del()函数**=>**db.class.php文件中array2sql()+master_db->delete(函数)**=>**mysqli.class.php文件中的delete()函数**=>**query()函数​。分析认为是query()会导致报错注入。

分析构造payload

query()函数,想要报错,只需要传入的$sql​语句可以报错即可

image

分析delete()函数,参数$where​是可控的,追溯到$where​变量

image

db.class.php文件中delete()函数,$where​变量可控但是会进行array2sql()函数,现在分析array2sql()函数

image

注释写了是数组转化为sql格式,但是中间有一段str_replace()函数的简单过滤。先写出报错注入的语句,然后根据过滤进行修改

AND updatexml(rand(),concat(0x3a,database(),0x3a),rand()) --+

image

通过分析其过滤了空格和’替空,可以利用+号代替空格。其次$sql​语句的拼接是直接拼接传入的$val,所以不需要其他绕过了

所以payload:AND+updatexml(rand(),concat(0x3a,database(),0x3a),rand())+--+

现在回到入口文件处,分析$where​的传入方式:判断传入的groupid参数是否是数组,这里发现不是的话,会进入else语句,其中的调用db->delete()​,是直接传入我们输入的参数。所以可以实现报错注入

image

最终Payload:+AND+updatexml(rand(),concat(0x3a,database(),0x3a),rand())+--+

image

导出excel

在coreframe/app/pay/admin/index.php文件中,入口代码30-43行:

通过status参数传入$where​变量上$status​变量,该变量未经过详细的过滤,可以作为sql注入的入侵点。接着通过if条件判断,将$status​参数提交的值拼接在

在第88行,通过调用coreframe/app/core/libs/class/db.class.php文件中的get_list()函数,传入$where​变量

因为传入的$where​为非数组变量,所以进过简单过滤直接传入到coreframe/app/core/libs/class/mysqli.class.php文件中的get_list()函数

在coreframe/app/core/libs/class/mysqli.class.php文件中的get_list()函数里,传入的$where​变量,直接拼接为新的$where​变量,并且拼接到$sql​语句中,利用自定义的query()函数执行sql查询

自定义的query()sql查询语句,当查询错误时,会触发halt()函数,在237行该函数中调用了mysqli_error(),导致了报错注入

任意文件写入

file_put_contents()

fwrite()

这里主要用到的是敏感函数回溯。通过seay源代码分析,先找一些主要是可以接收变量的,然后找存在注释的。

image

image

这里找到一对,写入缓存和读取缓存内容,关键是读取缓存内容中使用了include​包含

追溯函数的调用,全局搜索函数,发现有一处函数调用了set_cache,同时参数可控,并且该函数中同时调用了get_cache()

image​​

直接根据路由分析出Payload:https://www.testwuzhi.com/index.php?m=attachment&f=index&v=ueditor&_su=wuzhicms&submit=XXX&setting=<?php%20phpinfo();?>

可以看到ueditor函数中,如果没有submit写入,就会调用get_cache函数,而这个函数式用来读取缓存内容的。image

思考

在sql注入处,分析出因为query()函数的定义,导致的报错注入,那么是不是涉及到调用query()函数的都存在报错注入?