def test_explain_rule(self): rv = self.app.get('/sandbox/explain_rule/') self.assertEqual(rv.status_code, 302) self.assertEqual(urlparse(rv.location).path, '/sandbox/') _rule = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first() rv = self.app.get('/sandbox/explain_rule/?rule={0}'.format(_rule.sid + 1), follow_redirects=True) self.assertIn('Not rule with id {0}'.format(_rule.sid + 1), str(rv.data)) rv = self.app.get('/sandbox/explain_rule/?rule={0}'.format(_rule.sid)) self.assertEqual(rv.status_code, 200) self.assertIn(_rule.explain(), str(rv.data)) rv = self.app.get('/sandbox/explain_rule/?rule=lol') self.assertEqual(rv.status_code, 302) self.assertEqual(urlparse(rv.location).path, '/sandbox/') data = 'MainRule "rx:^POUET$" "msg: sqli" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005 ;' rv = self.app.post('/sandbox/explain_rule/', data={'rule': data}) self.assertEqual(rv.status_code, 200) _rule = NaxsiRules() _rule.parse_rule(data) self.assertIn(_rule.explain(), str(rv.data)) data = 'MainRule "lol:^POUET$" "msg: sqli" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005 ;' rv = self.app.post('/sandbox/explain_rule/', data={'rule': data}) self.assertEqual(rv.status_code, 200)
def test_parse_rule(self): rule_parser = NaxsiRules() errors, warnings, ret = rule_parser.parse_rule('MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop"' '"msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;') self.assertEqual(warnings, ['Cookie in $HEADERS_VAR:Cookie is not lowercase. naxsi is case-insensitive', 'rule IDs below 10k are reserved (1000)']) errors, warnings, ret = rule_parser.parse_rule('BasicRule "rx:^ratata$" "mz:$URL:/foobar" ' 'id:4200001 "s:$SQL:8";') self.assertIn('$URL', str(errors)) self.assertIn("The rule/whitelist doesn\'t target any zone.", str(errors)) self.assertIn("Parsing of element \'mz:$URL:/foobar\' failed.", str(errors)) errors, warnings, ret = rule_parser.parse_rule('"rx:^ratata$" "mz:$URL:/foobar|$BODY_VAR_X:^tutu$"' 'id:4200001 "s:$SQL:8";') self.assertIn('No mainrule/basicrule keyword.', str(errors)) errors, warnings, ret = rule_parser.parse_rule('MainRule BasicRule "rx:select"' '"msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;') self.assertEqual(['Both BasicRule and MainRule are present.'], errors) errors, warnings, ret = rule_parser.parse_rule('MainRule "rx:select"' '"msg:sql keywords" "mz:BODY" "s:$SQL:4" id:1000 "wrong:LOL";') self.assertIn("'wrong:LOL' is an invalid element and thus can not be parsed.", str(errors)) errors, warnings, ret = rule_parser.parse_rule('MainRule "rx:select"' '"msg:sql keywords" "mz:BODY" "s:$SQL:4" "id:non_numeric";') self.assertEqual(['id:non_numeric is not numeric', "Parsing of element 'id:non_numeric' failed."], errors) errors, warnings, ret = rule_parser.parse_rule('MainRule "rx:select" "mz:wrong" "msg:sql keywords" "s:$SQL:4" "id:10000";') self.assertIn("'wrong' is not a known sub-part of mz : ", str(errors)) self.assertIn("Parsing of element 'mz:wrong' failed.", str(errors))
def new(): latest_sid = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first() if latest_sid is None: sid = 200001 else: sid = latest_sid.sid + 1 if request.method == "GET": _rulesets = NaxsiRuleSets.query.all() return render_template("rules/new.html", mz=naxsi_mz, rulesets=_rulesets, score=naxsi_score, latestn=sid) # create new rule logging.debug('Posted new request: %s', request.form) mz = "|".join( filter( len, request.form.getlist("mz") + request.form.getlist("custom_mz_val"))) score = request.form.get("score", "") score += ':' score += request.form.get("score_%s" % request.form.get("score", ""), "") nrule = NaxsiRules(request.form.get("msg", ""), request.form.get("detection", ""), mz, score, sid, request.form.get("ruleset", ""), request.form.get("rmks", ""), "1", request.form.get("negative", "") == 'checked', int(time())) errors, warnings = nrule.validate() if errors: for error in errors: flash(error, category='error') return redirect(url_for("rules.new")) elif warnings: for warning in warnings: flash(warning, category='warnings') db.session.add(nrule) db.session.commit() return redirect("/rules/edit/%s" % sid)
def test_parse_rule(self): rule_parser = NaxsiRules() errors, warnings, ret = rule_parser.parse_rule( 'MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop"' '"msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;' ) self.assertEqual(warnings, [ 'Cookie in $HEADERS_VAR:Cookie is not lowercase. naxsi is case-insensitive', 'rule IDs below 10k are reserved (1000)' ]) errors, warnings, ret = rule_parser.parse_rule( 'BasicRule "rx:^ratata$" "mz:$URL:/foobar" ' 'id:4200001 "s:$SQL:8";') self.assertIn('$URL', str(errors)) self.assertIn("The rule/whitelist doesn\'t target any zone.", str(errors)) self.assertIn("Parsing of element \'mz:$URL:/foobar\' failed.", str(errors)) errors, warnings, ret = rule_parser.parse_rule( '"rx:^ratata$" "mz:$URL:/foobar|$BODY_VAR_X:^tutu$"' 'id:4200001 "s:$SQL:8";') self.assertIn('No mainrule/basicrule keyword.', str(errors)) errors, warnings, ret = rule_parser.parse_rule( 'MainRule BasicRule "rx:select"' '"msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;' ) self.assertEqual(['Both BasicRule and MainRule are present.'], errors) errors, warnings, ret = rule_parser.parse_rule( 'MainRule "rx:select"' '"msg:sql keywords" "mz:BODY" "s:$SQL:4" id:1000 "wrong:LOL";') self.assertIn( "'wrong:LOL' is an invalid element and thus can not be parsed.", str(errors)) errors, warnings, ret = rule_parser.parse_rule( 'MainRule "rx:select"' '"msg:sql keywords" "mz:BODY" "s:$SQL:4" "id:non_numeric";') self.assertEqual([ 'id:non_numeric is not numeric', "Parsing of element 'id:non_numeric' failed." ], errors) errors, warnings, ret = rule_parser.parse_rule( 'MainRule "rx:select" "mz:wrong" "msg:sql keywords" "s:$SQL:4" "id:10000";' ) self.assertIn("'wrong' is not a known sub-part of mz : ", str(errors)) self.assertIn("Parsing of element 'mz:wrong' failed.", str(errors))
def import_rules(): if request.method == "GET": return render_template("rules/import.html", rulesets=NaxsiRuleSets.query.all()) ruleset = request.form.get("ruleset", "") upfile = request.files.get('file', '') if not ruleset or not upfile: flash("Missing rule file and/or ruleset name.", 'error') return redirect(url_for("rules.new")) raw = upfile.stream.getvalue() success_imports = 0 potential_imports = 0 for potential_rule in raw.split('\n'): potential_rule = potential_rule.strip() if not potential_rule or potential_rule.startswith( "#" ): # Save ourselves some time by not trying to import comments continue potential_imports += 1 _rule = NaxsiRules(ruleset=ruleset, active=1) errors, warnings, rule = _rule.parse_rule(potential_rule) if errors: flash("Fail to parse %s: %s" % (potential_rule, ', '.join(errors)), 'error') continue else: db.session.add(rule) try: db.session.commit() success_imports += 1 except IntegrityError: db.session.rollback() flash("Rule %s has not an unique ID" % rule['sid']) flash("Imported %d rules out of %d lines in ruleset %s" % (success_imports, potential_imports, ruleset)) return render_template("rules/import.html", rulesets=NaxsiRuleSets.query.all())
def explain_rule(): errors = warnings = list() rule_get = request.args.get('rule', '') rule_post = request.form.get("rule", '') if rule_get.isdigit(): # explain a rule by id _rule = NaxsiRules.query.filter(NaxsiRules.sid == rule_get).first() if _rule is None: flash('Not rule with id %s' % rule_get) return redirect(url_for("sandbox.index")) elif rule_get is not '': flash('Please provide a numeric id') return redirect(url_for("sandbox.index")) elif not rule_post: flash('Please provide a rule') return redirect(url_for("sandbox.index")) else: _rule = NaxsiRules() errors, warnings, rdict = _rule.parse_rule(rule_post) _rule = NaxsiRules() _rule.from_dict(rdict) _rule.errors = errors _rule.warnings = warnings if _rule.errors: flash('You rule is wrong', 'error') return render_template("misc/sandbox.html") if 'visualise_rule' in request.form: if _rule.detection.startswith('rx:'): return redirect('https://regexper.com/#' + _rule.detection[3:]) else: flash('The rule is not a regexp, so you can not visualize it.', category='error') if errors: for error in errors: flash(error, category='error') if warnings: for warnings in warnings: flash(warnings, category='warning') return render_template("misc/sandbox.html", rule_explaination=_rule.explain(), rule=_rule)
def test_del_rule(self): _rule = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first() db.session.add( NaxsiRules(u'PIF', 'str:test', u'BODY', u'$SQL:8', _rule.sid + 1, u'WEB_APPS', u'f hqewifueiwf hueiwhf uiewh fiewh fhw', '1', True, 1457101045)) _sid = _rule.sid + 1 rv = self.app.get('/rules/del/%d' % _sid) self.assertEqual(rv.status_code, 302) _rule = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first() self.assertEqual(_rule.sid, _rule.sid)
def new(): latest_sid = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first() if latest_sid is None: sid = 200001 else: sid = latest_sid.sid + 1 if request.method == "GET": _rulesets = NaxsiRuleSets.query.all() return render_template("rules/new.html", mz=naxsi_mz, rulesets=_rulesets, score=naxsi_score, latestn=sid) # create new rule logging.debug('Posted new request: %s', request.form) mz = "|".join(filter(len, request.form.getlist("mz") + request.form.getlist("custom_mz_val"))) score = request.form.get("score", "") score += ':' score += request.form.get("score_%s" % request.form.get("score", ""), "") nrule = NaxsiRules(request.form.get("msg", ""), request.form.get("detection", ""), mz, score, sid, request.form.get("ruleset", ""), request.form.get("rmks", ""), "1", request.form.get("negative", "") == 'checked', int(time())) errors, warnings = nrule.validate() if errors: for error in errors: flash(error, category='error') return redirect(url_for("rules.new")) elif warnings: for warning in warnings: flash(warning, category='warnings') db.session.add(nrule) db.session.commit() return redirect("/rules/edit/%s" % sid)
def import_rules(): if request.method == "GET": return render_template("rules/import.html", rulesets=NaxsiRuleSets.query.all()) ruleset = request.form.get("ruleset", "") upfile = request.files.get('file', '') if not ruleset or not upfile: flash("Missing rule file and/or ruleset name.", 'error') return redirect(url_for("rules.new")) raw = upfile.stream.getvalue() success_imports = 0 potential_imports = 0 for potential_rule in raw.split('\n'): potential_rule = potential_rule.strip() if not potential_rule or potential_rule.startswith("#"): # Save ourselves some time by not trying to import comments continue potential_imports += 1 _rule = NaxsiRules(ruleset=ruleset, active=1) errors, warnings, rule = _rule.parse_rule(potential_rule) if errors: flash("Fail to parse %s: %s" % (potential_rule, ', '.join(errors)), 'error') continue else: db.session.add(rule) try: db.session.commit() success_imports += 1 except IntegrityError: db.session.rollback() flash("Rule %s has not an unique ID" % rule['sid']) flash("Imported %d rules out of %d lines in ruleset %s" % (success_imports, potential_imports, ruleset)) return render_template("rules/import.html", rulesets=NaxsiRuleSets.query.all())
def test_parse_rule(self): rule_parser = NaxsiRules() rv = rule_parser.parse_rule('MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop"' '"msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;') self.assertEqual(rv, True) self.assertEqual(rule_parser.warnings, ['Cookie in $HEADERS_VAR:Cookie is not lowercase. naxsi is case-insensitive', 'rule IDs below 10k are reserved (1000)']) rv = rule_parser.parse_rule('BasicRule "rx:^ratata$" "mz:$URL:/foobar|$BODY_VAR_X:^tutu$"' 'id:4200001 "s:$SQL:8";') self.assertEqual(rv, False) self.assertIn('$BODY_VAR_X', str(rule_parser.error)) self.assertIn('$URL', str(rule_parser.error)) self.assertIn("You can't mix static $* with regex $*_X", str(rule_parser.error)) self.assertIn("parsing of element 'mz:$URL:/foobar|$BODY_VAR_X:^tutu$' failed.", rule_parser.error) rv = rule_parser.parse_rule('"rx:^ratata$" "mz:$URL:/foobar|$BODY_VAR_X:^tutu$"' 'id:4200001 "s:$SQL:8";') self.assertEqual(rv, False) self.assertIn('No mainrule/basicrule keyword.', rule_parser.error) rv = rule_parser.parse_rule('MainRule BasicRule "rx:select"' '"msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;') self.assertEqual(rv, False) self.assertIn('Multiple mainrule/basicrule keywords.', rule_parser.error) rv = rule_parser.parse_rule('MainRule "rx:select"' '"msg:sql keywords" "mz:BODY" "s:$SQL:4" id:1000 "wrong:LOL";') self.assertEqual(rv, False) self.assertIn("'wrong:LOL' is an invalid element and thus can not be parsed.", rule_parser.error) rv = rule_parser.parse_rule('MainRule "rx:select"' '"msg:sql keywords" "mz:BODY" "s:$SQL:4" "id:non_numeric";') self.assertEqual(rv, False) self.assertEqual(['id:non_numeric is not numeric', "parsing of element 'id:non_numeric' failed."], rule_parser.error) rv = rule_parser.parse_rule('MainRule "rx:select" "mz:wrong" "msg:sql keywords" "s:$SQL:4" "id:10000";') self.assertEqual(rv, False) self.assertIn("'wrong' is not a known sub-part of mz : ", str(rule_parser.error)) self.assertIn("parsing of element 'mz:wrong' failed.", rule_parser.error)
def test_select(self): current_sid = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first() current_sid = 1337 if current_sid is None else current_sid.sid + 1 db.session.add(NaxsiRules(u'POUET', 'str:test', u'BODY', u'$SQL:8', current_sid, u'WEB_APPS', u'f hqewifueiwf hueiwhf uiewh fiewh fhw', '1', True, 1457101045)) db.session.commit() _ruleset = NaxsiRules.query.first().ruleset rv = self.app.get('/rulesets/select/%s' % _ruleset) self.assertEqual(rv.status_code, 200) self.assertIn(_ruleset, str(rv.data)) random_name = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(16)) rv = self.app.get('/rulesets/select/%s' % random_name) self.assertEqual(rv.status_code, 200) db.session.delete(NaxsiRules.query.filter(current_sid == NaxsiRules.sid).first())
def explain_rule(): errors = warnings = list() rule_get = request.args.get("rule", "") rule_post = request.form.get("rule", "") if rule_get.isdigit(): # explain a rule by id _rule = NaxsiRules.query.filter(NaxsiRules.sid == rule_get).first() if _rule is None: flash("Not rule with id %s" % rule_get) return redirect(url_for("sandbox.index")) elif rule_get is not "": flash("Please provide a numeric id") return redirect(url_for("sandbox.index")) elif not rule_post: flash("Please provide a rule") return redirect(url_for("sandbox.index")) else: _rule = NaxsiRules() errors, warnings, rdict = _rule.parse_rule(rule_post) _rule = NaxsiRules() _rule.from_dict(rdict) _rule.errors = errors _rule.warnings = warnings if _rule.errors: flash("You rule is wrong", "error") return render_template("misc/sandbox.html") if "visualise_rule" in request.form: if _rule.detection.startswith("rx:"): return redirect("https://regexper.com/#" + _rule.detection[3:]) else: flash("The rule is not a regexp, so you can not visualize it.", category="error") if errors: for error in errors: flash(error, category="error") if warnings: for warnings in warnings: flash(warnings, category="warning") return render_template("misc/sandbox.html", rule_explaination=_rule.explain(), rule=_rule)