Last active
June 2, 2019 16:28
-
-
Save airween/f56d2f1348bc95693d1209211919f07c to your computer and use it in GitHub Desktop.
CRS regex checker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
import glob | |
import sys | |
import os | |
import secrules_parsing.secrules_parser as secrules_parser | |
import sre_parse | |
import sre_constants | |
import re | |
class colorize(object): | |
WARNING = "\033[93m" | |
FAIL = "\033[91m" | |
ENDC = "\033[0m" | |
class ReCheck(object): | |
def __init__(self): | |
self.reset() | |
def reset(self): | |
self.repeat_vector = [] | |
self.vulnerable = False | |
# item: a Python sre_parse generated AST, which is a recursive | |
# embedded list/tuple sequence | |
# depth: marks how depth are we in the tree | |
# prev_repeat: marks that the previous level contains any relevant repetation | |
def walkpattern(self, item, depth = 0, prev_repeat = False): | |
has_repeat = False | |
repeat_over10 = False | |
self.repeat_vector.append(0) | |
for i in item: | |
# if the current item is a list, tuple or a SubPattern | |
# walk it recurse | |
if type(i) in [list, tuple, sre_parse.SubPattern]: | |
self.walkpattern(i, depth+1, has_repeat) | |
# else check the symbol | |
else: | |
# if it's a repeat symbol, then set the flag for the recurse call above | |
if item.index(i) == 0 and i in [sre_constants.MIN_REPEAT, sre_constants.MAX_REPEAT]: | |
has_repeat = True | |
# if the previous level contains a repeat symbol, and here the number of repeat is | |
# infinity (eg: *) or greather that 10 (eg. {n,10}), then set to 1 the current value | |
# in vector | |
if item.index(i) == 1 and prev_repeat and (i == sre_constants.MAXREPEAT or i >= 10): | |
self.repeat_vector[-1] = 1 | |
# if the vector contains more than 1 repeat symbol, set the vulnerable variable to True | |
if sum(self.repeat_vector) >= 2: | |
self.vulnerable = True | |
del(self.repeat_vector[-1]) | |
if len(sys.argv) < 2: | |
print("Argument missing") | |
sys.exit(-1) | |
confdir = sys.argv[1] | |
# Extract all of our pathing | |
files = glob.glob("%s/*.conf" % (confdir)) | |
# Pass absolute paths because of module location | |
files = [os.path.abspath(path) for path in files] | |
models = secrules_parser.process_rules(files) | |
# create a regular expression checker instance | |
rc = ReCheck() | |
# walk through models | |
for m in models: | |
# walk through rules in model | |
for r in m.rules: | |
# get regular expressions from rules | |
exp = secrules_parser.get_rule_regex(r) | |
if exp is not None: | |
# exps is a dict, {rule_id1: regex1, rule_id2: regex2, ...} | |
ekeys = exp.keys() | |
for k in ekeys: | |
# k is a rule id | |
for e in exp[k]: | |
rc.reset() | |
# some regex isn't python re compatible | |
# try to parse it and build the AST | |
try: | |
_e = sre_parse.parse(e) | |
except re.error: | |
print(colorize.FAIL + "%s: Regex compile error" % (k) + colorize.ENDC) | |
continue | |
except: | |
# okay, what else happened? | |
print(colorize.FAIL + "Error occured till build AST: '%s'" % (e) + colorize.ENDC) | |
sys.exit(-1) | |
# walk the AST | |
rc.walkpattern(_e) | |
if rc.vulnerable == True: | |
#print(colorize.WARNING + "%s: may be vulnerable, need to check (%s)" % (k, e) + colorize.ENDC) | |
print(colorize.WARNING + "%s: may be vulnerable, need to check" % (k) + colorize.ENDC) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment