Module: Msf::Auxiliary::Rocketmq

Includes:
Exploit::Remote::Tcp
Defined in:
lib/msf/core/auxiliary/rocketmq.rb

Overview

This module provides methods for working with Apache RocketMQ

Instance Attribute Summary

Attributes included from Exploit::Remote::Tcp

#sock

Instance Method Summary collapse

Methods included from Exploit::Remote::Tcp

#chost, #cleanup, #connect, #connect_timeout, #cport, #disconnect, #handler, #lhost, #lport, #peer, #print_prefix, #proxies, #replicant, #rhost, #rport, #set_tcp_evasions, #shutdown, #ssl, #ssl_cipher, #ssl_verify_mode, #ssl_version

Instance Method Details

#get_broker_port(broker_datas, rhost, default_broker_port: 10911) ⇒ Integer

This function takes the broker data from the name server response, the rhost address and a default Broker port number. The function searches the broker data for a broker instance listening on the rhost address and if found it returns the port found. If the search is unsuccessful it returns the default broker port.

Parameters:

  • broker_datas (Array)

    An array containing a hash of Broker info

  • rhosts (String)

    The RHOST address

  • default_broker_port (Integer) (defaults to: 10911)

    The default broker port

Returns:

  • (Integer)

    the determined broker port

[View source]

118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/msf/core/auxiliary/rocketmq.rb', line 118

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

#get_rocketmq_version(id) ⇒ String

This function takes an ID (number) and looks through rocketmq’s index of version numbers to find the real version number Errors will result in “UNKNOWN_VERSION_ID_<id>” and may be caused by needing to update the version table from github.com/apache/rocketmq/blob/develop/common/src/4d82b307ef50f5cba5717d0ebafeb3cabf336873/java/org/apache/rocketmq/common/MQVersion.java

Parameters:

  • id (Integer)

    The version id found in the NameServer response.

Returns:

  • (String)

    The Apache RocketMQ version string.

[View source]

59
60
61
62
# File 'lib/msf/core/auxiliary/rocketmq.rb', line 59

def get_rocketmq_version(id)
  version_list = JSON.parse(File.read(::File.join(Msf::Config.data_directory, 'rocketmq_versions_list.json'), mode: 'rb'))
  version_list.fetch(id, "UNKNOWN_VERSION_ID_#{id}").gsub('_', '.')
end

#initialize(info = {}) ⇒ Object

[View source]

11
12
13
14
# File 'lib/msf/core/auxiliary/rocketmq.rb', line 11

def initialize(info = {})
  super
  register_options([ Opt::RPORT(9876) ], Msf::Auxiliary::Rocketmq)
end

#parse_rocketmq_data(res) ⇒ Hash

This function takes a response from the send_version_request function and parses as it doesn’t get returned as proper json. It returns a Hash including RocketMQ versions info and Broker info if found

Parameters:

  • res (String)

    Response form the send_version_request request

Returns:

  • (Hash)

    Hash including RocketMQ versions info and Broker info if found

[View source]

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/msf/core/auxiliary/rocketmq.rb', line 69

def parse_rocketmq_data(res)
  # remove a response header so we have json-ish data
  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('}{')

  jsonable = []
  # patch back in the { and }
  res.each do |r|
    r += '}' unless r.end_with?('}')
    r = '{' + r unless r.start_with?('{')
    jsonable.append(r)
  end

  result = []
  jsonable.each do |j|
    res = JSON.parse(j)
    result.append(res)
  rescue JSON::ParserError
    vprint_error("Unable to parse json data: #{j}")
    next
  end

  parsed_data = {}
  result.each do |j|
    parsed_data['version'] = get_rocketmq_version(j['version']) if j['version']
    parsed_data['brokerDatas'] = j['brokerDatas'] if j['brokerDatas']
  end

  if parsed_data == {} || parsed_data['version'].nil?
    vprint_error('Unable to find version or other data within response.')
    return
  end
  parsed_data
end

#send_version_requestString?

Sends a version request to the service, and returns the data as a list of hashes or nil on error

Returns:

  • (String, nil)

    The data as a list of hashes or nil on error.

See Also:

[View source]

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/msf/core/auxiliary/rocketmq.rb', line 20

def send_version_request 
  data = '{"code":105,"extFields":{"Signature":"/u5P/wZUbhjanu4LM/UzEdo2u2I=","topic":"TBW102","AccessKey":"rocketmq2"},"flag":0,"language":"JAVA","opaque":1,"serializeTypeCurrentRPC":"JSON","version":401}'
  data_length = "\x00\x00\x00" + [data.length].pack('C')
  header = "\x00\x00\x00" + [data.length + data_length.length].pack('C')

  begin
    connect
    sock.send(header + data_length + data, 0)
    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('Error sending the rocketmq version request', error: e)
    return nil
  ensure
    disconnect
  end

  if res.nil?
    vprint_error('No response received')
    return nil
  end

  unless res.include?('{')
    vprint_error('Response contains unusable data')
    return nil
  end

  res
end