Openfire身份认证漏洞分析(CVE-2023-32315)
2023-9-21 21:35:0 Author: xz.aliyun.com(查看原文) 阅读量:11 收藏

openfire是一个即时通讯服务器,也称之为即时通讯平台。它是基于XMPP协议的,大家所熟悉的通讯软件QQ、MSN和Gtalk等等,其中Gtalk就是基于XMPP协议的实现。
在即时通讯中往往因为需要保存一些状态或者数据所以不能采用点对点通讯,而是需要搭建服务器来转发。

来源:`https://blog.csdn.net/qin34/article/details/41316231`

通俗的讲openfire是一个即时通讯服务器,也叫即时通讯平台

3.10.0 <= Openfire < 4.6.8

4.7.0 <= Openfire < 4.7.5

这个漏洞的原理有点像多年前出现的CVE-2008-6508目录穿越漏洞

在设置为maven项目之后,先从源头配置找起

<filter>
        <filter-name>AuthCheck</filter-name>
        <filter-class>org.jivesoftware.admin.AuthCheckFilter</filter-class>
        <init-param>
            <param-name>excludes</param-name>
            <param-value>
                login.jsp,index.jsp?logout=true,setup/index.jsp,setup/setup-*,.gif,.png,error-serverdown.jsp,loginToken.jsp
            </param-value>
        </init-param>
    </filter>

该处定义了一个名为AuthCheckfilter,用于对部分url免除鉴权

然后全局搜索AuthCheckFilter去寻找定义文件

定位代码

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException
    {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        // Do not allow framing; OF-997
        response.setHeader("X-Frame-Options", JiveGlobals.getProperty("adminConsole.frame-options", "SAMEORIGIN"));
        // Reset the defaultLoginPage variable
        String loginPage = defaultLoginPage;
        if (loginPage == null) {
            loginPage = request.getContextPath() + (AuthFactory.isOneTimeAccessTokenEnabled() ? "/loginToken.jsp" : "/login.jsp" );
        }
        // Get the page we're on:
        String url = request.getRequestURI().substring(1);
        if (url.startsWith("plugins/")) {
            url = url.substring("plugins/".length());
        }
        // See if it's contained in the exclude list. If so, skip filter execution
        boolean doExclude = false;
        for (String exclude : excludes) {
            if (testURLPassesExclude(url, exclude)) {
                doExclude = true;
                break;
            }
        }

定义代码里面可以看出

if (testURLPassesExclude(url, exclude)) {
                doExclude = true;
                break;
            }

doExclude = true则无需鉴权

继续跟进testURLPassesExclude方法

public static boolean testURLPassesExclude(String url, String exclude) {
        // If the exclude rule includes a "?" character, the url must exactly match the exclude rule.
        // If the exclude rule does not contain the "?" character, we chop off everything starting at the first "?"
        // in the URL and then the resulting url must exactly match the exclude rule. If the exclude ends with a "*"
        // character then the URL is allowed if it exactly matches everything before the * and there are no ".."
        // characters after the "*". All data in the URL before

        if (exclude.endsWith("*")) {
            if (url.startsWith(exclude.substring(0, exclude.length()-1))) {
                // Now make sure that there are no ".." characters in the rest of the URL.
                if (!url.contains("..") && !url.toLowerCase().contains("%2e")) {
                    return true;
                }
            }
        }
        else if (exclude.contains("?")) {
            if (url.equals(exclude)) {
                return true;
            }
        }
        else {
            int paramIndex = url.indexOf("?");
            if (paramIndex != -1) {
                url = url.substring(0, paramIndex);
            }
            if (url.equals(exclude)) {
                return true;
            }
        }
        return false;
    }

代码中​ if (!url.contains("..") && !url.toLowerCase().contains("%2e")) {可以发现对部分字符做了过滤

但是其内置的web服务器支持对%u002e非标准unicode编码的解析

具体代码如下所示

if (!encodedPath && !dot)
{
    if (_param == null)
        _decodedPath = _path;
    else
        _decodedPath = _path.substring(0, _path.length() - _param.length() - 1);
}
else if (_path != null)
{
    // The RFC requires this to be canonical before decoding, but this can leave dot segments and dot dot segments
    // which are not canonicalized and could be used in an attempt to bypass security checks.
    String decodedNonCanonical = URIUtil.decodePath(_path);
    _decodedPath = URIUtil.canonicalPath(decodedNonCanonical);
    if (_decodedPath == null)
        throw new IllegalArgumentException("Bad URI");
}

for (int i = offset; i < end; i++)
{
    char c = path.charAt(i);
    switch (c)
    {
        case '%':
            if (builder == null)
            {
                builder = new Utf8StringBuilder(path.length());
                builder.append(path, offset, i - offset);
            }
            if ((i + 2) < end)
            {
                char u = path.charAt(i + 1);
                if (u == 'u')
                {
                    // 解码%uxxxx形式的Unicode字符
                    builder.append((char)(0xffff & TypeUtil.parseInt(path, i + 2, 4, 16)));
                    i += 5;
                }
                else
                {
                    // 解码%xx形式的ASCII字符
                    builder.append((byte)(0xff & (TypeUtil.convertHexDigit(u) * 16 + TypeUtil.convertHexDigit(path.charAt(i + 2)))));
                    i += 2;
                }
            }
            else
            {
                throw new IllegalArgumentException("Bad URI % encoding");
            }
            break;

环境部署

首先利用docker拉取漏洞版本镜像

然后开启漏洞环境

然后确定一下

部署完毕

利用漏洞

brupsuit打洞

go打洞

cd CVE-2023-32315-Openfire-Bypass/scan_all
go run main.go -u http://openfire.com:9090

然后就直接出来了,操作不难


文章来源: https://xz.aliyun.com/t/12869
如有侵权请联系:admin#unsafe.sh