new: Basic support for monitoring

monitoring
Raphaël Vinot 2023-02-23 18:37:40 +01:00
parent 370850285d
commit 5e72e1104e
7 changed files with 118 additions and 3 deletions

View File

@ -26,6 +26,10 @@
"enable": false, "enable": false,
"url": "" "url": ""
}, },
"monitoring": {
"enable": false,
"url": "https://127.0.0.1:5200"
},
"tor_proxy": { "tor_proxy": {
"server": "socks5://127.0.0.1:9050" "server": "socks5://127.0.0.1:9050"
}, },
@ -74,6 +78,7 @@
"auto_trigger_modules": "Automatically trigger the modules when the tree is loaded and when the capture is cached", "auto_trigger_modules": "Automatically trigger the modules when the tree is loaded and when the capture is cached",
"enable_mail_notification": "Allow users to notify a pre-configured email address about a specific capture", "enable_mail_notification": "Allow users to notify a pre-configured email address about a specific capture",
"remote_lacus": "By default, lookyloo will do the capture locally. Enabling this feature means you have a dedicated Lacus instance somewhere", "remote_lacus": "By default, lookyloo will do the capture locally. Enabling this feature means you have a dedicated Lacus instance somewhere",
"monitoring": "Enable connection to a remote monitoring instance",
"tor_proxy": "URL to connect to a SOCKS 5 proxy for tor", "tor_proxy": "URL to connect to a SOCKS 5 proxy for tor",
"email": "Configuration for sending email notifications.", "email": "Configuration for sending email notifications.",
"priority": "Define the priority of a new capture. A capture from the web interface has priority over a capture from the API, same for authenticated user vs. anonymous.", "priority": "Define the priority of a new capture. A capture from the web interface has priority over a capture from the API, same for authenticated user vs. anonymous.",

View File

@ -34,6 +34,7 @@ from pylacus import (PyLacus,
# CaptureSettings as CaptureSettingsPy) # CaptureSettings as CaptureSettingsPy)
from pymisp import MISPAttribute, MISPEvent, MISPObject from pymisp import MISPAttribute, MISPEvent, MISPObject
from pysecuritytxt import PySecurityTXT, SecurityTXTNotAvailable from pysecuritytxt import PySecurityTXT, SecurityTXTNotAvailable
from pylookyloomonitoring import PyLookylooMonitoring
from redis import ConnectionPool, Redis from redis import ConnectionPool, Redis
from redis.connection import UnixDomainSocketConnection from redis.connection import UnixDomainSocketConnection
@ -44,7 +45,8 @@ from .exceptions import (MissingCaptureDirectory,
MissingUUID, TreeNeedsRebuild, NoValidHarFile) MissingUUID, TreeNeedsRebuild, NoValidHarFile)
from .helpers import (get_captures_dir, get_email_template, from .helpers import (get_captures_dir, get_email_template,
get_resources_hashes, get_taxonomies, get_resources_hashes, get_taxonomies,
uniq_domains, ParsedUserAgent, load_cookies, UserAgents) uniq_domains, ParsedUserAgent, load_cookies, UserAgents,
get_useragent_for_requests)
from .indexing import Indexing from .indexing import Indexing
from .modules import (MISP, PhishingInitiative, UniversalWhois, from .modules import (MISP, PhishingInitiative, UniversalWhois,
UrlScan, VirusTotal, Phishtank, Hashlookup, UrlScan, VirusTotal, Phishtank, Hashlookup,
@ -112,6 +114,14 @@ class Lookyloo():
if not self.urlhaus.available: if not self.urlhaus.available:
self.logger.warning('Unable to setup the URLhaus module') self.logger.warning('Unable to setup the URLhaus module')
self.monitoring_enabled = False
if monitoring_config := get_config('generic', 'monitoring'):
print(monitoring_config)
if monitoring_config['enable']:
self.monitoring = PyLookylooMonitoring(monitoring_config['url'], get_useragent_for_requests())
if self.monitoring.is_up:
self.monitoring_enabled = True
self.logger.info('Initializing context...') self.logger.info('Initializing context...')
self.context = Context() self.context = Context()
self.logger.info('Context initialized.') self.logger.info('Context initialized.')

24
poetry.lock generated
View File

@ -2185,6 +2185,28 @@ requests = ">=2.28.2,<3.0.0"
[package.extras] [package.extras]
docs = ["Sphinx (>=6.1.3,<7.0.0)"] docs = ["Sphinx (>=6.1.3,<7.0.0)"]
[[package]]
name = "pylookyloomonitoring"
version = "0.1.0"
description = "Python API to connect to lookyloo monitoring"
category = "main"
optional = false
python-versions = "^3.8"
files = []
develop = false
[package.dependencies]
requests = "^2.28.2"
[package.extras]
docs = ["Sphinx (>=6.1.3,<7.0.0)"]
[package.source]
type = "git"
url = "https://github.com/Lookyloo/PyLookylooMonitoring.git"
reference = "HEAD"
resolved_reference = "99edb224dce7126c0ed8aabb4628e04d93e1580a"
[[package]] [[package]]
name = "pymisp" name = "pymisp"
version = "2.4.168" version = "2.4.168"
@ -3159,4 +3181,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.8,<3.12" python-versions = ">=3.8,<3.12"
content-hash = "cb5328d141e3bafc70de74d7a715b842271da5c19c0fb4e9996907f3e5751959" content-hash = "37b91a1a0b9b1409a2206b80d029d5c5684850c56f839bb24b09d14b0f639c6f"

View File

@ -72,6 +72,7 @@ publicsuffixlist = "^0.9.3"
pyfaup = "^1.2" pyfaup = "^1.2"
chardet = "^5.1.0" chardet = "^5.1.0"
pysecuritytxt = "^1.0.1" pysecuritytxt = "^1.0.1"
pylookyloomonitoring = {git = "https://github.com/Lookyloo/PyLookylooMonitoring.git"}
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
mypy = "^1.0.1" mypy = "^1.0.1"

View File

@ -621,6 +621,23 @@ def cache_tree(tree_uuid: str):
return redirect(url_for('index')) return redirect(url_for('index'))
@app.route('/tree/<string:tree_uuid>/monitor', methods=['POST', 'GET'])
def monitor(tree_uuid: str):
if not lookyloo.monitoring_enabled:
return redirect(url_for('tree', tree_uuid=tree_uuid))
if request.form.get('name') or not request.form.get('confirm'):
# got a bot.
logging.info(f'{src_request_ip(request)} is a bot - {request.headers.get("User-Agent")}.')
return redirect('https://www.youtube.com/watch?v=iwGFalTRHDA')
collection: str = request.form['collection'] if request.form.get('collection') else ''
frequency: str = request.form['frequency'] if request.form.get('frequency') else 'daily'
cache = lookyloo.capture_cache(tree_uuid)
monitoring_uuid = lookyloo.monitoring.monitor({'url': cache.url}, frequency=frequency, collection=collection)
flash(f"Sent to monitoring: {monitoring_uuid}", 'success')
return redirect(url_for('tree', tree_uuid=tree_uuid))
@app.route('/tree/<string:tree_uuid>/send_mail', methods=['POST', 'GET']) @app.route('/tree/<string:tree_uuid>/send_mail', methods=['POST', 'GET'])
def send_mail(tree_uuid: str): def send_mail(tree_uuid: str):
if not enable_mail_notification: if not enable_mail_notification:
@ -688,6 +705,7 @@ def tree(tree_uuid: str, node_uuid: Optional[str]=None):
screenshot_thumbnail=b64_thumbnail, page_title=cache.title, screenshot_thumbnail=b64_thumbnail, page_title=cache.title,
screenshot_size=screenshot_size, screenshot_size=screenshot_size,
meta=meta, enable_mail_notification=enable_mail_notification, meta=meta, enable_mail_notification=enable_mail_notification,
enable_monitoring=lookyloo.monitoring_enabled,
enable_context_by_users=enable_context_by_users, enable_context_by_users=enable_context_by_users,
enable_categorization=enable_categorization, enable_categorization=enable_categorization,
enable_bookmark=enable_bookmark, enable_bookmark=enable_bookmark,

View File

@ -14,6 +14,7 @@ from lacuscore import CaptureStatus as CaptureStatusCore
from pylacus import CaptureStatus as CaptureStatusPy from pylacus import CaptureStatus as CaptureStatusPy
from lookyloo.lookyloo import Lookyloo from lookyloo.lookyloo import Lookyloo
from lookyloo.comparator import Comparator from lookyloo.comparator import Comparator
from lookyloo.exceptions import MissingUUID
from .helpers import build_users_table, load_user_from_request, src_request_ip from .helpers import build_users_table, load_user_from_request, src_request_ip
@ -455,7 +456,15 @@ class CompareCaptures(Resource):
@api.doc(body=compare_captures_fields) @api.doc(body=compare_captures_fields)
def post(self): def post(self):
parameters: Dict = request.get_json(force=True) parameters: Dict = request.get_json(force=True)
result = comparator.compare_captures(parameters.get('capture_left'), parameters.get('capture_right')) left_uuid = parameters.get('capture_left')
right_uuid = parameters.get('capture_right')
try:
result = comparator.compare_captures(left_uuid, right_uuid)
except MissingUUID as e:
# UUID non-existent, or capture still ongoing.
status_left = lookyloo.get_capture_status(left_uuid)
status_right = lookyloo.get_capture_status(right_uuid)
return {'error': e, 'details': {left_uuid: status_left, right_uuid: status_right}}
return result return result

View File

@ -191,6 +191,12 @@
<li class="list-group-item"> <li class="list-group-item">
<a href="{{ url_for('index') }}" role="button">Homepage</a> <a href="{{ url_for('index') }}" role="button">Homepage</a>
</li> </li>
{% if enable_monitoring %}
<hr/>
<li class="list-group-item">
<a href="#monitoringModal" data-bs-toggle="modal" data-bs-target="#monitoringModal" role="button" class="btn btn-outline-info">Monitor</br>capture</a>
</li>
{% endif %}
{% if enable_mail_notification %} {% if enable_mail_notification %}
<hr/> <hr/>
<li class="list-group-item"> <li class="list-group-item">
@ -681,6 +687,50 @@
</div> </div>
{% endif %} {% endif %}
{% if enable_monitoring %}
<div class="modal fade" id="monitoringModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-xl" role="document">
<form role="form" action="{{ tree_uuid }}/monitor" method=post enctype=multipart/form-data>
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="monitorModalLabel">Monitor capture</h5>
<button type="button" class="btn btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>The capture will be submitted to the monitoring interface.</p>
<div class="mb-3">
<label for=frequency" class="form-label">Frequency</label>
<select name="frequency" id="frequency" class="form-select" aria-label="Select a frequency for the monitoring">
<option value="hourly" selected>Hourly</option>
<option value="daily">Daily</option>
</select>
<!-- boat fields -->
<label class="boatymcboat form-label" for="name">Your Name</label>
<input class="boatymcboat" autocomplete="off" type="text" id="name" name="name"
placeholder="Your fav boat name here">
</div>
<div class="mb-3">
<label for="collection" class="form-label">Collection</label>
<input type="text" class="form-control" name="collection" id="collection" placeholder="Name of the collection">
</div>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" name="confirm" onchange="document.getElementById('btn-notification').disabled = !this.checked;"></input>
<label for="force_push" class="form-check-label">{{ confirm_message }}</label>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success" id="btn-notification" disabled=true>Send to monitoring</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</form>
</div>
</div>
{% endif %}
<div class="modal fade" id="urlsInPageModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-xl" role="document">
{% if enable_mail_notification %} {% if enable_mail_notification %}
<div class="modal fade" id="emailModal" tabindex="-1" role="dialog"> <div class="modal fade" id="emailModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-xl" role="document"> <div class="modal-dialog modal-xl" role="document">