-
Notifications
You must be signed in to change notification settings - Fork 1
/
check_vulns.py
199 lines (155 loc) · 8.06 KB
/
check_vulns.py
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
128
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
198
199
#!/usr/bin/python3
# Download all known vulns from:
# https://security-tracker.debian.org/tracker/data/json
# if not already downloaded.
#
# Get list of installed packages on the system (or from remote systems)
#
# Check if there is a known vulnerability present in installed packages
# and alert
from subprocess import getoutput as run_shell_cmd
from configparser import ConfigParser as confparsing
from argparse import ArgumentParser as argsparsing
from json import load as jsonload
from urllib import request as urlrequest
from os import path as ospath
from re import search as research
from sys import exit as sysexit
vulnpkgs = ''
config_filename = 'check_vulns.conf'
def get_debian_tracker_file(local_file):
''' Get the json file from https://security-tracker.debian.org/tracker/data/json '''
URL='https://security-tracker.debian.org/tracker/data/json'
if ospath.isfile(local_file):
return
with urlrequest.urlopen(URL) as response, open(local_file, 'wb') as outfile:
datain = response.read()
outfile.write(datain)
return
def read_debian_tracker_file(local_file):
''' Populate vulnpkgs variable with tracker file contents '''
with open(local_file, "r") as jsonfile:
return jsonload(jsonfile)
def get_os_packages_installed(host):
''' Get package names and versions from installed packages on local or remote system '''
pkgdict = {}
if host == 'localhost':
# localhost aka same machine this script is running on:
res = run_shell_cmd("/usr/bin/dpkg-query -W -f='${binary:Package}\t${Version}\n'")
os_version = run_shell_cmd("/bin/cat /etc/os-release | /bin/grep 'VERSION='")
else:
# remote host aka other machine than where this script is running:
res = run_shell_cmd("ssh {} ".format(host)+'"'+"/usr/bin/dpkg-query -W -f='\${binary:Package}\\t\${Version}\\n'"+'"')
os_version = run_shell_cmd("ssh {} ".format(host)+'"'+"/bin/cat /etc/os-release | /bin/grep 'VERSION='"+'"')
try:
check_ssh_result = research('(^ssh: Could not resolve hostname).*$', res)
if check_ssh_result.group(1):
print('Host in configfile: {} could not be contacted. Pls fix access and try again.'.format(host))
sysexit(2)
except:
pass
print('\n================\nHost: {}\n================'.format(host))
rel = research('\((\w+)\)"', os_version)
os_release = rel.group(1)
for elem in res.split('\n'):
out = research('^(\S+)\t(\S+)$', elem)
pkg = out.group(1)
ver = out.group(2)
pkgdict[pkg] = ver
return os_release, pkgdict
def match_os_vs_known_vulns(vulnpkgs, pkgdict, os_release, args):
''' Match the OS packages against known vulns '''
sec_os_release = os_release + '-security'
local_or_remote = ''
vuln_description = ''
for vuln_pkg_name, values1 in vulnpkgs.items():
packagevulns = vulnpkgs[vuln_pkg_name]
if vuln_pkg_name in pkgdict:
""" If we're here, the package is installed on system being checked """
for vuln_pkg_cve, values2 in packagevulns.items():
try:
os_release_dict = packagevulns[vuln_pkg_cve]['releases'][os_release]
except KeyError:
continue
vuln_urgency = os_release_dict['urgency']
try:
vuln_description = packagevulns[vuln_pkg_cve]['description']
except:
vuln_description = 'no description'
try:
vuln_repository = os_release_dict['repositories'][sec_os_release]
except:
vuln_repository = os_release_dict['repositories'][os_release]
try:
local_or_remote = packagevulns[vuln_pkg_cve]['scope']
except KeyError:
local_or_remote = 'not disclosed'
# This is where changes need to be to reflect users wants/need regarding output
outstring = 'Package: {} - Fixed in version: {} '.format(vuln_pkg_name, vuln_repository)
# Add CVE/DSA to output according to CVE/DSA argument (on, off)
if args.c == 'on':
outstring += '- CVE/(NO)DSA: {} '.format(vuln_pkg_cve)
# Add to output according to Status argument (open, resolved, all)
if args.s == 'all':
outstring += '- Status: {} '.format(os_release_dict['status'])
elif args.s == 'open':
if os_release_dict['status'] == 'open':
outstring += '- Status: {} '.format(os_release_dict['status'])
else:
break
elif args.s == 'resolved':
if os_release_dict['status'] == 'resolved':
outstring += '- Status: {} '.format(os_release_dict['status'])
else:
break
# Add to output according to Scope argument (local, remote, all)
if args.e == 'all':
outstring += '- Scope: {} '.format(local_or_remote)
elif args.e == 'local':
if local_or_remote == 'local':
outstring += '- Scope: {} '.format(local_or_remote)
else:
break
elif args.e == 'remote':
if local_or_remote == 'remote':
outstring += '- Scope: {} '.format(local_or_remote)
else:
break
# Add to output if Urgency argument is on (on, off)
if args.u == 'on':
outstring += '- Urgency: {} '.format(vuln_urgency)
# Add to output if Description argument is on (on, off)
if args.d == 'on':
outstring += '- Description: {} '.format(vuln_description)
# Add to notice about keeping an eye out for updates to vulnerable package
if args.o == 'on':
if pkgdict[vuln_pkg_name] != vuln_repository:
outstring += '\n--> You need to watch for upgrades for: {} <--\n'.format(vuln_pkg_name)
else:
outstring=''
if outstring:
print(outstring)
def main(argv):
''' Main program here '''
config = confparsing()
config.read(config_filename)
parser = argsparsing(description=r"Script to find vulns that are known, but not yet patched. This will give you an overview of which packages are part of the attack surface.")
parser.add_argument('-c', type=str, default='on', choices=['on', 'off'], help='Show CVE/DSA identification number of vulnerability.')
parser.add_argument('-d', type=str, default='off', choices=['on', 'off'], help='Show description of vulnerability.')
parser.add_argument('-e', type=str, default='all', choices=['none', 'local', 'remote', 'all'], help='Show local and/or remote exploitable vulns.')
parser.add_argument('-s', type=str, default='all', choices=['none', 'open', 'resolved', 'all'], help='Show open and/or resolved vulns.')
parser.add_argument('-u', type=str, default='on', choices=['on', 'off'], help='Show the urgency.')
parser.add_argument('-o', type=str, default='on', choices=['on', 'off'], help='Show only packages if there is a difference in version - not taking status into account.')
args = parser.parse_args()
# Get variables from config-file
local_filename = config['config']['temp_file']
list_of_hosts = config['config']['hosts'].split(' ')
get_debian_tracker_file(local_filename)
vulnpkgs = read_debian_tracker_file(local_filename)
# Check host by host for known vulns against the different packages
# installed on the different hosts.
for host_by_host in list_of_hosts:
os_release, pkgdict = get_os_packages_installed(host_by_host)
match_os_vs_known_vulns(vulnpkgs, pkgdict, os_release, args)
if __name__ == '__main__':
main('')