diff --git a/lookyloo/lookyloo.py b/lookyloo/lookyloo.py index a13044b6..d3f7d810 100644 --- a/lookyloo/lookyloo.py +++ b/lookyloo/lookyloo.py @@ -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) 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: '''Populate the redis cache for a capture. Mostly used on the index page.''' if force or not self.redis.exists(str(capture_dir)): diff --git a/lookyloo/modules.py b/lookyloo/modules.py index ab4c3af8..d8e53ccb 100644 --- a/lookyloo/modules.py +++ b/lookyloo/modules.py @@ -19,7 +19,7 @@ from pysanejs import SaneJS from pyeupi import PyEUPI from pymisp import PyMISP, MISPEvent, MISPAttribute -from har2tree import CrawledTree, HostNode +from har2tree import CrawledTree, HostNode, URLNode class MISP(): @@ -121,6 +121,20 @@ class MISP(): return event 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(): diff --git a/website/web/__init__.py b/website/web/__init__.py index f5a9d844..9f8fc227 100644 --- a/website/web/__init__.py +++ b/website/web/__init__.py @@ -338,6 +338,17 @@ def stats(tree_uuid: str): return render_template('statistics.html', uuid=tree_uuid, stats=stats) +@app.route('/tree//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//modules', methods=['GET']) def modules(tree_uuid: str): 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_bookmark=enable_bookmark, 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, auto_trigger_modules=auto_trigger_modules, confirm_message=confirm_message if confirm_message else 'Tick to confirm.', diff --git a/website/web/templates/tree.html b/website/web/templates/tree.html index 0b0ea05e..856b8ad0 100644 --- a/website/web/templates/tree.html +++ b/website/web/templates/tree.html @@ -73,6 +73,13 @@ }); +