Lucene search
K

Jenkins-CI Enumeration

🗓️ 22 Oct 2013 16:16:14Reported by Jeff McCutchanType 
metasploit
 metasploit
🔗 www.rapid7.com👁 22 Views

Enumerate remote Jenkins-CI installation in unauthenticated manner, including host operating system and installation detail

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

##
# Some of this code was taken from the "jboss_vulnscan" module by: Tyler Krpata
##


require 'rexml/document'

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::HttpClient
  include Msf::Auxiliary::Scanner
  include Msf::Auxiliary::Report

  def initialize(info = {})
    super(update_info(info,
      'Name'        => 'Jenkins-CI Enumeration',
      'Description' => %q{
        This module enumerates a remote Jenkins-CI installation in an unauthenticated manner, including
        host operating system and Jenkins installation details.
      },
      'Author'      => 'Jeff McCutchan',
      'License'     => MSF_LICENSE
      ))

    register_options(
      [
        OptString.new('TARGETURI', [ true,  'The path to the Jenkins-CI application', '/jenkins/' ])
      ])
  end

  def run_host(ip)
    res = send_request_cgi(
      {
      'uri'       => target_uri.path,
      'method'    => 'GET',
      'ctype'     => 'text/plain',
    })

    unless res
      vprint_error("No response received")
      return
    end

    unless res.headers.include?('X-Jenkins')
      vprint_error("responded with #{res.code} but does not seem to be Jenkins")
      return
    end

    version = res.headers['X-Jenkins']
    print_good("#{peer} - Jenkins Version #{version}")
    report_service(
      :host  => rhost,
      :port  => rport,
      :name  => (ssl ? 'https' : 'http'),
      :proto => 'tcp'
    )

    report_web_site(
      :host  => rhost,
      :port  => rport,
      :ssl   => ssl,
      :info  => "Jenkins Version - #{version}"
    )

    # script - exploit module for this
    # view/All/newJob - can be exploited manually
    # asynchPeople - Jenkins users
    # systemInfo - system information
    apps = [
      'script',
      'view/All/newJob',
      'asynchPeople/',
      'systemInfo'
    ]
    apps.each do |app|
      check_app(app)
    end
  end

  def check_app(app)
    uri_path = normalize_uri(target_uri.path, app)
    res = send_request_cgi({
      'uri'       => uri_path,
      'method'    => 'GET',
      'ctype'     => 'text/plain',
    })
    unless res
      vprint_error("Timeout")
      return
    end

    case res.code
    when 200
      print_good("#{full_uri} - #{uri_path} does not require authentication (200)")
      report_note({
        :type  => "jenkins_path",
        :host  => rhost,
        :port  => rport,
        :proto => 'tcp',
        :data  => "#{full_uri} - #{uri_path} does not require authentication (200)",
        :update => :unique_data
      })
      case app
      when "systemInfo"
        parse_system_info(res.body)
      when "script"
        report_vuln(
          :host        => rhost,
          :port        => rport,
          :proto       => 'tcp',
          :sname       => (ssl ? 'https' : 'http'),
          :name        => "Jenkins Script-Console Java Execution",
          :info        => "Module #{self.fullname} confirmed access to the Jenkins Script Console with no authentication"
        )
      end
    when 403
      print_status("#{uri_path} restricted (403)")
    when 401
      print_status("#{uri_path} requires authentication (401): #{res.headers['WWW-Authenticate']}")
    when 404
      print_status("#{uri_path} not found (404)")
    when 301
      print_status("#{uri_path} is redirected (#{res.code}) to #{res.headers['Location']} (not following)")
    when 302
      print_status("#{uri_path} is redirected (#{res.code}) to #{res.headers['Location']} (not following)")
    else
      print_status("#{uri_path} Don't know how to handle response code #{res.code}")
    end
  end

  def parse_system_info(body)
    vprint_status("Getting useful information from systemInfo")
    infos = {
      "os.name"            => nil,
      "os.version"         => nil,
      "sun.os.patch.level" => nil,
      "os.arch"            => nil,
      "user.name"          => nil,
      "USERDOMAIN"         => nil,
      "user.home"          => nil,
      "user.language"      => nil,
      "user.country"       => nil,
      "user.timezone"      => nil,
      "COMPUTERNAME"       => nil,
      "SystemDrive"        => nil,
      "TEMP"               => nil,
      "TMP"                => nil,
      "SHELL"              => nil
    }

    # remove unclosed tags for REXML
    body.gsub!('<wbr>', '')
    body.gsub!('<br>', '')
    doc = REXML::Document.new(body)
    tds = doc.get_elements("//td")
    tds.each_index do |idx|
      td = tds[idx].get_text.to_s.strip
      infos[td] = tds[idx+1].get_text.to_s.strip if infos.has_key?(td)
    end

    fprint = {}
    jinfo  = {}

    # print out the goodies
    infos.each do |k, v|
      next if v.nil?
      v = v.strip
      next if v.length == 0

      jinfo[k.gsub(/\s+/, '_')] = v

      case k
      when "os.name"
        vprint_line("   OS: #{v}")
        fprint['os.product'] = v
      when "os.version"
        vprint_line("   OS Version: #{v}")
        fprint['os.version'] = v
      when "sun.os.patch.level"
        vprint_line("   Patch Level: #{v}")
      when "os.arch"
        vprint_line("   Arch: #{v}")
        fprint['os.arch'] = v
      when "user.name"
        vprint_line("   User: #{v}")
      when "USERDOMAIN"
        vprint_line("   Domain: #{v}")
        fprint['host.domain'] = v
      when "COMPUTERNAME"
        vprint_line("   Computer Name: #{v}")
        fprint['host.name'] = v
      when "SystemDrive"
        vprint_line("   System Drive: #{v}")
      when "SHELL"
        vprint_line("   Shell: #{v}")
      when "TEMP"
        vprint_line("   Temp Directory: #{v}")
      when "TMP"
        vprint_line("   Temp Directory: #{v}")
      when "user.home"
        vprint_line("   Home Directory: #{v}")
      when "user.language"
        vprint_line("   Language: #{v}")
        fprint['os.language'] = v
      when "user.country"
        vprint_line("   Country: #{v}")
      when "user.timezone"
        vprint_line("   Timezone: #{v}")
      end
    end

    # Report a fingerprint.match for OS fingerprinting support, tied to this service
    report_note(:host => rhost, :port => rport, :proto => 'tcp', :ntype => 'fingerprint.match', :data => fprint)

    # Report a jenkins information note for future analysis, tied to this service
    report_note(:host => rhost, :port => rport, :proto => 'tcp', :ntype => 'jenkins.info', :data => jinfo)

    vprint_line
  end
end

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