Skip to content

Commit

Permalink
Add Exclusion Capability
Browse files Browse the repository at this point in the history
  • Loading branch information
dolevf committed Oct 13, 2020
1 parent 26bc7f7 commit 3460c60
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 12 deletions.
14 changes: 12 additions & 2 deletions bin/attacker.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

def run_rules(conf):
data = rds.get_scan_data()

exclusions = rds.get_exclusions()

if not data:
return

Expand All @@ -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():
Expand Down
8 changes: 7 additions & 1 deletion core/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down Expand Up @@ -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')

Expand Down
2 changes: 2 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down Expand Up @@ -68,6 +69,7 @@
api.add_resource(Health, '/health')
api.add_resource(Update, '/api/update', '/api/update/<string:component>')
api.add_resource(Scan, '/api/scan', '/api/scan/<string:action>')
api.add_resource(Exclusion, '/api/exclusion', '/api/exclusion')


# Set Security Headers
Expand Down
95 changes: 87 additions & 8 deletions templates/documentation.html
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ <h3>API</h3>
<li><a href="#post_scan">POST /api/scan</a></li>
<li><a href="#get_status">GET /api/scan/status</a></li>
<li><a href="#put_reset">PUT /api/scan/reset</a></li>
<li><a href="#get_exclusion">GET /api/exclusion</a></li>
<li><a href="#post_exclusion">POST /api/exclusion</a></li>
<li><a href="#get_update">GET /api/update/platform</a></li>
</ul>
</li>
Expand All @@ -342,8 +344,9 @@ <h3>API</h3>

<div class="row">
<div class="col-md-12">
<h3 id="authentication"><b>Authentication</b></h3>
<div class="card">
<div id="authentication" class="card-header"><b>Authentication</b></div>
<div class="card-header"><b>Authentication</b></div>
<div class="card-body">
<p>API Authentication is done using Basic Authentication.</p>
<p>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</p>
Expand All @@ -361,10 +364,10 @@ <h3>API</h3>
</div>
<div class="row">
<div class="col-md-12">
<h3 id="endpoints" ><b>API Table</b></h3>
<div class="card">
<div class="card-header"><b>API Endpoints</b></div>
<div class="card-body">

<table id="api" class="table">
<thead>
<tr>
Expand Down Expand Up @@ -404,8 +407,22 @@ <h3>API</h3>
<td>True</td>
<td>Resets the Server. Roll Back.</td>
</tr>
<tr>
<th scope="row">5.</th>
<td><code>GET</code></td>
<td><a href="#get_exclusion">/api/exclusion</a></td>
<td>True</td>
<td>Returns the current exclusion list</td>
</tr>
<tr>
<th scope="row">6.</th>
<td><code>POST</code></td>
<td><a href="#post_exclusion">/api/exclusion</a></td>
<td>True</td>
<td>Submits an exclusion list</td>
</tr>
<tr>
<th scope="row">5.</th>
<th scope="row">7.</th>
<td><code>GET</code></td>
<td><a href="#get_update">/api/update/platform</a></td>
<td>True</td>
Expand All @@ -423,8 +440,10 @@ <h3>API</h3>

<div class="row">
<div class="col-md-12">
<h3 id="get_health">GET /health</h3>
<p>Health is an endpoint to do basic sanity-checks for monitoring systems, etc.</p>
<div class="card">
<div id="get_health" class="card-header"><b><code>GET /health</code></b></div>
<div class="card-header"><b><code>GET /health</code></b></div>
<div class="card-body">
<pre>
import requests
Expand All @@ -439,8 +458,11 @@ <h3>API</h3>

<div class="row">
<div class="col-md-12">
<h3 id="post_scan">POST /api/scan</h3>
<p>This endpoint is where you can submit new scans with a configuration file similar to the Web Interface.<br>
The configuration file must be provided as-is without removing any entries.</p>
<div class="card">
<div id="post_scan" class="card-header"><b><code>POST /api/scan</code></b></div>
<div class="card-header"><b><code>POST /api/scan</code></b></div>
<div class="card-body">
<pre>
import requests
Expand Down Expand Up @@ -491,8 +513,11 @@ <h3>API</h3>

<div class="row">
<div class="col-md-12">
<h3 id="get_status"> <b>GET /api/scan/status</b></h3>
<p>This endpoint is where you can view the state of a running scan.<br>
Note that you do not need to wait for a scan to complete, results will be shown as soon as new data is created.</p>
<div class="card">
<div id="get_status" class="card-header"><b><code>GET /api/scan/status</code></b></div>
<div class="card-header"><b><code>GET /api/scan/status</code></b></div>
<div class="card-body">
<pre>
import requests
Expand All @@ -518,8 +543,10 @@ <h3>API</h3>

<div class="row">
<div class="col-md-12">
<h3 id="put_reset"><b>PUT /api/scan/reset</b></h3>
<p>This endpoint allows you to reset the system / stop a currently running assessment (such as continuous)</p>
<div class="card">
<div id="put_reset" class="card-header"><b><code>PUT /api/scan/reset</code></b></div>
<div class="card-header"><b><code>PUT /api/scan/reset</code></b></div>
<div class="card-body">
<pre>
import requests
Expand All @@ -536,8 +563,60 @@ <h3>API</h3>

<div class="row">
<div class="col-md-12">
<h3 id="get_exclusion"><b>GET /api/exclusion</b></h3>
<p>This endpoint is where you get the current exclusion list.<br>
You may want to the Exclusion functionality to prevent certain traffic from running against certain assets, or to prevent certain alerts from getting created.</p>
<div class="card">
<div class="card-header"><b><code>GET /api/exclusion</code></b></div>
<div class="card-body">
<pre>
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']}}
</pre>
</div>
</div>
</div>
</div>

<div class="row">
<div class="col-md-12">
<h3 id="post_exclusion"><b>POST /api/exclusion</b></h3>
<p>This endpoint is where you can submit an exclusion list. The JSON in your POST must have a valid format as shown below.<br>
Rule IDs can be retrieved from <b>/opt/nerve/rules/**/*.py files</b></p>
<div class="card">
<div class="card-header"><b><code>POST /api/exclusion</code></b></div>
<div class="card-body">
<pre>
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'}
</pre>
</div>
</div>
</div>
</div>

<div class="row">
<div class="col-md-12">
<h3 id="get_update"><b>GET /api/update/platform</b></h3>
<p>This endpoint allows you to check if you have the latest NERVE version.</p>
<div class="card">
<div id="get_update" class="card-header"><b><code>GET /api/update/platform</code></b></div>
<div class="card-header"><b><code>GET /api/update/platform</code></b></div>
<div class="card-body">
<pre>
import requests
Expand Down
2 changes: 1 addition & 1 deletion version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = '2.5.4'
VERSION = '2.5.5'
18 changes: 18 additions & 0 deletions views_api/api_exclusions.py
Original file line number Diff line number Diff line change
@@ -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'}

0 comments on commit 3460c60

Please sign in to comment.