├── .idea ├── .name ├── encodings.xml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── modules.xml ├── pandb-query.iml ├── scopes │ └── scope_settings.xml ├── vcs.xml └── workspace.xml ├── README.md └── pandb-query.py /.idea/.name: -------------------------------------------------------------------------------- 1 | testpandb -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 37 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/pandb-query.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 48 | 49 | 54 | 55 | 56 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 94 | 95 | 96 | 97 | 100 | 101 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 1413396900332 128 | 1413396900332 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 160 | 163 | 164 | 165 | 167 | 168 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pandb-query 2 | =========== 3 | 4 | A tool for bulk URL queries against Palo Alto Networks' PAN-DB cloud database 5 | 6 | Requirements: 7 | ---- 8 | * pan-python - Multi-tool set for Palo Alto Networks PAN-OS, Panorama and WildFire 9 | 10 | Usage: 11 | ---- 12 |
13 | pandb-query.py [-h] [-u USERNAME] [-p PASSWORD] [-f FIREWALL] [-t TAG]
14 |                       [-i INFILE] [-o OUTFILE]
15 | 
16 | optional arguments:
17 |   -h, --help            show this help message and exit
18 |   -u USERNAME, --username USERNAME
19 |                         administrator username
20 |   -p PASSWORD, --password PASSWORD
21 |                         administrator password
22 |   -f FIREWALL, --firewall FIREWALL
23 |                         firewall hostname or IP address
24 |   -t TAG, --tag TAG     firewall tag from the .panrc file
25 |   -i INFILE, --infile INFILE
26 |                         input file of URLs
27 |   -o OUTFILE, --outfile OUTFILE
28 |                         output file
29 | 
30 | -------------------------------------------------------------------------------- /pandb-query.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import getpass 3 | from pan.xapi import * 4 | 5 | 6 | ''' 7 | Script Name: urlcheck 8 | Author: Robert Hagen - https://github.com/stealthllama/pandb-query 9 | Adapted by: Arlo Hollingshad 10 | Version Number: 1.0 11 | Language Version: 3.8 12 | Description of Function: This script takes a file with a list of URLs and checks the Palo Alto URL database on the 13 | local firewall, and the Cloud URL database 14 | ''' 15 | 16 | 17 | def make_parser(): 18 | """ 19 | This function takes the command line inputs and returns them in variables the rest of the script will use. 20 | :return: All arguments passed into the Command line 21 | """ 22 | # Parse the arguments 23 | parser = argparse.ArgumentParser(description="Bulk PAN-DB URL lookup utility") 24 | parser.add_argument("-u", "--username", help="administrator username") 25 | parser.add_argument("-p", "--password", help="administrator password") 26 | parser.add_argument("-f", "--firewall", help="firewall hostname or IP address") 27 | parser.add_argument("-t", "--tag", help="firewall tag from the .panrc file", default='') 28 | parser.add_argument("-i", "--infile", help="input file of URLs", default='') 29 | parser.add_argument("-o", "--outfile", help="output file", default='') 30 | args = parser.parse_args() 31 | return args 32 | 33 | 34 | def read_file(filename): 35 | """ 36 | This function takes a file name, and opens a file in read-only mode. 37 | :param filename: Name of the file to be opened 38 | :return: File object in read-only mode 39 | """ 40 | try: 41 | infilehandle = open(filename, 'r') 42 | return infilehandle 43 | except IOError: 44 | print("Error: Cannot open file %s" % filename) 45 | 46 | 47 | def write_file(filename): 48 | """ 49 | This function takes a file name and opens a file in write mode. 50 | :param filename: Name of the file to be opened 51 | :return: File object in write mode 52 | """ 53 | try: 54 | outfilehandle = open(filename, 'w') 55 | return outfilehandle 56 | except IOError: 57 | print("Error: Cannot open file %s" % filename) 58 | 59 | 60 | def make_key(user, pswd, firewall): 61 | """ 62 | 63 | :param user: API-enabled username for connecting to Palo Alto firewall 64 | :param pswd: Password for the user specified above 65 | :param firewall: IP address of the firewall to be connected to 66 | :return: A connection object, which includes the API key for making new calls to the firewall. 67 | """ 68 | newconn = PanXapi(api_username=user, api_password=pswd, hostname=firewall) 69 | newconn.keygen() 70 | return newconn 71 | 72 | 73 | def get_url(fwconn, url): 74 | """ 75 | 76 | :param fwconn: Connection object, needs to include the API key 77 | :param url: URL to find categories for 78 | :return: Output of test url command 79 | """ 80 | try: 81 | fwconn.op(cmd="%s" % url) 82 | except PanXapiError: 83 | print('Bad URL: ', url) 84 | return fwconn 85 | 86 | 87 | def make_pretty(elem): 88 | """ 89 | This function takes the result of the URL lookup, and returns the local and cloud discovered categories 90 | :param elem: URL lookup result. Output from get_url function. 91 | :returns local_category: URL category returned by URL database locally on firewall 92 | :returns cloud_category: URL category returned by Palo Alto Cloud URL database 93 | """ 94 | try: 95 | result = elem.text.split('\n') 96 | local = result[0] 97 | cloud = result[1] 98 | except AttributeError: 99 | return 'not-resolved' 100 | local_category = local.split(' ')[1] 101 | cloud_category = cloud.split(' ')[1] 102 | return local_category, cloud_category 103 | 104 | 105 | def main(**kwargs): 106 | """ 107 | This the main script activity. It pulls in the 108 | :param kwargs: Only used when not running in command line mode. Valid Keyword arugments: 109 | firewall: IP address/hostname for the firewall to connect to 110 | username: API enabled user to sign in to the above firewall 111 | password: Password for the user specified above 112 | infile: Input file with list of URLs 113 | outfile: Output file for URL lookup results (default results.csv) 114 | """ 115 | # Grab the args 116 | myargs = make_parser() 117 | bad_urls = write_file('BadUrls.txt') 118 | # Open the input file 119 | if 'infile' in kwargs: 120 | infile = read_file(kwargs['infile']) 121 | with read_file(kwargs['infile']) as file: 122 | line_count = sum(1 for line in file) 123 | elif myargs.infile: 124 | infile = read_file(myargs.infile) 125 | with read_file(myargs.infile) as file: 126 | line_count = sum(1 for line in file) 127 | else: 128 | infile = sys.stdin 129 | with read_file(sys.stdin) as file: 130 | line_count = sum(1 for line in file) 131 | 132 | # Open the output file 133 | if 'outfile' in kwargs: 134 | outfile = write_file(kwargs['outfile']) 135 | elif myargs.outfile: 136 | outfile = write_file(myargs.outfile) 137 | else: 138 | outfile = sys.stdout 139 | 140 | # Open a firewall API connection 141 | if 'tag' in kwargs: 142 | myconn = PanXapi(tag=kwargs['tag']) 143 | elif myargs.tag: 144 | # Use the .panrc API key 145 | myconn = PanXapi(tag=myargs.tag) 146 | else: 147 | # Generate the API key 148 | # if 'username' 149 | if 'firewall' and 'username' and 'password' in kwargs: 150 | myconn = make_key(kwargs['username'], kwargs['password'], kwargs['firewall']) 151 | else: 152 | myconn = make_key(myargs.username, myargs.password, myargs.firewall) 153 | 154 | # Iterate through the URL list, perform the lookup, and print the result 155 | outfile.write('URL,Local Category,Cloud Category\n') 156 | count = 0 157 | for myurl in infile: 158 | count += 1 159 | get_url(myconn, myurl.strip()) 160 | try: 161 | local_cat, cloud_cat = make_pretty(myconn.element_result) 162 | outfile.write(f'{myurl.strip()},{local_cat},{cloud_cat}\n') 163 | outfile.flush() 164 | except ValueError: 165 | bad_urls.write(f'{myurl.strip()}\n') 166 | if count % 10 == 0: 167 | print(f'{count}/{line_count}') 168 | 169 | # Close the input file 170 | if infile is not sys.stdin: 171 | infile.close() 172 | 173 | # Close the output file 174 | if outfile is not sys.stdout: 175 | outfile.close() 176 | 177 | 178 | # Enables script to be run as a traditional script rather than the command line 179 | if __name__ == '__main__': 180 | args = make_parser() 181 | if not len(sys.argv) > 1: 182 | hostname = input('Enter the IP of the firewall: ') 183 | username = input('Enter the username: ') 184 | password = getpass.getpass() 185 | file_in = input('Enter the text file with URLs: ') 186 | main(firewall=hostname, username=username, password=password, infile=file_in, outfile='results.csv') 187 | else: 188 | main() 189 | --------------------------------------------------------------------------------