diff --git a/bin/attacker.py b/bin/attacker.py index 0074ecf..e8f5550 100644 --- a/bin/attacker.py +++ b/bin/attacker.py @@ -8,7 +8,8 @@ def run_rules(conf): data = rds.get_scan_data() - + exclusions = rds.get_exclusions() + if not data: return @@ -18,9 +19,18 @@ def run_rules(conf): for port in values['ports']: logger.info('Attacking Asset: {} on port: {}'.format(ip, port)) for rule in rules.values(): + """ + Check if the target is in exclusions list, if it is, skip. + """ + if rule.rule in exclusions and ip in exclusions[rule.rule]: + logger.debug('Skipping rule {} for target {}'.format(rule.rule, ip)) + continue + + """ + Only run rules that are in the allowed_aggressive config level. + """ if conf['config']['allow_aggressive'] >= rule.intensity: thread = threading.Thread(target=rule.check_rule, args=(ip, port, values, conf)) - thread.name = 'rule_{}_{}_{}'.format(rule.rule, ip, port) thread.start() def attacker(): diff --git a/core/redis.py b/core/redis.py index 2d72cb0..0e2dbc7 100644 --- a/core/redis.py +++ b/core/redis.py @@ -161,6 +161,12 @@ def get_scan_progress(self): count += 1 return count + def get_exclusions(self): + exc = self.r.get('p_rule-exclusions') + if exc: + return pickle.loads(exc) + return {} + def get_last_scan(self): return self.r.get('p_last-scan') @@ -234,7 +240,7 @@ def db_size(self): return self.r.dbsize() def initialize(self): - self.flushdb() + self.clear_session() self.r.set('p_scan-count', 0) self.r.set('p_last-scan', 'N/A') diff --git a/main.py b/main.py index 573e290..a7d1ea2 100644 --- a/main.py +++ b/main.py @@ -34,6 +34,7 @@ from views_api.api_health import Health from views_api.api_scan import Scan from views_api.api_update import Update +from views_api.api_exclusions import Exclusion app = Flask(__name__) @@ -68,6 +69,7 @@ api.add_resource(Health, '/health') api.add_resource(Update, '/api/update', '/api/update/') api.add_resource(Scan, '/api/scan', '/api/scan/') +api.add_resource(Exclusion, '/api/exclusion', '/api/exclusion') # Set Security Headers diff --git a/templates/documentation.html b/templates/documentation.html index b6b89ba..bd65371 100644 --- a/templates/documentation.html +++ b/templates/documentation.html @@ -325,6 +325,8 @@

API

  • POST /api/scan
  • GET /api/scan/status
  • PUT /api/scan/reset
  • +
  • GET /api/exclusion
  • +
  • POST /api/exclusion
  • GET /api/update/platform
  • @@ -342,8 +344,9 @@

    API

    +

    Authentication

    -
    Authentication
    +
    Authentication

    API Authentication is done using Basic Authentication.

    Basic authentication is a simple authentication scheme built into the HTTP protocol. The client sends HTTP requests with the Authorization header that contains the word Basic word followed by a space and a base64-encoded string username:password

    @@ -361,10 +364,10 @@

    API

    +

    API Table

    API Endpoints
    - @@ -404,8 +407,22 @@

    API

    + + + + + + + + + + + + + + - + @@ -423,8 +440,10 @@

    API

    +

    GET /health

    +

    Health is an endpoint to do basic sanity-checks for monitoring systems, etc.

    -
    GET /health
    +
    GET /health
     import requests
    @@ -439,8 +458,11 @@ 

    API

    +

    POST /api/scan

    +

    This endpoint is where you can submit new scans with a configuration file similar to the Web Interface.
    + The configuration file must be provided as-is without removing any entries.

    -
    POST /api/scan
    +
    POST /api/scan
     import requests
    @@ -491,8 +513,11 @@ 

    API

    +

    GET /api/scan/status

    +

    This endpoint is where you can view the state of a running scan.
    + Note that you do not need to wait for a scan to complete, results will be shown as soon as new data is created.

    -
    GET /api/scan/status
    +
    GET /api/scan/status
     import requests
    @@ -518,8 +543,10 @@ 

    API

    +

    PUT /api/scan/reset

    +

    This endpoint allows you to reset the system / stop a currently running assessment (such as continuous)

    -
    PUT /api/scan/reset
    +
    PUT /api/scan/reset
     import requests
    @@ -536,8 +563,60 @@ 

    API

    +

    GET /api/exclusion

    +

    This endpoint is where you get the current exclusion list.
    + You may want to the Exclusion functionality to prevent certain traffic from running against certain assets, or to prevent certain alerts from getting created.

    +
    +
    GET /api/exclusion
    +
    +
    +import requests
    +from requests.auth import HTTPBasicAuth
    +
    +>>> requests.get('http://endpoint/api/exclusion', auth=HTTPBasicAuth("admin", "admin"))
    +
    +<<< {'exclusions': {'SVC_ZGZA': ['192.168.0.1', '192.168.0.254']}}
    +                          
    +
    +
    +
    +
    + +
    +
    +

    POST /api/exclusion

    +

    This endpoint is where you can submit an exclusion list. The JSON in your POST must have a valid format as shown below.
    + Rule IDs can be retrieved from /opt/nerve/rules/**/*.py files

    +
    +
    POST /api/exclusion
    +
    +
    +import requests
    +from requests.auth import HTTPBasicAuth
    +
    +# FORMAT:
    +# 'MY_RULE_ID':['MY_IP-1', 'MY_IP-2']
    +
    +EXCLUSIONS = {
    +  'SVC_ZGZA':['192.168.0.1', '192.168.0.254'],
    +  'CVE_72D3':['192.168.0.1'],
    +}
    +
    +>>> requests.post('http://endpoint/api/exclusion', auth=HTTPBasicAuth("admin", "admin"), json=EXCLUSIONS)
    +
    +<<< {'status':'ok'}
    +                          
    +
    +
    +
    +
    + +
    +
    +

    GET /api/update/platform

    +

    This endpoint allows you to check if you have the latest NERVE version.

    -
    GET /api/update/platform
    +
    GET /api/update/platform
     import requests
    diff --git a/version.py b/version.py
    index 88c5f39..7a6e749 100644
    --- a/version.py
    +++ b/version.py
    @@ -1 +1 @@
    -VERSION = '2.5.4'
    +VERSION = '2.5.5'
    diff --git a/views_api/api_exclusions.py b/views_api/api_exclusions.py
    new file mode 100644
    index 0000000..057b249
    --- /dev/null
    +++ b/views_api/api_exclusions.py
    @@ -0,0 +1,18 @@
    +from core.security  import auth
    +from core.redis import rds
    +from flask_restful  import Resource
    +from flask import request
    +
    +class Exclusion(Resource):
    +  @auth.login_required
    +  def get(self):  
    +    exclusions = rds.get_exclusions()
    +    return {'exclusions':exclusions}, 200
    +  
    +  @auth.login_required
    +  def post(self):
    +    user_exclusions = request.get_json()
    +    if isinstance(user_exclusions, dict):
    +      rds.store_json('p_rule-exclusions', user_exclusions)
    +      return {'status':'ok'}
    +    return {'status':'Malformed data, must be JSON'}
    \ No newline at end of file
    
    True Resets the Server. Roll Back.
    5.GET/api/exclusionTrueReturns the current exclusion list
    6.POST/api/exclusionTrueSubmits an exclusion list
    5.7. GET /api/update/platform True