Skip to content

Commit b8c9ef9

Browse files
committed
Land rapid7#4003, @nstarke's Login Scanner for WD MyBook Live NAS
2 parents 313c240 + 765b5e6 commit b8c9ef9

File tree

3 files changed

+162
-0
lines changed

3 files changed

+162
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
require 'metasploit/framework/login_scanner/http'
2+
3+
module Metasploit
4+
module Framework
5+
module LoginScanner
6+
7+
# Western Digital MyBook Live login scanner
8+
class MyBookLive < HTTP
9+
10+
# Inherit LIKELY_PORTS,LIKELY_SERVICE_NAMES, and REALM_KEY from HTTP
11+
CAN_GET_SESSION = true
12+
DEFAULT_PORT = 80
13+
PRIVATE_TYPES = [ :password ]
14+
15+
# (see Base#set_sane_defaults)
16+
def set_sane_defaults
17+
self.uri = '/UI/login' if self.uri.nil?
18+
self.method = 'POST' if self.method.nil?
19+
20+
super
21+
end
22+
23+
def attempt_login(credential)
24+
result_opts = {
25+
credential: credential,
26+
host: host,
27+
port: port,
28+
protocol: 'tcp'
29+
}
30+
if ssl
31+
result_opts[:service_name] = 'https'
32+
else
33+
result_opts[:service_name] = 'http'
34+
end
35+
begin
36+
cred = Rex::Text.uri_encode(credential.private)
37+
body = "data%5BLogin%5D%5Bowner_name%5D=admin&data%5BLogin%5D%5Bowner_passwd%5D=#{cred}"
38+
cli = Rex::Proto::Http::Client.new(host, port, {}, ssl, ssl_version)
39+
cli.connect
40+
req = cli.request_cgi(
41+
'method' => method,
42+
'uri' => uri,
43+
'data' => body
44+
)
45+
res = cli.send_recv(req)
46+
if res && res.code == 302 && res.headers['location'] && res.headers['location'].include?('UI')
47+
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.headers)
48+
elsif res.nil?
49+
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: 'No response')
50+
else
51+
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res.headers)
52+
end
53+
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
54+
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT)
55+
end
56+
Result.new(result_opts)
57+
end
58+
end
59+
end
60+
end
61+
end
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'metasploit/framework/credential_collection'
8+
require 'metasploit/framework/login_scanner/mybook_live'
9+
10+
class Metasploit3 < Msf::Auxiliary
11+
include Msf::Auxiliary::Scanner
12+
include Msf::Exploit::Remote::HttpClient
13+
include Msf::Auxiliary::Report
14+
include Msf::Auxiliary::AuthBrute
15+
16+
def initialize
17+
super(
18+
'Name' => 'Western Digital MyBook Live Login Utility',
19+
'Description' => 'This module simply attempts to login to a Western Digital MyBook Live instance using a specific user/pass.',
20+
'Author' => [ 'Nicholas Starke <starke.nicholas[at]gmail.com>' ],
21+
'License' => MSF_LICENSE
22+
)
23+
24+
register_options(
25+
[
26+
Opt::RPORT(80)
27+
], self.class)
28+
29+
register_autofilter_ports([ 80 ])
30+
31+
# username is hardcoded into application
32+
deregister_options('RHOST', 'USERNAME', 'USER_FILE', 'USER_AS_PASS', 'DB_ALL_USERS')
33+
end
34+
35+
def setup
36+
super
37+
# They must select at least blank passwords, provide a pass file or a password
38+
one_required = %w(BLANK_PASSWORDS PASS_FILE PASSWORD)
39+
unless one_required.any? { |o| datastore.has_key?(o) && datastore[o] }
40+
fail_with(Failure::BadConfig, "Invalid options: One of #{one_required.join(', ')} must be set")
41+
end
42+
if !datastore['PASS_FILE']
43+
if !datastore['BLANK_PASSWORDS'] && datastore['PASSWORD'].blank?
44+
fail_with(Failure::BadConfig, "PASSWORD or PASS_FILE must be set to a non-empty string if not BLANK_PASSWORDS")
45+
end
46+
end
47+
end
48+
49+
def run_host(ip)
50+
cred_collection = Metasploit::Framework::CredentialCollection.new(
51+
blank_passwords: datastore['BLANK_PASSWORDS'],
52+
pass_file: datastore['PASS_FILE'],
53+
password: datastore['PASSWORD'],
54+
username: 'admin'
55+
)
56+
57+
scanner = Metasploit::Framework::LoginScanner::MyBookLive.new(
58+
host: ip,
59+
port: rport,
60+
proxies: datastore['PROXIES'],
61+
cred_details: cred_collection,
62+
stop_on_success: datastore['STOP_ON_SUCCESS'],
63+
connection_timeout: 10,
64+
user_agent: datastore['UserAgent'],
65+
vhost: datastore['VHOST']
66+
)
67+
68+
if ssl
69+
scanner.ssl = datastore['SSL']
70+
scanner.ssl_version = datastore['SSLVERSION']
71+
end
72+
73+
scanner.scan! do |result|
74+
credential_data = result.to_h
75+
credential_data.merge!(
76+
module_fullname: fullname,
77+
workspace_id: myworkspace_id
78+
)
79+
if result.success?
80+
credential_core = create_credential(credential_data)
81+
credential_data[:core] = credential_core
82+
create_credential_login(credential_data)
83+
84+
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
85+
else
86+
invalidate_login(credential_data)
87+
vprint_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status})"
88+
end
89+
end
90+
end
91+
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
require 'spec_helper'
2+
require 'metasploit/framework/login_scanner/mybook_live'
3+
4+
describe Metasploit::Framework::LoginScanner::MyBookLive do
5+
6+
it_behaves_like 'Metasploit::Framework::LoginScanner::Base', has_realm_key: true, has_default_realm: false
7+
it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket'
8+
it_behaves_like 'Metasploit::Framework::LoginScanner::HTTP'
9+
10+
end

0 commit comments

Comments
 (0)