在index.php文件中包含 coreframe 框架下core.php文件并调用load_class加载
然后在application.class.php 文件、core.php 框架核心文件
调用了load_class加载类。然后通过load_class()类实例化wuzhi_application类、
通过 load_class() 类实例化 WUZHI_application 类。
实例化 WUZHI_application 类调用下面构造方法
最后调用 WUZHI_application 类下 run() 方法获取路由信息
也可以通过抓包去查看他的路由访问规律
定位在文件 /coreframe/app/member/admin/group.php
下的 del() 函数
在代码133行判断是否传入groupid参数且参数是否为空,条件满足在134行代码中判断传入的groupid
是否为数组。
跟进delete()函数,在db.class.php中找到对应函数。
而下面223行的delete()函数才是真正去操作数据库的函数,我们跟进这个delete(),这个函数是
在这个过程中我们传入的sql语句并没有进行任何过滤就直接拼接到$sql并通过query()执行,所以这里存
在SQL注入漏洞。
在query()函数中如果出现SQL语句错误会直接爆出SQL语句,所以这里存在报错注入。
使用工具或者全局搜索file_put_contents函数
通过Seay的审计结果,翻找到一处可能存在任意文件写入的地方。
通过翻找发现一处可以直接控制 $data 的文件:
oreframe/app/attachment/admin/index.php
这里的 ueditor() 函数调用了 set_cache() 并且这里的 $GLOBALS['setting'] 参数可控。
假如不设置submit,则会通过 get_cache() 读取缓存文件内容。
通过前面路由分析以及上面SQL注入的路由,我们可以构造出调用ueditor()的路由:
/index.php?
m=attachment&f=index&v=ueditor&_su=wuzhicms&submit=XXX&setting=payload
文件已被成功写入到缓存文件中
读取缓存中文件内容
在测试功能点: 系统设置 => 权限管理 => 添加管理员 处我们点击提交
通过提示我们了解到必须存在一个前台账户,然后才可以将前台账户绑定为系统管理员,而前台账户是可
以随便注册的。
/index.php?m=core&f=power&v=add&&_su=wuzhicms
通过路由找到对应代码
从最上面的注解就可以看出 add() 函数是用于添加管理员的,35-37行代码判断是否点击提交操作,然
后判断用户名是否为空,不为空则将传入的 username 值赋值给 $username ,
在代码38行处根据用户名
从数据库中取出前台账户,如果该前台用户不存在则会提示如下内容,也就是我们上面的提示,在40行
代码判断该用户是否已经是管理员,而44-51行代码是判断添加管理员时是否设置密码,这部分操作在上
面部分我们添加管理员的时候也可以看得出来,在代码54行将我们的内容插入到数据库中。
/index.php?m=core&f=power&v=add&&_su=wuzhicms
coreframe/app/core/admin/power.php
通过全局搜索 glob 函数发现一处功能点处可能存在目录遍历,这里的 glob() 函数中 $dir 参数可控,
上面代码29行通过 template() 函数渲染 listing.tpl.php 文件,将我们目录下的内容遍历出来并显
示在前端页面。
添加我们所需要的 dir 参数,由于上面的代码存在过滤且这里仅仅是将传入的内容替换为空而且只替
换一次,通过 ...../// 的形式来绕过该处的过滤实现目录遍历
全局搜索危险函数 unlink() ,发现该处存在一个 my_unlink() 函数调用了 unlink() 函数并
且通过注解也可以大致了解到该函数是用来删除附件的,这里的 $path 参数传递需要删除的文件。
接下来我们的思路是两点:一是看哪些功能点调用了 my_unlink() ,二是看调用处 $path 是否可以控
制。
通过上面的思路,我们 Ctrl+鼠标左键 点击该函数,发现有三处功能点调用了 my_unlink() 函数。
这里我们发现其中一处 del() 函数调用了该功能点进行文件删除.
上面的176、177行代码需要我们传入两个参数id、url,这里需要看看177行代码处的remove_xs()函数中
进行了哪些过滤。
通过上面一系列的过滤最终返回我们过滤后的内容。
我们再接着看代码,我们如果传入id参数,这里的path参数是从数据库中拿到的,我们不好去控制
如果不传入id参数则会走下面的else,在207行代码处判断我们传入的 path 是否在数据库中,如果不在就
会进入208行代码,对我们传入的 $path 进行删除。
漏洞复现:
我们看到数据包中的路由其实与我们分析的代码是一样的.
GET /www/index.php?v=del&url=qr_image/mobile.png&m=attachment&f=index&_su=wuzhicms&_menuid=29&_submenuid=52 HTTP/1.1
Host: www.wuzhicms.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://www.wuzhicms.com/www/index.php?dir=.%2Fqr_image&m=attachment&f=index&v=dir&_su=wuzhicms&_menuid=29&_submenuid=52
Cookie: PHPSESSID=k652a136tnltn7n1obr88jo5h6; fbp_uid=QO%2BEiNPcY77ztIj3VwL07Q%3D%3D; fbp_username=qPFUCdQQfkRNinLQXE3qZA%3D%3D; fbp_wz_name=CfApQiRkuXByfw7AqHDG5A%3D%3D; fbp_siteid=MCakL%2FitF2Mz8zst4dyFvQ%3D%3D; fbp_userkeys=u0CzDoL6HLYQwPI%2Ffgo2bg%3D%3D
Upgrade-Insecure-Requests: 1
在更目录下创建一个 ceshi.txt 用于测试
成功将test.txt删除。
先直接全局搜索select
通过全局搜索 在这个地方 发现调用了这个函数
然后查看传递的参数
主要传递的是55行这个$where参数 传到了函数
继续看$keywords 因为这个没调用函数 但是调用了一个$GLOBALS来获取值
这里就又要介绍下$GLOBALS
在这个文件看见了广告管理的注释
那我就去后台找这个功能了
还是在搜索select的时候 发现在mysql.class文件下有一个函数里面有select 并且后面的拼接也没有任何的过滤
然后我们搜索哪里调用了这个函数
首先是在api目录下的sms_check文件中发现调用了get_one函数 并且参数是通过前面的$code拼接
我们可以看到code 先是通过$GLOBALS来获取参数param的值 从前面的介绍可以知道 $GLOBALS是可以获取post get的值 这个文件前面没有定义param变量 那么 这个param应该就是post 或者get 就是我们可控的 这也是导致注入的点
code还通过strip_tags() 函数 而这个函数的作用是剥去html标签 应该是过滤xss吧大概
之后就直接传入了函数 继续更进函数 因为这个文件前面还引入了db类
这个函数应该是调用的这个文件里面的
来到这个文件可以看到这个get-one函数里面 还调用了一个array2sql函数来处理$where
那先来看看这个函数的作用
可以看到这个函数是用来过滤的
如果是数组 这进入if 把括号 单引号这些过滤掉
不是则走else 过滤 %20 %27
然后返回参数
但是二次编码的就不一样了 因为web服务器只解码一次
如果是二次编码这里的else过滤就起效果
return 调用的get_one 则是最开始看见的mysql.class文件里面了
下面就可以开始直接构造payload了 这里通过代码分析可以看到是单引号闭合
从前面两个分析 我发现的注入的地方就存在两个函数中get_list get_one
然后直接全局搜索这两个函数 看看什么地方调用
可以看到 在copyfrom.php中listing函数下调用了这个函数,主要传进去的就一个$where 和 $page
我们看看where 在if内部 想要进入if 就需要通过GLOBALS获取到keywords
相当于就要传参,这里也可以看出 闭合方式是百分号单引号 %'
我们在来到mysql文件中定义的这个函数 也可以看到 是对where没有过滤处理的
那么 有了前面的基础 直接来构造payload:
http://192.168.1.7/wuzhicms/index.php?m=core&f=copyfrom&v=listing&_su=wuzhicms&keywords=%27
源代码分析:/wuzhicms/coreframe/app/attachment/admin/index.php
当用户传递动态函数sumit时,web程序会引用set_cache()方法,其中M为当前模块名attachment,继续跟进:
$cache_path为web服务器的缓存路径目录,$filename为缓存文件名,然后通过file_put_contents方法,毫无过滤地将$data写入到缓存文件中,那么如果该缓存文件被包含,则可以造成RCE的问题。
当对象创建时会调用魔术方法__construct(),然后会引入get_cache()方法,继续分析:get_cache()方法会包含并返回缓存文件,那么就可以造成RCE的问题。
看一看代码,没有做任何处理和限制。
和zzcms2021利用方法是一样的。
具体可以参考https://xz.aliyun.com/t/10432
www\api\uc.php中:
通过传参可以调用uc_note类的任意方法:
https://www.sohu.com/a/529635812_99907709
https://blog.csdn.net/hackzkaq/article/details/125595758
https://www.jianshu.com/p/12d28873d4ee