前言
距离上次编写文章已经过了很久很久... 本篇文章是从去年七月份挖掘后提交CNVD
通过, 并等待厂商修复漏洞后, 时隔9月, 才分享出来的. 因为笔者在其中挖掘到多处前台漏洞, 并成功申请了CNVD
编号.
那么回头望首, 今天我们所分析的系统是HadSky
, 该系统具有一定的安全性, 几乎站点从外部接收上采用的全局性白名单过滤, 那么接下来聊一下笔者与该CMS之间的小故事.
本次分析是HadSky v7.12.10.20230702 Latest (2023年7月2日)
版本, 笔者会在文末附上该版本的链接, 方便笔者进行复盘.
官网安装渠道:
https://www.hadsky.com/hadsky-download.html
该框架使用puyuetian
轻量级框架进行开发, 笔者会在此分析出框架的运行原理, 以及分析过程中所遇到的安全隐患问题.
安装口存在的安全隐患&&开发模式分析
安装部分给大家一个小小的引子.
这里“创始人信息填写”则是后台的账号密码设置, 这里默认填写上很明显是存在一些安全隐患的, 但并不是什么大的问题, 这里不细说.
下面有一个“云服务信息初始化”, 这个点在我当前测试时功能已经失效了, 但安装完毕后可以正常使用, 后面该功能模块会引出高危漏洞, 这里先给大家提前剧透一下.
安全的密钥
WEB 界面其实并没有什么发现, 而重点关注的代码层面到底干了什么, 我们查看\install\phpscript\install.php
文件会发现该站点在数据库中存在两种密钥, 如图:
通过观察代码, 我们可以知道该数据库的password
字段只经过了一次md5加密, 但其实第124行的 $s_t 变量则类似于数据库安全中的“盐”, 它在后面的COOKIE用户身份校验中起了非常大的作用. 到下面MVC分析我们会进行刨析. 下面可能出现漏洞的地方则是CreateRandomString
方法.
但CreateRandomString
函数则使用了非常安全的rand()
函数, 以避免了Dz
论坛某版本曾爆出的key泄露情况.
参考链接:
https://www.jianshu.com/p/592d336adfe3
所以这里并不存在漏洞, 接下来就是重点了.
配置项存放形式
通过这个信息我们可以得到两点:
该站点的配置信息都在数据库里, 则是
$mysql_prefix . 'set'
这张表
setname
是配置项, 而setvalue
则是配置项的值. 这里可以简单理解为一个简单的键值对.
该站点存在第二个密钥, 该密钥目前的用途不知, 但与官方网站是存在握手行为的.
MVC 路由 && 框架运行原理分析
那么聊完一些有的没的, 下面我们从index.php
开始分析, 深入浅出一些开发上存在缺陷的code, 并理解它的网站架构.
22行之前都是一些版本定义, debug默认开启, 安装检测. 我们重点关注22行引入的puyuetian.php
文件.
这里定义了一个全局数据$_G, 以存放WEB应用系统层面的信息, 并且19~31行的if判断中, 不允许直接访问puyuetian.php
文件, 接下来我们继续跟进35行的puyuetian/vars.php
文件.
站点全局变量存放
这里重点关注上面的$_G['STRING']
变量的定义(安全机制), 以及下面$_G['SYSTEM']['CLIENTIP'] = $IPaddress;
可以进行IP伪造, 并且没有过滤机制,$_G['SYSTEM']['REFERER'] = $_SERVER['HTTP_REFERER'];
未经任何判断, 直接放入$_G['SYSTEM']
中, 这里可能也会触发漏洞, 特意留意一下, 那么接下来回到puyuetian.php
文件中, 继续往下读取.
后面引入了一个function.php
文件, 这里都是一些函数的定义, 等分析到再看, 17行的firewall.php
看似是一个防火墙配置, 但该站点默认未开启, 所以不用分析. 回到puyuetian.php
文件继续分析.
47~55行禁止了/index.php/.../...
的写法, 那么接着往下看.
全局GET过滤 (*)
从这里我们可以发现,$_GET
的所有内容全部经过了Cstr
函数处理, 随后都给了$G
, 这里Cstr
函数的过滤还是比较严格的, 强制白名单只允许A-Za-z0-9_
字符, 不允许其他任何字符出现, 否则函数返回false, 算是一个全局的外部GET参数过滤, 那么接着往下看.
IP黑名单 (被修复的XFF头)
ips.php
文件的主要内容则是通过\puyuetian\ips\config.hst
的IP设置来进行某某IP, 这里看到一个点, 而是$_G['SYSTEM']['CLIENTIP']
之前是没有任何过滤的, 但包含了ips.php
后则有了htmlspecialchars + strip_tags
过滤, 并且使用htmlspecialchars
的ENT_QUOTES
模式, 完全过滤了一些引号, 尖括号等攻击语句.
所以, 这里本应该存在的XFF
头注入, 目前被修复了, 只留下了$_SERVER[REFERER]
目前还没有被修复.
缓存机制
这里缓存机制的配置比较简单, 若发现/cache/html/
目录中有关于当前$_G[SYSTEM][LOCATION]
的缓存, 则只使用file_get_contents进行读取内容, 缓存文件的末尾会有CacheFile
的注释结尾.
MySQL机制 (*)
80以及81行的代码比较简单, 对$_G进行数据库的定义, 以及定义Data类, 我们下面仔细分析一下第82行做的事情.
puyuetian/mysql/install.php
文件的前半部分主要是增加了mysql_query
方法, 但是从这里可以看出来$_G[PDO]
未经过任何过滤直接调用了PDO::query
方法,mysql_query && sqlQuery
方法使用不当会造成SQL注入问题.
第19行因为$_G[PDO]
是PDO
类的实例, 而PDO
默认支持堆叠形式, 这也是一个需要留意的点.
再往下面的$_G[SET]
则是我们的pk_set
表中的各个配置项了.
那么知道上半部分在做什么事情之后, 我们来分析下半部分所做的事情.
再往下的核心思想则是, 将本数据库下的所有以pk_
打头的表, 全部实例化一个Data
对象, 并特别指明了Data
对象的table
成员属性就是表名. 随后依次存入到$_G[TABLE]
中去.
Data 类的下可注入的函数 (!)
为什么说该系统比较安全, 其实也有这里Data类的死亡过滤原因, 我们来仔细分析一下Data
类做了一些什么事情.
看一下它的getSql
方法:
可以看到这个if分支中, 只有$field
传入字符串, 并且$str
未传入任何内容才不会经过mysqlstr
函数的过滤, 所以这里的利用条件是比较苛刻的. 我们再看一下其他函数的定义.
这里其他任何函数, 几乎都是封装了getSql
方法, 要么调用getSql
方法时只传递了一个字符串, 要么调用了自定义的mysql_query
方法, 否则, 几乎都无法注入成功.
puyuetian/mysql/notfound.php
中没什么框架核心思想||安全方面的问题, 就不细说了.
业务逻辑 (用户校验核心文件)
/puyuetian/ext/preload.php
文件只需要着重注意一下$_G[TEMP\LATE]
变量即可, 因为它与后面的模板输出环环相扣.
/puyuetian/ext/function.php
又引入了一系列业务逻辑函数, 例如: 发帖, 消息回复等函数.
/puyuetian/ext/uia.php
这里不介绍, 因为在文章下文会详细描述该文件所导致的问题缺陷.
查看/puyuetian/ext/normal.php
这里其实用来记录你的$_SERVER[REQUEST_URI]
是否存在恶意语句等, 这里默认值为0, 所以一带而过.
接下来我们看一下/puyuetian/ext/loadapps.php
主要应用于插件是否开启, 这里我们刚安装的系统可以发现自带了这么多插件, 并且app_filesmanager_load
,app_mysqlmanager_load
等插件都是默认开启的.
INDEX 层路由分析 (*)
安全的外部路由
跌跌撞撞, 终于到达了我们路由分析的点, 这一部分是整个运行原理分析中最重要的一部分了, 再努努力, 把代码读下去.
这里我们发现了$_G['GET']['C']
, 用于接收外部c参数, 也就是GET请求的c. 如果外部参数c存在, 那么则直接使用, 但是我们知道, $_G的所有参数在全局GET过滤 (*)
分析部分已经经过Cstr
所处理, 故, 该站点的路由安全是没问题的, 因为$_G['STRING']['SAFECHARS']
并不包含..
等字符, 通过白名单的手段白名单过滤了目录跳跃漏洞.
而不存在外部的参数c时, 在正常业务逻辑中, 数据库中的select * from pk_set where setname like 'CHKCSRFPAGES';
, 默认返回了list
. 那么我们继续往下看.
未打开的CSRF开关
这里的CSRF
配置项默认被关闭了, 也不知道为何关闭该功能, 它里面的$_G['CHKCSRFVAL']
大家可能一脸懵, 它是在业务逻辑 (用户校验核心文件)
中笔者忽略的UIA.PHP
文件, 后面在安全漏洞部分会详细说明.
XSS可能利用点
在接下来就会包含我们的./phpscript/$_GET[c].php
文件了, 只是不能跳路径罢了. 因为我们$G['GET']['C']
默认是list
, 我们跟进list.php
给大家看一下.
可以看到, 已经进入到正常的业务逻辑当中去了, 这里成功理解了我们外部参数C
的作用. 我们继续多个phpscript
目录下的文件中的文件末尾的代码, 看一下模板解析部分是如何处理的, 如图:
在本张图片的分析中, 我们会发现, 每个控制器下的最后一行代码的处理方式都不同, 有调用PkPopUp
函数返回弹窗的, 有返回JSON
的, 有直接exit
暂停程序运行的, 有经过temp\late
函数处理的.
在这里笔者提到一些点, 可能会存在安全隐患.
JsonData
函数中, 使用了json_encode
函数进行返回, 当函数第三个参数为null
时直接返回json_decode
出来的数据, 若将该函数返回结果直接抛出到浏览器身上, 可能会存在XSS
漏洞,
ExitJson
中, 若函数第三个参数指定了false, 则不会输出Content-Type: application/json
前缀, 这里可能也会在处理上产生XSS
漏洞.
PkPopUp
函数中直接拼接了JS
代码, 也就是我们的可控点若是在该函数中, 并且传入的数据没有被正确消毒, 即可产生一个XSS
漏洞.
当然, 笔者会在本篇文章的安全部分详细介绍出来一些实例.