├── .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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
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 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
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 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
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 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
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 |
--------------------------------------------------------------------------------