IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> 友点 CMS V9.1 后台登录绕过 GetShell -> 正文阅读

[PHP知识库]友点 CMS V9.1 后台登录绕过 GetShell

声明:本公众号大部分文章来黑白之间安全团队成员的实战经验以及学习积累,文章内公布的漏洞或者脚本都来自互联网,未经授权,严禁转载。
请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与文章作者和本团队无关。

前言

友点 CMS V9.1 后台登录绕过 GetShell ,攻击者可无需任意权限即可登录后台并 getshell

漏洞影响

youdiancms <=9.1

漏洞细节

漏洞流程

  • 验证码可设置session
  • session(AdminGroupID==1) 超级管理员
  • 后台模板修改代码执行

任意 session 获取

站在前人的肩膀上,看到 App/Lib/Action/BaseAction.class.php ,217 行 verifyCode 函数

function verifyCode(){
    $length = $_GET['length'];        //长度
    $mode = $_GET['mode'];          //模式
    $type = $_GET['type'];              //图像类型
    $width = $_GET['width'];           //宽度
    $height = $_GET['height'];        //高度
    $verifyName = $_GET['verify'];  //验证码session名称
    import("ORG.Util.Image");
    Image::buildImageVerify($length, $mode, $type, $width, $height, $verifyName);
}

这是一个生成验证码的函数,可以看到,这些参数都是可控的,进入

Image::buildImageVerify($length, $mode, $type, $width, $height, $verifyName);
static function buildImageVerify($length=4, $mode=1, $type='png', $width=48, $height=22, $verifyName='verify') {
    import('ORG.Util.StringEx');
    $randval = StringEx::randString($length, $mode);
    $_SESSION[$verifyName] = md5($randval);
    ...

看到这句

$_SESSION[$verifyName] = md5($randval);

$verifyName 是可控的,也就是说我们可以控制 $_SESSION 的键,后面的 md5 值暂时不考虑

接下来我们找可以利用的地方,我们看到管理员登陆的地方 App/Lib/Action/AdminBaseAction.class.php

function _initialize(){
    $mName = strtolower(ACTION_NAME);
    $NoCheckAction = array('login', 'verify','checklogin','showcode','logout'); //免登录验证模块
    if( !$this->isLogin() && !in_array($mName, $NoCheckAction)){ //没有登录,将返回登录页面
        $this->redirect("Public/login");
    }

    if( !$this->checkPurview() ){ //没有登录,将返回网站首页
        $this->redirect("Public/welcome");
    }

这是初始化的位置,需要注意两个地方

$this->isLogin()
$this->checkPurview()

想要得到登陆的状态,就需要绕过这两个方法,先来看 $this->isLogin()

function isLogin(){
    $b = session("?AdminID") && session("?AdminName");
    return $b;
}

这里很简单,只要存在 AdminID 和 AdminName 这两个session就可以,我们之前找到的地方就可以赋值

再来看 $this->checkPurview()

function checkPurview(){
    $gid = session('AdminGroupID');
    if( $gid == 1 ) return true;  //超级管理员拥有所有权限

这里首先要有一个 session AdminGroupID,这个容易满足,也可以直接赋值,但是只能获得普通的登陆权限,想要获得超级管理员的权限,就需要 $gid == 1 ,看似没有办法完成,但是注意到这里是弱比较,而之前赋值的时候,是用的 md5 值,我们只需要得到一个 md5,满足第一个字符是 1 ,第二个字符不是数字,就可以完成

回到一开始的漏洞位置,我们看看如何生成 md5 值

import('ORG.Util.StringEx');
$randval = StringEx::randString($length, $mode);
$_SESSION[$verifyName] = md5($randval);

我们看到这里应该是生成的随机字符串,传入的 $length$mode 是我们可控的,进入该函数

App/Core/Extend/Library/ORG/Util/StringEx.class.php 的 128 行

static public function randString($len=6,$type='',$addChars='') {
    $str ='';
    switch($type) {
        case 0:
            $chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.$addChars;
            break;
        case 1:
            $chars= str_repeat('0123456789',3);
            break;
        case 2:
            $chars='ABCDEFGHIJKLMNOPQRSTUVWXYZ'.$addChars;
            break;
        case 3:
            $chars='abcdefghijklmnopqrstuvwxyz'.$addChars;
            break;
        case 4:
            $chars = "们以我到...".$addChars;
            break;
        default :
            // 默认去掉了容易混淆的字符oOLl和数字01,要添加请使用addChars参数
            $chars='ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789'.$addChars;
            break;
    }
    if($len>10 ) {//位数过长重复字符串一定次数
        $chars= $type==1? str_repeat($chars,$len) : str_repeat($chars,5);
    }
    if($type!=4) {
        $chars   =   str_shuffle($chars);
        $str     =   substr($chars,0,$len);
    }else{
        // 中文随机字
        for($i=0;$i<$len;$i++){
          $str.= self::msubstr($chars, floor(mt_rand(0,mb_strlen($chars,'utf-8')-1)),1,'utf-8',false);
        }
    }
    return $str;
}

可以看到,这里是利用 $type 选择相应的字符串,然后用 $len 控制长度,最后生成字符串

满足条件的 md5 值,1 - 100 以内就有 3个 ,为了减少爆破的次数,我们就直接选择数字以及长度设置成两位

后台getshell

后台 getshell 可以利用修改模板

App/Lib/Action/Admin/TemplateAction.class.php 的 68 行 saveModify 方法

function saveModify(){
    header("Content-Type:text/html; charset=utf-8");
    $ThemeName = C('HOME_DEFAULT_THEME');
    $_POST['FileName'] = YdInput::checkFileName( $_POST['FileName'] );
    $FullFileName = TMPL_PATH.'Home/'.$ThemeName.'/'.ltrim($_POST['FileName'],'/');
    if( !$this->isValidTplFile($FullFileName)){
        $this->ajaxReturn(null, '无效模板文件!' , 0);
    }
    //实体解码
    $FileContent = htmlspecialchars_decode($_POST['FileContent']); 
    if (get_magic_quotes_gpc()) {
        $FileContent = stripslashes($FileContent);
    }		
    $b = file_put_contents($FullFileName, $FileContent);

首先获取文件名,然后将 post 上来的 content 写入模板文件,过程较为简单

Exp

https://github.com/N0puple/poc-set/tree/main/YouDian%20CMS%20Auth%20Bypass%20and%20RCE

总结

测试版本为 9.0,很多文章中写的利用短标签绕过过滤来getshell,这里没有必要,就没有去用,应该是 9.1 才有的过滤,这个前面的绕过漏洞是很有意思的,后面的后台 getshell 写的有点草率,尤其是exp中,直接覆盖了整个模板文件,真实的渗透测试中还是不要这么干,毕竟会影响到正常的业务了。

参考

  • https://forum.butian.net/share/132

欢迎大家关注公众号

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-12-02 16:33:16  更:2021-12-02 16:34:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 20:19:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计