Apache RocketMQ: Possible remote code execution vulnerability when using the update configuration function
Description
For RocketMQ versions 5.1.0 and below, under certain conditions, there is a risk of remote command execution.
Several components of RocketMQ, including NameServer, Broker, and Controller, are leaked on the extranet and lack permission verification, an attacker can exploit this vulnerability by using the update configuration function to execute commands as the system users that RocketMQ is running as. Additionally, an attacker can achieve the same effect by forging the RocketMQ protocol content.
To prevent these attacks, users are recommended to upgrade to version 5.1.1 or above for using RocketMQ 5.x or 4.9.6 or above for using RocketMQ 4.x .
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.rocketmq:rocketmq-brokerMaven | >= 5.0.0, < 5.1.1 | 5.1.1 |
org.apache.rocketmq:rocketmq-namesrvMaven | >= 4.0.0, < 4.9.6 | 4.9.6 |
org.apache.rocketmq:rocketmq-controllerMaven | >= 5.0.0, < 5.1.1 | 5.1.1 |
org.apache.rocketmq:rocketmq-namesrvMaven | >= 5.0.0, < 5.1.1 | 5.1.1 |
Affected products
1- Apache Software Foundation/Apache RocketMQv5Range: 0
Patches
2c3ada731405cMake configPath unable to update at runtime
2 files changed · +46 −0
namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/DefaultRequestProcessor.java+6 −0 modified@@ -581,6 +581,12 @@ private RemotingCommand updateConfig(ChannelHandlerContext ctx, RemotingCommand return response; } + if (properties.containsKey("kvConfigPath") || properties.containsKey("configStorePathName")) { + response.setCode(ResponseCode.NO_PERMISSION); + response.setRemark("Can not update config path"); + return response; + } + this.namesrvController.getConfiguration().update(properties); }
namesrv/src/test/java/org/apache/rocketmq/namesrv/processor/DefaultRequestProcessorTest.java+40 −0 modified@@ -20,10 +20,13 @@ import io.netty.channel.ChannelHandlerContext; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; +import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.TopicConfig; import org.apache.rocketmq.logging.InternalLogger; import org.apache.rocketmq.common.namesrv.NamesrvConfig; @@ -159,6 +162,43 @@ public void testProcessRequest_DeleteKVConfig() throws RemotingCommandException .isNull(); } + @Test + public void testProcessRequest_UpdateConfigPath() throws RemotingCommandException { + final RemotingCommand updateConfigRequest = RemotingCommand.createRequestCommand(RequestCode.UPDATE_NAMESRV_CONFIG, null); + Properties properties = new Properties(); + + // Update allowed value + properties.setProperty("enableTopicList", "true"); + updateConfigRequest.setBody(MixAll.properties2String(properties).getBytes(StandardCharsets.UTF_8)); + + RemotingCommand response = defaultRequestProcessor.processRequest(null, updateConfigRequest); + + assertThat(response).isNotNull(); + assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS); + + //update disallowed value + properties.clear(); + properties.setProperty("configStorePathName", "test/path"); + updateConfigRequest.setBody(MixAll.properties2String(properties).getBytes(StandardCharsets.UTF_8)); + + response = defaultRequestProcessor.processRequest(null, updateConfigRequest); + + assertThat(response).isNotNull(); + assertThat(response.getCode()).isEqualTo(ResponseCode.NO_PERMISSION); + assertThat(response.getRemark()).contains("Can not update config path"); + + //update disallowed values + properties.clear(); + properties.setProperty("kvConfigPath", "test/path"); + updateConfigRequest.setBody(MixAll.properties2String(properties).getBytes(StandardCharsets.UTF_8)); + + response = defaultRequestProcessor.processRequest(null, updateConfigRequest); + + assertThat(response).isNotNull(); + assertThat(response.getCode()).isEqualTo(ResponseCode.NO_PERMISSION); + assertThat(response.getRemark()).contains("Can not update config path"); + } + @Test public void testProcessRequest_RegisterBroker() throws RemotingCommandException, NoSuchFieldException, IllegalAccessException {
9d411cf04a69Make configPath unable to update at runtime (#6733)
6 files changed · +163 −2
broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java+9 −1 modified@@ -775,13 +775,21 @@ private synchronized RemotingCommand updateBrokerConfig(ChannelHandlerContext ct String bodyStr = new String(body, MixAll.DEFAULT_CHARSET); Properties properties = MixAll.string2Properties(bodyStr); if (properties != null) { - LOGGER.info("updateBrokerConfig, new config: [{}] client: {} ", properties, ctx.channel().remoteAddress()); + LOGGER.info("updateBrokerConfig, new config: [{}] client: {} ", properties, callerAddress); + + if (properties.containsKey("brokerConfigPath")) { + response.setCode(ResponseCode.NO_PERMISSION); + response.setRemark("Can not update config path"); + return response; + } + this.brokerController.getConfiguration().update(properties); if (properties.containsKey("brokerPermission")) { long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0; this.brokerController.getTopicConfigManager().getDataVersion().nextVersion(stateMachineVersion); this.brokerController.registerBrokerAll(false, false, true); } + } else { LOGGER.error("string2Properties error"); response.setCode(ResponseCode.SYSTEM_ERROR);
broker/src/test/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessorTest.java+32 −0 modified@@ -24,8 +24,10 @@ import java.net.SocketAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; +import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.LongAdder; @@ -276,6 +278,36 @@ public void testGetBrokerConfig() throws Exception { assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS); } + @Test + public void testProcessRequest_UpdateConfigPath() throws RemotingCommandException { + final RemotingCommand updateConfigRequest = RemotingCommand.createRequestCommand(RequestCode.UPDATE_BROKER_CONFIG, null); + Properties properties = new Properties(); + + ChannelHandlerContext ctx = mock(ChannelHandlerContext.class); + when(ctx.channel()).thenReturn(null); + + // Update allowed value + properties.setProperty("allAckInSyncStateSet", "true"); + updateConfigRequest.setBody(MixAll.properties2String(properties).getBytes(StandardCharsets.UTF_8)); + + RemotingCommand response = adminBrokerProcessor.processRequest(ctx, updateConfigRequest); + + assertThat(response).isNotNull(); + assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS); + + //update disallowed value + properties.clear(); + properties.setProperty("brokerConfigPath", "test/path"); + updateConfigRequest.setBody(MixAll.properties2String(properties).getBytes(StandardCharsets.UTF_8)); + + response = adminBrokerProcessor.processRequest(ctx, updateConfigRequest); + + assertThat(response).isNotNull(); + assertThat(response.getCode()).isEqualTo(ResponseCode.NO_PERMISSION); + assertThat(response.getRemark()).contains("Can not update config path"); + + } + @Test public void testSearchOffsetByTimestamp() throws Exception { messageStore = mock(MessageStore.class);
controller/src/main/java/org/apache/rocketmq/controller/processor/ControllerRequestProcessor.java+6 −0 modified@@ -241,6 +241,12 @@ private RemotingCommand handleUpdateControllerConfig(ChannelHandlerContext ctx, return response; } + if (properties.containsKey("configStorePath")) { + response.setCode(ResponseCode.NO_PERMISSION); + response.setRemark("Can not update config path"); + return response; + } + this.controllerManager.getConfiguration().update(properties); }
controller/src/test/java/org/apache/rocketmq/controller/ControllerRequestProcessorTest.java+70 −0 added@@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.controller; + +import java.nio.charset.StandardCharsets; +import java.util.Properties; +import org.apache.rocketmq.common.ControllerConfig; +import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.controller.processor.ControllerRequestProcessor; +import org.apache.rocketmq.remoting.netty.NettyClientConfig; +import org.apache.rocketmq.remoting.netty.NettyServerConfig; +import org.apache.rocketmq.remoting.protocol.RemotingCommand; +import org.apache.rocketmq.remoting.protocol.RequestCode; +import org.apache.rocketmq.remoting.protocol.ResponseCode; +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ControllerRequestProcessorTest { + + private ControllerRequestProcessor controllerRequestProcessor; + + @Before + public void init() throws Exception { + controllerRequestProcessor = new ControllerRequestProcessor(new ControllerManager(new ControllerConfig(), new NettyServerConfig(), new NettyClientConfig())); + } + + @Test + public void testProcessRequest_UpdateConfigPath() throws Exception { + final RemotingCommand updateConfigRequest = RemotingCommand.createRequestCommand(RequestCode.UPDATE_CONTROLLER_CONFIG, null); + Properties properties = new Properties(); + + // Update allowed value + properties.setProperty("notifyBrokerRoleChanged", "true"); + updateConfigRequest.setBody(MixAll.properties2String(properties).getBytes(StandardCharsets.UTF_8)); + + RemotingCommand response = controllerRequestProcessor.processRequest(null, updateConfigRequest); + + assertThat(response).isNotNull(); + assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS); + + // Update disallowed value + properties.clear(); + properties.setProperty("configStorePath", "test/path"); + updateConfigRequest.setBody(MixAll.properties2String(properties).getBytes(StandardCharsets.UTF_8)); + + response = controllerRequestProcessor.processRequest(null, updateConfigRequest); + + assertThat(response).isNotNull(); + assertThat(response.getCode()).isEqualTo(ResponseCode.NO_PERMISSION); + assertThat(response.getRemark()).contains("Can not update config path"); + + } +}
namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/DefaultRequestProcessor.java+6 −0 modified@@ -627,6 +627,12 @@ private RemotingCommand updateConfig(ChannelHandlerContext ctx, RemotingCommand return response; } + if (properties.containsKey("kvConfigPath") || properties.containsKey("configStorePathName")) { + response.setCode(ResponseCode.NO_PERMISSION); + response.setRemark("Can not update config path"); + return response; + } + this.namesrvController.getConfiguration().update(properties); }
namesrv/src/test/java/org/apache/rocketmq/namesrv/processor/RequestProcessorTest.java+40 −1 modified@@ -21,10 +21,13 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; +import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.TopicConfig; import org.apache.rocketmq.common.UtilAll; import org.apache.rocketmq.common.namesrv.NamesrvConfig; @@ -85,7 +88,6 @@ public void init() throws Exception { clientRequestProcessor = new ClientRequestProcessor(namesrvController); - registerRouteInfoManager(); logger = mock(Logger.class); @@ -178,6 +180,43 @@ public void testProcessRequest_UnSupportedRequest() throws RemotingCommandExcept assertThat(response.getCode()).isEqualTo(ResponseCode.REQUEST_CODE_NOT_SUPPORTED); } + @Test + public void testProcessRequest_UpdateConfigPath() throws RemotingCommandException { + final RemotingCommand updateConfigRequest = RemotingCommand.createRequestCommand(RequestCode.UPDATE_NAMESRV_CONFIG, null); + Properties properties = new Properties(); + + // Update allowed value + properties.setProperty("enableTopicList", "true"); + updateConfigRequest.setBody(MixAll.properties2String(properties).getBytes(StandardCharsets.UTF_8)); + + RemotingCommand response = defaultRequestProcessor.processRequest(null, updateConfigRequest); + + assertThat(response).isNotNull(); + assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS); + + //update disallowed value + properties.clear(); + properties.setProperty("configStorePathName", "test/path"); + updateConfigRequest.setBody(MixAll.properties2String(properties).getBytes(StandardCharsets.UTF_8)); + + response = defaultRequestProcessor.processRequest(null, updateConfigRequest); + + assertThat(response).isNotNull(); + assertThat(response.getCode()).isEqualTo(ResponseCode.NO_PERMISSION); + assertThat(response.getRemark()).contains("Can not update config path"); + + //update disallowed values + properties.clear(); + properties.setProperty("kvConfigPath", "test/path"); + updateConfigRequest.setBody(MixAll.properties2String(properties).getBytes(StandardCharsets.UTF_8)); + + response = defaultRequestProcessor.processRequest(null, updateConfigRequest); + + assertThat(response).isNotNull(); + assertThat(response.getCode()).isEqualTo(ResponseCode.NO_PERMISSION); + assertThat(response.getRemark()).contains("Can not update config path"); + } + @Test public void testProcessRequest_RegisterBroker() throws RemotingCommandException, NoSuchFieldException, IllegalAccessException {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-x3cq-8f32-5f63ghsaADVISORY
- lists.apache.org/thread/1s8j2c8kogthtpv3060yddk03zq0pxypghsavendor-advisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2023-33246ghsaADVISORY
- packetstormsecurity.com/files/173339/Apache-RocketMQ-5.1.0-Arbitrary-Code-Injection.htmlghsaWEB
- www.openwall.com/lists/oss-security/2023/07/12/1ghsaWEB
- github.com/apache/rocketmq/commit/9d411cf04a695e7a3f41036e8377b0aa544d754dghsaWEB
- github.com/apache/rocketmq/commit/c3ada731405c5990c36bf58d50b3e61965300703ghsaWEB
- www.cisa.gov/known-exploited-vulnerabilities-catalogghsaWEB
- www.vicarius.io/vsociety/posts/rocketmq-rce-cve-2023-33246-33247ghsaWEB
News mentions
0No linked articles in our index yet.