unbridled-41 opened a new issue, #2832:
URL: https://github.com/apache/rocketmq-dashboard/issues/2832
## Problem
PR #2339 added a broker-topology guard (`validatedBrokerAddr`) that rejects
offset message ids whose decoded broker address is outside the selected
instance topology. However, the guard only covers the **fallback** path
(`viewMessageByOffsetId`). The **primary** lookup path still invokes
`adminExt.viewMessage(topic, msgId)` with no address validation, and it runs
**before** the fallback — so a forged offset msgId still makes Studio open a
RocketMQ remoting connection to an attacker-chosen `ip:port`.
## Evidence
`server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProvider.java`:
- Line 146 (`queryByMsgId`) and line 570 (`resolveMessageStoreTimestamp`)
call `adminExt.viewMessage(topic, msgId)` directly, before any validation.
- The guarded fallback (`viewMessageByOffsetId`, `validatedBrokerAddr`) is
only reached when the primary call throws or returns null.
What the client does with that call (verified in the rocketmq 5.5.x
bytecode):
`MQAdminImpl.viewMessage(topic, msgId)` ->
`MessageDecoder.decodeMessageId(msgId)` ->
`MQClientAPIImpl.viewMessage(NetworkUtil.socketAddress2String(messageId.getAddress()),
topic, offset, ...)`
`decodeMessageId` builds a literal `InetSocketAddress` from bytes 0-7 of a
32-char hex offset id (`InetAddress.getByAddress` — no DNS), so the caller
fully controls both IP and port. The existing fallback test
(`queryByMsgIdRejectsDecodedBrokerOutsideKnownTopology`) stubs `viewMessage` to
throw, which is why the guard test passes while the production primary path
still connects first.
The codebase itself acknowledges this bug class in `ToolAccessPolicy.java`:
> These read-labeled tools still let callers steer RocketMQ remoting to
user-influenced broker addresses via msgId-derived lookups, so readers must not
gain access before the address-ownership validation bug is fixed.
The AI tool route is deny-listed for readers, but the REST routes `GET
/api/messages` (query by msgId) and `GET /api/messages/{msgId}/trace` are
reader-accessible and reach the identical unguarded sink.
## Impact
Any authenticated reader-role user can make Studio open RocketMQ remoting
connections to arbitrary internal `ip:port` endpoints (blind SSRF / port-scan
oracle via timing and error text). Against a broker address of a *different*
instance, the primary path returns that broker's message content — a
cross-instance message read.
## Suggested fix
Decode the msgId and validate the embedded address with the existing
`validatedBrokerAddr` helper **before** invoking `adminExt.viewMessage(topic,
msgId)` in both `queryByMsgId` and `resolveMessageStoreTimestamp`. Ids that do
not decode as offset ids can pass through untouched — `MQAdminImpl` falls back
to the unique-key lookup, which resolves brokers from the topic route and is
not steerable by the id.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]