new: Add MISP lookup

pull/210/head
Raphaël Vinot 2021-06-01 15:31:14 -07:00
parent 53ef253c94
commit 3071a1a7c9
4 changed files with 73 additions and 1 deletions

View File

@ -414,6 +414,20 @@ class Lookyloo():
to_return['pi'][ct.root_hartree.har.root_url] = self.pi.get_url_lookup(ct.root_hartree.har.root_url) to_return['pi'][ct.root_hartree.har.root_url] = self.pi.get_url_lookup(ct.root_hartree.har.root_url)
return to_return return to_return
def get_misp_occurrences(self, capture_uuid: str, /) -> Optional[Dict[str, Any]]:
if not self.misp.available:
return None
try:
ct = self.get_crawled_tree(capture_uuid)
except LookylooException:
self.logger.warning(f'Unable to get the modules responses unless the tree ({capture_uuid}) is cached.')
return None
nodes_to_lookup = ct.root_hartree.rendered_node.get_ancestors()
events = {}
for node in nodes_to_lookup:
events[node.name] = self.misp.lookup(node, ct.root_hartree.get_host_node_by_uuid(node.hostnode_uuid))
return events
def _set_capture_cache(self, capture_dir: Path, force: bool=False, redis_pipeline: Optional[Redis]=None) -> None: def _set_capture_cache(self, capture_dir: Path, force: bool=False, redis_pipeline: Optional[Redis]=None) -> None:
'''Populate the redis cache for a capture. Mostly used on the index page.''' '''Populate the redis cache for a capture. Mostly used on the index page.'''
if force or not self.redis.exists(str(capture_dir)): if force or not self.redis.exists(str(capture_dir)):

View File

@ -19,7 +19,7 @@ from pysanejs import SaneJS
from pyeupi import PyEUPI from pyeupi import PyEUPI
from pymisp import PyMISP, MISPEvent, MISPAttribute from pymisp import PyMISP, MISPEvent, MISPAttribute
from har2tree import CrawledTree, HostNode from har2tree import CrawledTree, HostNode, URLNode
class MISP(): class MISP():
@ -121,6 +121,20 @@ class MISP():
return event return event
return None return None
def lookup(self, node: URLNode, hostnode: HostNode) -> Union[List[str], Dict]:
if self.available and self.enable_lookup:
to_lookup = [node.name, node.hostname] + hostnode.resolved_ips
if hasattr(hostnode, 'cnames'):
to_lookup += hostnode.cnames
if attributes := self.client.search(controller='attributes', value=to_lookup, pythonify=True):
if isinstance(attributes, list):
return list(set(attribute.event_id for attribute in attributes))
else:
return attributes
return []
else:
return {'error': 'Module not available or lookup not enabled.'}
class UniversalWhois(): class UniversalWhois():

View File

@ -338,6 +338,17 @@ def stats(tree_uuid: str):
return render_template('statistics.html', uuid=tree_uuid, stats=stats) return render_template('statistics.html', uuid=tree_uuid, stats=stats)
@app.route('/tree/<string:tree_uuid>/misp_lookup', methods=['GET'])
@flask_login.login_required
def web_misp_lookup_view(tree_uuid: str):
hits = lookyloo.get_misp_occurrences(tree_uuid)
if hits:
misp_root_url = lookyloo.misp.client.root_url
else:
misp_root_url = None
return render_template('misp_lookup.html', uuid=tree_uuid, hits=hits, misp_root_url=misp_root_url)
@app.route('/tree/<string:tree_uuid>/modules', methods=['GET']) @app.route('/tree/<string:tree_uuid>/modules', methods=['GET'])
def modules(tree_uuid: str): def modules(tree_uuid: str):
modules_responses = lookyloo.get_modules_responses(tree_uuid) modules_responses = lookyloo.get_modules_responses(tree_uuid)
@ -551,6 +562,7 @@ def tree(tree_uuid: str, node_uuid: Optional[str]=None):
enable_categorization=enable_categorization, enable_categorization=enable_categorization,
enable_bookmark=enable_bookmark, enable_bookmark=enable_bookmark,
misp_push=lookyloo.misp.available and lookyloo.misp.enable_push, misp_push=lookyloo.misp.available and lookyloo.misp.enable_push,
misp_lookup=lookyloo.misp.available and lookyloo.misp.enable_lookup,
blur_screenshot=blur_screenshot, urlnode_uuid=hostnode_to_highlight, blur_screenshot=blur_screenshot, urlnode_uuid=hostnode_to_highlight,
auto_trigger_modules=auto_trigger_modules, auto_trigger_modules=auto_trigger_modules,
confirm_message=confirm_message if confirm_message else 'Tick to confirm.', confirm_message=confirm_message if confirm_message else 'Tick to confirm.',

View File

@ -73,6 +73,13 @@
}); });
</script> </script>
<script> <script>
$('#mispLookupModal').on('show.bs.modal', function(e) {
var button = $(e.relatedTarget);
var modal = $(this);
modal.find('.modal-body').load(button.data("remote"));
});
</script>
<script>
$('#urlsInPageModal').on('show.bs.modal', function(e) { $('#urlsInPageModal').on('show.bs.modal', function(e) {
var button = $(e.relatedTarget); var button = $(e.relatedTarget);
var modal = $(this); var modal = $(this);
@ -229,6 +236,12 @@
data-toggle="modal" data-target="#mispPushModal" role="button">Prepare push to MISP</a> data-toggle="modal" data-target="#mispPushModal" role="button">Prepare push to MISP</a>
</li> </li>
{% endif %} {% endif %}
{% if current_user.is_authenticated and misp_lookup%}
<li>
<a href="#mispLookupModal" data-remote="{{ url_for('web_misp_lookup_view', tree_uuid=tree_uuid) }}"
data-toggle="modal" data-target="#mispLookupModal" role="button">Search events on MISP</a>
</li>
{% endif %}
{% if enable_bookmark %} {% if enable_bookmark %}
<li> <li>
<a href="#/" role="button" onclick="UnbookmarkAllNodes();">Unbookmark all nodes</a> <a href="#/" role="button" onclick="UnbookmarkAllNodes();">Unbookmark all nodes</a>
@ -392,6 +405,25 @@
</div> </div>
</div> </div>
<div class="modal fade" id="mispLookupModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="mispLookupModalLabel">MISP Lookup</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
... loading MISP Lookup view ...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="screenshotModal" tabindex="-1" role="dialog"> <div class="modal fade" id="screenshotModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-xl" role="document"> <div class="modal-dialog modal-xl" role="document">
<div class="modal-content"> <div class="modal-content">