Skip to content

Commit 013781f

Browse files
committed
Land rapid7#5292, WordPress custom file version check
2 parents c540ba4 + 18791ce commit 013781f

File tree

2 files changed

+113
-1
lines changed

2 files changed

+113
-1
lines changed

lib/msf/http/wordpress/version.rb

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,28 @@ def check_theme_version_from_readme(theme_name, fixed_version = nil, vuln_introd
8282
check_version_from_readme(:theme, theme_name, fixed_version, vuln_introduced_version)
8383
end
8484

85+
# Checks a custom file for a vulnerable version
86+
#
87+
# @param [String] uripath The relative path of the file
88+
# @param [Regexp] regex The regular expression to extract the version. The first captured group must contain the version.
89+
# @param [String] fixed_version Optional, the version the vulnerability was fixed in
90+
# @param [String] vuln_introduced_version Optional, the version the vulnerability was introduced
91+
#
92+
# @return [ Msf::Exploit::CheckCode ]
93+
def check_version_from_custom_file(uripath, regex, fixed_version = nil, vuln_introduced_version = nil)
94+
res = send_request_cgi(
95+
'uri' => uripath,
96+
'method' => 'GET'
97+
)
98+
99+
# file not found
100+
unless res && res.code == 200
101+
return Msf::Exploit::CheckCode::Unknown
102+
end
103+
104+
extract_and_check_version(res.body.to_s, :custom, 'custom file', fixed_version, vuln_introduced_version, regex)
105+
end
106+
85107
private
86108

87109
def wordpress_version_helper(url, regex)
@@ -137,7 +159,7 @@ def check_version_from_readme(type, name, fixed_version = nil, vuln_introduced_v
137159
end
138160
end
139161

140-
def extract_and_check_version(body, type, item_type, fixed_version = nil, vuln_introduced_version = nil)
162+
def extract_and_check_version(body, type, item_type, fixed_version = nil, vuln_introduced_version = nil, regex = nil)
141163
case type
142164
when :readme
143165
# Try to extract version from readme
@@ -149,6 +171,8 @@ def extract_and_check_version(body, type, item_type, fixed_version = nil, vuln_i
149171
# Example line:
150172
# Version: 1.5.2
151173
version = body[/(?:Version):\s*([0-9a-z.-]+)/i, 1]
174+
when :custom
175+
version = body[regex, 1]
152176
else
153177
fail("Unknown file type #{type}")
154178
end

spec/lib/msf/http/wordpress/version_spec.rb

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,92 @@
238238
end
239239
end
240240

241+
describe '#check_version_from_custom_file' do
242+
before :each do
243+
allow(subject).to receive(:send_request_cgi) do |opts|
244+
res = Rex::Proto::Http::Response.new
245+
res.code = wp_code
246+
res.body = wp_body
247+
res
248+
end
249+
end
250+
251+
let(:wp_code) { 200 }
252+
let(:wp_body) { nil }
253+
let(:wp_path) { '/test/' }
254+
let(:wp_fixed_version) { nil }
255+
let(:wp_regex) { /(?:Version):\s*([0-9a-z.-]+)/i }
256+
257+
context 'when no file is found' do
258+
let(:wp_code) { 404 }
259+
it { expect(subject.send(:check_version_from_custom_file, wp_path, wp_regex, wp_fixed_version)).to be(Msf::Exploit::CheckCode::Unknown) }
260+
end
261+
262+
context 'when no version can be extracted from style' do
263+
let(:wp_code) { 200 }
264+
let(:wp_body) { 'invalid content' }
265+
it { expect(subject.send(:check_version_from_custom_file, wp_path, wp_regex, wp_fixed_version)).to be(Msf::Exploit::CheckCode::Detected) }
266+
end
267+
268+
context 'when version from style has arbitrary leading whitespace' do
269+
let(:wp_code) { 200 }
270+
let(:wp_fixed_version) { '1.0.1' }
271+
let(:wp_body) { 'Version: 1.0.0' }
272+
it { expect(subject.send(:check_version_from_custom_file, wp_path, wp_regex, wp_fixed_version)).to be(Msf::Exploit::CheckCode::Appears) }
273+
let(:wp_body) { 'Version:1.0.0' }
274+
it { expect(subject.send(:check_version_from_custom_file, wp_path, wp_regex, wp_fixed_version)).to be(Msf::Exploit::CheckCode::Appears) }
275+
end
276+
277+
context 'when installed version is vulnerable' do
278+
let(:wp_code) { 200 }
279+
let(:wp_fixed_version) { '1.0.1' }
280+
let(:wp_body) { 'Version: 1.0.0' }
281+
it { expect(subject.send(:check_version_from_custom_file, wp_path, wp_regex, wp_fixed_version)).to be(Msf::Exploit::CheckCode::Appears) }
282+
end
283+
284+
context 'when installed version is not vulnerable' do
285+
let(:wp_code) { 200 }
286+
let(:wp_fixed_version) { '1.0.1' }
287+
let(:wp_body) { 'Version: 1.0.2' }
288+
it { expect(subject.send(:check_version_from_custom_file, wp_path, wp_regex, wp_fixed_version)).to be(Msf::Exploit::CheckCode::Safe) }
289+
end
290+
291+
context 'when installed version is vulnerable (version range)' do
292+
let(:wp_code) { 200 }
293+
let(:wp_fixed_version) { '1.0.2' }
294+
let(:wp_introd_version) { '1.0.0' }
295+
let(:wp_body) { 'Version: 1.0.1' }
296+
it { expect(subject.send(:check_version_from_custom_file, wp_path, wp_regex, wp_fixed_version, wp_introd_version)).to be(Msf::Exploit::CheckCode::Appears) }
297+
end
298+
299+
context 'when installed version is older (version range)' do
300+
let(:wp_code) { 200 }
301+
let(:wp_fixed_version) { '1.0.1' }
302+
let(:wp_introd_version) { '1.0.0' }
303+
let(:wp_body) { 'Version: 0.0.9' }
304+
it { expect(subject.send(:check_version_from_custom_file, wp_path, wp_regex, wp_fixed_version, wp_introd_version)).to be(Msf::Exploit::CheckCode::Safe) }
305+
end
306+
307+
context 'when installed version is newer (version range)' do
308+
let(:wp_code) { 200 }
309+
let(:wp_fixed_version) { '1.0.1' }
310+
let(:wp_introd_version) { '1.0.0' }
311+
let(:wp_body) { 'Version: 1.0.2' }
312+
it { expect(subject.send(:check_version_from_custom_file, wp_path, wp_regex, wp_fixed_version, wp_introd_version)).to be(Msf::Exploit::CheckCode::Safe) }
313+
end
314+
315+
context 'when installed version is newer (text in version number)' do
316+
let(:wp_code) { 200 }
317+
let(:wp_fixed_version) { '1.5.3' }
318+
let(:wp_body) { 'Version: 2.0.0-beta1' }
319+
it { expect(subject.send(:check_version_from_custom_file, wp_path, wp_regex, wp_fixed_version)).to be(Msf::Exploit::CheckCode::Safe) }
320+
end
321+
322+
context 'when all versions are vulnerable' do
323+
let(:wp_code) { 200 }
324+
let(:wp_body) { 'Version: 1.0.0' }
325+
it { expect(subject.send(:check_version_from_custom_file, wp_path, wp_regex)).to be(Msf::Exploit::CheckCode::Appears) }
326+
end
327+
end
328+
241329
end

0 commit comments

Comments
 (0)