Skip to content

Commit

Permalink
Land #19141, Apache RocketMQ & ActiveMQ fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
smcintyre-r7 committed Apr 29, 2024
2 parents 8a08f6a + 6055d8a commit 4341862
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 9 deletions.
23 changes: 19 additions & 4 deletions lib/msf/core/auxiliary/rocketmq.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ def send_version_request
begin
connect
sock.send(header + data_length + data, 0)
res = sock.recv(1024)
res_length = sock.timed_read(4)&.unpack1('N')
return nil if res_length.nil?

res = sock.timed_read(res_length)
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
print_error("Unable to connect: #{e.class} #{e.message}\n#{e.backtrace * "\n"}")
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
elog('Error sending the rocketmq version request', error: e)
return nil
ensure
disconnect
end
Expand Down Expand Up @@ -64,7 +68,11 @@ def get_rocketmq_version(id)
# @return [Hash] Hash including RocketMQ versions info and Broker info if found
def parse_rocketmq_data(res)
# remove a response header so we have json-ish data
res = res[8..]
res = res.split(/\x00_/)[1]
unless res.starts_with?("{")
print_error("Failed to successfully remove the response header and now cannot parse the response.")
return nil
end

# we have 2 json objects appended to each other, so we now need to split that out and make it usable
res = res.split('}{')
Expand Down Expand Up @@ -111,14 +119,21 @@ def get_broker_port(broker_datas, rhost, default_broker_port: 10911)
# Example of brokerData:
# [{"brokerAddrs"=>{"0"=>"172.16.199.135:10911"}, "brokerName"=>"DESKTOP-8ATHH6O", "cluster"=>"DefaultCluster"}]

if broker_datas['brokerDatas'].blank?
print_status("brokerDatas field is missing from the response, assuming default broker port of #{default_broker_port}")
return default_broker_port
end
broker_datas['brokerDatas'].each do |broker_data|
if broker_data['brokerAddrs'].blank?
print_status("brokerAddrs field is missing from the response, assuming default broker port of #{default_broker_port}")
return default_broker_port
end
broker_data['brokerAddrs'].values.each do |broker_endpoint|
next unless broker_endpoint.start_with?("#{rhost}:")
return broker_endpoint.match(/\A#{rhost}:(\d+)\z/)[1].to_i
end
end


print_status("autodetection failed, assuming default port of #{default_broker_port}")
default_broker_port
end
Expand Down
2 changes: 2 additions & 0 deletions modules/exploits/multi/http/apache_rocketmq_update_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ def initialize(info = {})

def check
@version_request_response = send_version_request
return Exploit::CheckCode::Unknown('Unable to determine the version') unless @version_request_response

@parsed_data = parse_rocketmq_data(@version_request_response)
return Exploit::CheckCode::Unknown('RocketMQ did not respond to the request for version information') unless @parsed_data['version']

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,19 @@ def initialize(info = {})
def check
connect

res = sock.get_once
len = sock.timed_read(4)&.unpack1('N')

return CheckCode::Unknown if len.nil? || len > 0x2000 # upper limit in case the service isn't ActiveMQ

res = sock.timed_read(len)

disconnect

return CheckCode::Unknown unless res

len, _, magic = res.unpack('NCZ*')
_, magic = res.unpack('CZ*')

return CheckCode::Unknown unless res.length == len + 4
return CheckCode::Unknown unless res.length == len

return CheckCode::Unknown unless magic == 'ActiveMQ'

Expand All @@ -110,6 +114,8 @@ def check
end

Exploit::CheckCode::Safe("Apache ActiveMQ #{version}")
rescue ::Timeout::Error
CheckCode::Unknown
end

def exploit
Expand Down
7 changes: 5 additions & 2 deletions spec/lib/msf/core/auxiliary/rocketmq_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
end

let(:expected_name_server_response) do
"\x00\x00\x01a\x00\x00\x00_{\"code\":0,\"flag\":1,\"language\":\"JAVA\",\"opaque\":1,\"serializeTypeCurrentRPC\":\"JSON\",\"version\":403}{\"brokerDatas\":[{\"brokerAddrs\":{\"0\":\"172.16.199.135:10911\"},\"brokerName\":\"DESKTOP-8ATHH6O\",\"cluster\":\"DefaultCluster\"}],\"filterServerTable\":{},\"queueDatas\":[{\"brokerName\":\"DESKTOP-8ATHH6O\",\"perm\":7,\"readQueueNums\":8,\"topicSysFlag\":0,\"writeQueueNums\":8}]}".b
"\x00\x00\x00_{\"code\":0,\"flag\":1,\"language\":\"JAVA\",\"opaque\":1,\"serializeTypeCurrentRPC\":\"JSON\",\"version\":403}{\"brokerDatas\":[{\"brokerAddrs\":{\"0\":\"172.16.199.135:10911\"},\"brokerName\":\"DESKTOP-8ATHH6O\",\"cluster\":\"DefaultCluster\"}],\"filterServerTable\":{},\"queueDatas\":[{\"brokerName\":\"DESKTOP-8ATHH6O\",\"perm\":7,\"readQueueNums\":8,\"topicSysFlag\":0,\"writeQueueNums\":8}]}".b
end

let(:expected_parsed_data_response) do
Expand Down Expand Up @@ -55,6 +55,9 @@
describe '#send_version_request' do
it 'returns version info' do
expect(mock_sock).to receive(:send).with(mock_name_server_response, 0)
expect(mock_sock).to receive(:timed_read).with(4).and_return([expected_name_server_response.length].pack('N'))
expect(mock_sock).to receive(:timed_read).with(expected_name_server_response.length).and_return(expected_name_server_response)

expect(subject.send_version_request).to eq(expected_name_server_response)
end
end
Expand All @@ -74,4 +77,4 @@
expect(subject.get_broker_port(expected_parsed_data_response, '172.16.199.1', default_broker_port: 10000)).to eq(10000)
end
end
end
end

0 comments on commit 4341862

Please sign in to comment.