【新】通达OA前台反序列化漏洞分析

0x01 前言

注:本文仅以安全研究为目的,分享对该漏洞的挖掘过程,文中涉及的所有漏洞均已报送给国家单位,请勿用做非法用途。

通达OA作为历史上出现漏洞较多的OA,在经过多轮的迭代之后已经很少前台的RCE漏洞了。一般来说通达OA是通过auth.inc.php文件来进行鉴权,如图1.1所示。整个通达全部的代码看下来很少有未鉴权的代码。

图片

在通达中有一个模块

/general/appbuilder/web/index.php采用了yii框架实现,其鉴权逻辑与其他模块存在显著差异。

$url = $_SERVER["REQUEST_URI"];    $strurl = substr($url, 0, strpos($url, "?"));
    if (strpos($strurl, "/portal/") !== false) {      if (strpos($strurl, "/gateway/") === false) {        header("Location:/index.php");        sess_close();        exit();      }      else if (strpos($strurl, "/gateway/saveportal") !== false) {        header("Location:/index.php");        sess_close();        exit();      }      else if (strpos($url, "edit") !== false) {        header("Location:/index.php");        sess_close();        exit();      }    }    else if (strpos($url, "/appdata/doprint") !== false) {      $_GET["csrf"] = urldecode($_GET["csrf"]);      $b_check_csrf = false;      if (!empty($_GET["csrf"]) && preg_match("/^\{([0-9A-Z]|-){36}\}$/", $_GET["csrf"])) {        $s_tmp = __DIR__ . "/../../../../logs/appbuilder/logs";        $s_tmp .= "/" . $_GET["csrf"];
        if (file_exists($s_tmp)) {          $b_check_csrf = true;          $b_dir_priv = true;        }      }
      if (!$b_check_csrf) {        header("Location:/index.php");        sess_close();        exit();      }    }    else {      header("Location:/index.php");      sess_close();      exit();    }

从上面的代码可以看出,如果访问的目标地址是/general/appbuilder/web/portal/gateway/?,则不需要授权就能访问对应的接口。

再来看一下通达中使用的yii版本,如图1.2所示。Yii2 < 2.0.38是存在反序列化利用链的,网上已经有很多分析文章,感兴趣的小伙伴可以关注。

https://www.anquanke.com/post/id/254429

虽然有了利用链,但如何利用一直是个难题。本文的目的是寻找反序列化利用点并构造反序列化利用链达到RCE效果。

0x02 反序列化点

在appbuilder模块中多数情况下会加载视图views/layouts/main.php。视图中会调用csrfMetaTags方法。

<?php
$this->beginPage();echo "<!DOCTYPE html>\n<html lang=\"";echo Yii::$app->language;echo "\">\n<head>\n    <meta charset=\"";echo Yii::$app->charset;echo "\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    ";echo yii\helpers\Html::csrfMetaTags();echo "    <title>";echo yii\helpers\Html::encode($this->title);echo "</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"";echo Yii::$app->params["MYOA_STATIC_SERVER"];echo "/static/theme/";echo Yii::$app->params["LOGIN_THEME"] == "" ? "1" : Yii::$app->params["LOGIN_THEME"];echo "/style.css\" />\n    <link href=\"/static/js/bootstrap/css/bootstrap.css\" rel=\"stylesheet\">\n<!--    <link href=\"/module/appbuilder/css/bootstrap.css\" rel=\"stylesheet\">-->\n    <link href=\"/module/appbuilder/css/site.css\" rel=\"stylesheet\"></head>\n\t<style>\n\ta.btn.btn-danger {\n\t\tcolor: #fff;\n\t}\n\t</style>\n    ";$this->head();echo "</head>\n<body class=\"bodycolor\">\n";$this->beginBody();echo "\n<div><!--class=\"wrap\"-->\n    ";

在yii框架中存在yii\helpers\Html::csrfMetaTags()方法,该方法的主要作用时用于生成csrf校验需要的meta标签。

 public static function csrfMetaTags(){        $request = Yii::$app->getRequest();        if ($request instanceof Request && $request->enableCsrfValidation) {            return static::tag('meta', '', ['name' => 'csrf-param', 'content' => $request->csrfParam]) . "\n"                . static::tag('meta', '', ['name' => 'csrf-token', 'content' => $request->getCsrfToken()]) . "\n";        }
        return '';    }

在方法中调用了$request->getCsrfToken(),跟踪该方法。

public function getCsrfToken($regenerate = false){        if ($this->_csrfToken === null || $regenerate) {            $token = $this->loadCsrfToken();            if ($regenerate || empty($token)) {                $token = $this->generateCsrfToken();            }            $this->_csrfToken = Yii::$app->security->maskToken($token);        }
        return $this->_csrfToken;    }   public function getCsrfToken($regenerate = false){        if ($this->_csrfToken === null || $regenerate) {            $token = $this->loadCsrfToken();            if ($regenerate || empty($token)) {                $token = $this->generateCsrfToken();            }            $this->_csrfToken = Yii::$app->security->maskToken($token);        }
        return $this->_csrfToken;    }

在该方法中继续调用了loadCsrfToken方法,跟踪该方法。

​​​​​​​

protected function loadCsrfToken(){        if ($this->enableCsrfCookie) {            return $this->getCookies()->getValue($this->csrfParam);        }
        return Yii::$app->getSession()->get($this->csrfParam);    }

继续跟踪getCookies方法。

public function getCookies(){    if ($this->_cookies === null) {        $this->_cookies = new CookieCollection($this->loadCookies(), [            'readOnly' => true,        ]);    }    return $this->_cookies;}

继续跟踪loadCookies方法,在这个方法中会调用unserialize方法对传入的Cookie的值进行反序列化。这也就会造成反序列化漏洞。

​​​​​​​​​​​​​​

protected function loadCookies(){        $cookies = [];        if ($this->enableCookieValidation) {            if ($this->cookieValidationKey == '') {                throw new InvalidConfigException(get_class($this) . '::cookieValidationKey must be configured with a secret key.');            }            foreach ($_COOKIE as $name => $value) {                if (!is_string($value)) {                    continue;                }                $data = Yii::$app->getSecurity()->validateData($value, $this->cookieValidationKey);                if ($data === false) {                    continue;                }                if (defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 70000) {                    $data = @unserialize($data, ['allowed_classes' => false]);                } else {                    $data = @unserialize($data); //这里是反序列化点                }                if (is_array($data) && isset($data[0], $data[1]) && $data[0] === $name) {                    $cookies[$name] = Yii::createObject([                        'class' => 'yii\web\Cookie',                        'name' => $name,                        'value' => $data[1],                        'expire' => null,                    ]);                }            }        } else {            foreach ($_COOKIE as $name => $value) {                $cookies[$name] = Yii::createObject([                    'class' => 'yii\web\Cookie',                    'name' => $name,                    'value' => $value,                    'expire' => null,                ]);            }        }
        return $cookies;    }

0x03 绕过限制

在上面的方法中会对传入的Cookie值进行签名校验,校验的方法是validateData,其中$this->cookieValidationKey是签名的key。

Yii::$app->getSecurity()->validateData($value, $this->cookieValidationKey)

在validateData方法中会通过hash_hmac对传入的key和value进行签名校验。

public function validateData($data, $key, $rawHash = false){    $test = @hash_hmac($this->macHash, '', '', $rawHash);    if (!$test) {        throw new InvalidConfigException('Failed to generate HMAC with hash algorithm: ' . $this->macHash);    }    $hashLength = StringHelper::byteLength($test);    if (StringHelper::byteLength($data) >= $hashLength) {        $hash = StringHelper::byteSubstr($data, 0, $hashLength);        $pureData = StringHelper::byteSubstr($data, $hashLength, null);        $calculatedHash = hash_hmac($this->macHash, $pureData, $key, $rawHash);        if ($this->compareString($hash, $calculatedHash)) {            return $pureData;        }    }    return false;}

由于通达OA中的$this->cookieValidationKey来自于配置文件general/appbuilder/config/web.php。其中值是固定的。

<?php
$params = require __DIR__ . "/params.php";$config = array(  "id"          => "appbuilder",  "basePath"    => dirname(__DIR__),  "bootstrap"   => array("log"),  "charset"     => "GB2312",  "language"    => "zh-CN",  "runtimePath" => "@app/../../../logs/appbuilder",  "components"  => array(    "request"      => array("cookieValidationKey" => "tdide2"), //这是固定的密钥tdide2    "cache"        => array("class" => "app\\td\base\TDRedisCache"),    "redis"        => array("class" => "yii\\redis\Connection", "hostname" => $MYOA_REDIS_SERVERS[0]["host"], "port" => $MYOA_REDIS_SERVERS[0]["port"], "database" => $MYOA_REDIS_DB_ID + 1, "password" => $MYOA_REDIS_PASS),    "errorHandler" => array("errorAction" => "site/error"),    "log"          => array(      "traceLevel" => 1,      "targets"    => array(        array(          "class"  => "yii\log\FileTarget",          "levels" => array("error")          )        )

另外通达OA有全局的addslashes过滤,包括Cookie中的值。由于PHP反序列化中有大量的双引号,如果直接通过Cookie传递则会因为双引号被转义而失败。但是令人开心的是通达在进行全局addslashes的时候对部分值进行了例外排查。

if (0 < count($_COOKIE)) {  foreach ($_COOKIE as $s_key => $s_value ) {    if ((substr($s_key, 0, 7) == "_SERVER") || (substr($s_key, 0, 8) == "_SESSION") || (substr($s_key, 0, 7) == "_COOKIE") || (substr($s_key, 0, 4) == "_GET") || (substr($s_key, 0, 5) == "_POST") || (substr($s_key, 0, 6) == "_FILES")) {      continue;    }
    if (!is_array($s_value)) {      $_COOKIE[$s_key] = addslashes(strip_tags($s_value));    }
    $s_key = $_COOKIE[$s_key];  }
  reset($_COOKIE);}

如果Cookie中字段名称的前面几位字符为_GET这种,则不进行addslashes操作。这也就给漏洞利用提供了便利。

0x04结论

反序列化利用链是直接采用yii中已经公开的反序列化链,本文作者调好的poc(实际是exp)如下所示,完整的poc可从ddpoc平台查看。

https://www.ddpoc.com/poc/DVB-2023-4705.html

图片

使用该poc之后可以会在网站根目录生成哥斯拉的webshell。

在根目录生成文件/logon.php 111/111 哥斯拉

本漏洞已向相关单位报送并已推出补丁,可适用于通达11.X全版本。对应12系列未做过多测试,本文提到的所有漏洞在最新版的通达12.4中已修复,使用此漏洞造成的任何攻击影响均与本文作者无关

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ的安装

RabbitMQ的安装 1、Windows环境下的RabbitMQ安装步骤 使用的版本&#xff1a;otp_win64_23.2 rabbitmq-server-3.8.16 版本说明&#xff1a;https://www.rabbitmq.com/which-erlang.html#compatibility-matrix 1.1 下载并安装erlang RabbitMQ 服务端代码是使用并发式语言…

探讨|使用或不使用机器学习

动动发财的小手&#xff0c;点个赞吧&#xff01; 机器学习擅长解决某些复杂问题&#xff0c;通常涉及特征和结果之间的困难关系&#xff0c;这些关系不能轻易地硬编码为启发式或 if-else 语句。然而&#xff0c;在决定 ML 是否是当前给定问题的良好解决方案时&#xff0c;有一…

按轨迹运行

文章目录 import math import timeimport numpy as np import matplotlib.pyplot as pltdef plot_arrow(x, y, yaw, length=5, width=1):dx = length * math.cos(yaw)dy = length * math.sin(yaw)plt.arrow(x, y, dx, dy, head_length=width, head_width=width)plt.plot([x, x …

Spring Boot、Spring Cloud、Spring Alibaba 版本对照关系及稳定兼容版本

Spring Boot、Spring Cloud、Spring Alibaba 版本对照关系及稳定兼容版本 引言 在 Java 生态系统中&#xff0c;Spring Boot、Spring Cloud 和 Spring Alibaba 是非常流行的框架&#xff0c;它们提供了丰富的功能和优雅的解决方案。然而&#xff0c;随着不断的发展和更新&…

SSM项目-博客系统

在线体验项目&#xff1a;登陆页面 项目连接&#xff1a;huhublog_ssm: 个人博客系统 技术栈&#xff1a;SpringBoot、SpringMVC、Mybatis、Redis、JQuery、Ajax、Json (gitee.com) 1.项目技术点分析 SpringBoot、SpringWeb(SpringMVC)、MyBatis、MySQL(8.x)、Redis(存储验…

EXCEL, 用if({1,0,0} ...) 实现把给定的区域,输出为任意你想要的矩阵,数组区域!

目录 1 原材料&#xff1a;这样的一个区域 工具 if({1,0,0}) 数组公式 1.1 原始数据 1.2 原理 if(0/1,t-value,f-value)---变形--->if({},range1,range2) 1.2.1 if(0/1,t-value,f-value)---变形--->if({},range1,range2) 1.2.2 原理1&#xff1a; if 数组原理&#…

【无标题】云原生在工业互联网的落地及好处!

什么是工业互联网&#xff1f; 工业互联网&#xff08;Industrial Internet&#xff09;是新一代信息通信技术与工业经济深度融合的新型基础设施、应用模式和工业生态&#xff0c;通过对人、机、物、系统等的全面连接&#xff0c;构建起覆盖全产业链、全价值链的全新制造和服务…

【Pycharm2022.2.1】python编辑器最新版安装教程(包含2017-2022的所有版本win/mac/linux)

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 永久安装 Pycharm&#xff08;2017-2022的win/mac/linux所有版本&#xff09;/ IntelliJ IDEA也可以, 按照本文教程所写的&#xff0c;具体步骤跟着下面的图文教程一步一步来就行&#xff0c;一分钟即可搞定&#xff0c;过…

红队钓鱼技术之LNK快捷方式

简介 lnk文件是用于指向其他文件的一种文件。这些文件通常称为快捷方式文件&#xff0c;通常它以快捷方式放在硬盘上&#xff0c;以方便使用者快速的调用。lnk钓鱼主要将图标伪装成正常图标&#xff0c;但是目标会执行shell命令 步骤 1.编写shell命令 首先新建一个文本文件t…

vuejs源码分析之全局API(vm.$off)

vue在初始化的时候会给vue对象本身挂载一些全局的api。今天我们一个一个来看这些api。 vm.$off方法 这个方法是用来移除自定义事件监听器。 他的用法 vm.$off(event, calback)第一个参数event取值可以是string字符串&#xff0c;也可以是Array<string>也就是说既可以删…

无涯教程-Perl - each函数

描述 在列表context中调用此函数时,将返回一个由2个元素组成的列表,该列表由哈希的下一个元素的键和值组成,以便您可以对其进行迭代。在标量context中调用时,仅返回哈希中下一个元素的键。 语法 以下是此函数的简单语法- each HASH返回值 在列表context中调用此函数时,将返…

如何使用 reqwest 包

GET 请求 向连接发起一个 GET 请求&#xff1a;https://hacker-news.firebaseio.com/v0/topstories.json&#xff0c;并解析返回的内容。 尝试发起请求 因为是 GET 请求&#xff0c;可以先在浏览器中进行查看&#xff0c;浏览器可以正常显示一个 id 列表&#xff0c;如下所示…

P1629 邮递员送信(最短路)(内附封面)

邮递员送信 题目描述 有一个邮递员要送东西&#xff0c;邮局在节点 1 1 1。他总共要送 n − 1 n-1 n−1 样东西&#xff0c;其目的地分别是节点 2 2 2 到节点 n n n。由于这个城市的交通比较繁忙&#xff0c;因此所有的道路都是单行的&#xff0c;共有 m m m 条道路。这…

嵌入式:C高级 Day4

一、整理思维导图 二、写一个函数&#xff0c;获取用户的uid和gid并使用变量接收 三、整理冒泡排序、简单选择排序和快速排序的代码 冒泡排序 #include <myhead.h>void output(int arr[], int len); void bubble_sort(int arr[], int len);int main(int argc, const ch…

摄像机终端IP地址白名单配置流程

海康摄像头配置白名单流程 1.登录海康摄像机前端 2.进入配置-系统-安全管理-IP地址过滤 3.IP地址过滤方式选择“允许” 4.点击添加按钮输入对应的IP地址或者IP网段 5.最后勾选启用IP地址过滤&#xff0c;然后保存 大华摄像头配置白名单流程 1.登录大华摄像机前端 2.进入设…

怎么样通过Bootstrap已经编译好(压缩好)的源码去查看符合阅读习惯的源码【通过Source Map(源映射)文件实现】

阅读本篇博文前&#xff0c;建议大家先看看下面这篇博文&#xff1a; https://blog.csdn.net/wenhao_ir/article/details/132089650 Bootstrap经编译(压缩)后的源码百度网盘下载地址&#xff1a; https://pan.baidu.com/s/14BM9gpC3K-LKxhyLGh4J9Q?pwdm02m Bootstrap未经编译…

优化手机性能,解决卡顿问题:关闭这3个微信开关,释放内存空间

在日常使用手机的过程中&#xff0c;微信无疑是受欢迎和广泛使用的社交应用之一。然而&#xff0c;由于微信的一些默认设置可能会占用大量内存空间&#xff0c;导致手机性能下降并出现卡顿问题。为了提高手机的运行效果&#xff0c;我们需要注意并关闭这3个微信开关&#xff0c…

什么样的跨网文件安全交换系统 可实现安全便捷的文件摆渡?

进入互联网时代&#xff0c;网络的运算和数据管理能力助力各个行业高速发展&#xff0c;但同样带来了一些网络安全隐患&#xff0c;网络攻击、数据窃取、敏感信息泄露等问题。为此&#xff0c;我国出台了系列政策来全面提升银各行业系统网络安全整体防护水平&#xff0c;其中“…

electron+vue3全家桶+vite项目搭建【25】使用electron-updater自动更新应用

文章目录 引入实现效果实现步骤引入依赖配置electron-buidler文件封装版本升级工具类主进程调用版本更新校验渲染进程封装方法调用 测试版本更新 引入 demo项目地址 electron-updater官网 我们不可能每次发布新的版本都让用户去手动下载安装最新的包&#xff0c;而是应用可以…

数据库与数据仓库的区别及关系

数据库与数据仓库的区别及关系 数据库数据仓库异同差异联系例子 数据库 数据库是结构化信息或数据的有序集合&#xff0c;一般以电子形式存储在计算机系统中。通常由数据库管理系统 (DBMS) 来控制。它是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集…