mirror of https://github.com/CIRCL/lookyloo
chg: Improve popup, remove session.
parent
cd5eabafba
commit
3b631f2c92
|
@ -18,7 +18,7 @@ from uuid import uuid4
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
from defang import refang # type: ignore
|
from defang import refang # type: ignore
|
||||||
from har2tree import CrawledTree, Har2TreeError, HarFile
|
from har2tree import CrawledTree, Har2TreeError, HarFile, HostNode, URLNode
|
||||||
from redis import Redis
|
from redis import Redis
|
||||||
from scrapysplashwrapper import crawl
|
from scrapysplashwrapper import crawl
|
||||||
|
|
||||||
|
@ -84,6 +84,14 @@ class Lookyloo():
|
||||||
sample_config = json.load(_c)
|
sample_config = json.load(_c)
|
||||||
return sample_config[entry]
|
return sample_config[entry]
|
||||||
|
|
||||||
|
def get_urlnode_from_tree(self, capture_dir: Path, node_uuid: str) -> URLNode:
|
||||||
|
ct = self._load_pickle(capture_dir / 'tree.pickle')
|
||||||
|
return ct.root_hartree.get_url_node_by_uuid(node_uuid)
|
||||||
|
|
||||||
|
def get_hostnode_from_tree(self, capture_dir: Path, node_uuid: str) -> HostNode:
|
||||||
|
ct = self._load_pickle(capture_dir / 'tree.pickle')
|
||||||
|
return ct.root_hartree.get_host_node_by_uuid(node_uuid)
|
||||||
|
|
||||||
def get_statistics(self, capture_dir: Path) -> Dict[str, Any]:
|
def get_statistics(self, capture_dir: Path) -> Dict[str, Any]:
|
||||||
# We need the pickle
|
# We need the pickle
|
||||||
ct = self._load_pickle(capture_dir / 'tree.pickle')
|
ct = self._load_pickle(capture_dir / 'tree.pickle')
|
||||||
|
@ -280,7 +288,7 @@ class Lookyloo():
|
||||||
ct = CrawledTree(har_files, uuid)
|
ct = CrawledTree(har_files, uuid)
|
||||||
with pickle_file.open('wb') as _p:
|
with pickle_file.open('wb') as _p:
|
||||||
pickle.dump(ct, _p)
|
pickle.dump(ct, _p)
|
||||||
return str(pickle_file), ct.to_json(), ct.start_time.isoformat(), ct.user_agent, ct.root_url, meta
|
return ct.to_json(), ct.start_time.isoformat(), ct.user_agent, ct.root_url, meta
|
||||||
except Har2TreeError as e:
|
except Har2TreeError as e:
|
||||||
raise NoValidHarFile(e.message)
|
raise NoValidHarFile(e.message)
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,11 @@ from pysanejs import SaneJS
|
||||||
|
|
||||||
class SaneJavaScript():
|
class SaneJavaScript():
|
||||||
|
|
||||||
|
skip_lookup: Dict[str, str] = {
|
||||||
|
"717ea0ff7f3f624c268eccb244e24ec1305ab21557abb3d6f1a7e183ff68a2d28f13d1d2af926c9ef6d1fb16dd8cbe34cd98cacf79091dddc7874dcee21ecfdc": "1*1px gif",
|
||||||
|
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e": "Empty file"
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any]):
|
def __init__(self, config: Dict[str, Any]):
|
||||||
if not ('enabled' in config or config['enabled']):
|
if not ('enabled' in config or config['enabled']):
|
||||||
self.available = False
|
self.available = False
|
||||||
|
@ -44,12 +49,12 @@ class SaneJavaScript():
|
||||||
with sanejs_unknowns.open() as f:
|
with sanejs_unknowns.open() as f:
|
||||||
unknown_hashes = [line.strip() for line in f.readlines()]
|
unknown_hashes = [line.strip() for line in f.readlines()]
|
||||||
|
|
||||||
if force:
|
to_return = {h: details for h, details in self.skip_lookup.items() if h in sha512}
|
||||||
to_lookup = hashes
|
|
||||||
else:
|
to_lookup = [h for h in hashes if h not in self.skip_lookup]
|
||||||
to_lookup = [h for h in sha512 if (h not in unknown_hashes
|
if not force:
|
||||||
and not (today_dir / h).exists())]
|
to_lookup = [h for h in to_lookup if (h not in unknown_hashes
|
||||||
to_return = {}
|
and not (today_dir / h).exists())]
|
||||||
for h in to_lookup:
|
for h in to_lookup:
|
||||||
response = self.client.sha512(h)
|
response = self.client.sha512(h)
|
||||||
if 'error' in response:
|
if 'error' in response:
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import json
|
|
||||||
import pickle
|
|
||||||
from zipfile import ZipFile, ZIP_DEFLATED
|
from zipfile import ZipFile, ZIP_DEFLATED
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from flask import Flask, render_template, request, session, send_file, redirect, url_for, Response, flash
|
from flask import Flask, render_template, request, send_file, redirect, url_for, Response, flash
|
||||||
from flask_bootstrap import Bootstrap # type: ignore
|
from flask_bootstrap import Bootstrap # type: ignore
|
||||||
from flask_httpauth import HTTPDigestAuth # type: ignore
|
from flask_httpauth import HTTPDigestAuth # type: ignore
|
||||||
|
|
||||||
|
@ -18,7 +16,7 @@ from lookyloo.lookyloo import Lookyloo
|
||||||
from lookyloo.exceptions import NoValidHarFile
|
from lookyloo.exceptions import NoValidHarFile
|
||||||
from .proxied import ReverseProxied
|
from .proxied import ReverseProxied
|
||||||
|
|
||||||
from typing import Tuple, Optional, Dict, Any
|
from typing import Optional, Dict, Any
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -79,14 +77,6 @@ def rebuild_tree(tree_uuid: str):
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
|
||||||
# keep
|
|
||||||
def load_tree(capture_dir: Path) -> Tuple[str, str, str, str, Dict[str, Any]]:
|
|
||||||
session.clear()
|
|
||||||
temp_file_name, tree_json, tree_time, tree_ua, tree_root_url, meta = lookyloo.load_tree(capture_dir)
|
|
||||||
session["tree"] = temp_file_name
|
|
||||||
return tree_json, tree_time, tree_ua, tree_root_url, meta
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/submit', methods=['POST', 'GET'])
|
@app.route('/submit', methods=['POST', 'GET'])
|
||||||
def submit():
|
def submit():
|
||||||
to_query = request.get_json(force=True)
|
to_query = request.get_json(force=True)
|
||||||
|
@ -116,11 +106,10 @@ def scrape_web():
|
||||||
return render_template('scrape.html', user_agents=user_agents)
|
return render_template('scrape.html', user_agents=user_agents)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/tree/hostname/<string:node_uuid>/text', methods=['GET'])
|
@app.route('/tree/<string:tree_uuid>/hostname/<string:node_uuid>/text', methods=['GET'])
|
||||||
def hostnode_details_text(node_uuid: str):
|
def hostnode_details_text(tree_uuid: str, node_uuid: str):
|
||||||
with open(session["tree"], 'rb') as f:
|
capture_dir = lookyloo.lookup_capture_dir(tree_uuid)
|
||||||
ct = pickle.load(f)
|
hostnode = lookyloo.get_hostnode_from_tree(capture_dir, node_uuid)
|
||||||
hostnode = ct.root_hartree.get_host_node_by_uuid(node_uuid)
|
|
||||||
urls = []
|
urls = []
|
||||||
for url in hostnode.urls:
|
for url in hostnode.urls:
|
||||||
urls.append(url.name)
|
urls.append(url.name)
|
||||||
|
@ -134,11 +123,10 @@ def hostnode_details_text(node_uuid: str):
|
||||||
as_attachment=True, attachment_filename='file.md')
|
as_attachment=True, attachment_filename='file.md')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/tree/hostname_popup/<string:node_uuid>', methods=['GET'])
|
@app.route('/tree/<string:tree_uuid>/hostname_popup/<string:node_uuid>', methods=['GET'])
|
||||||
def hostnode_popup(node_uuid: str):
|
def hostnode_popup(tree_uuid: str, node_uuid: str):
|
||||||
with open(session["tree"], 'rb') as f:
|
capture_dir = lookyloo.lookup_capture_dir(tree_uuid)
|
||||||
ct = pickle.load(f)
|
hostnode = lookyloo.get_hostnode_from_tree(capture_dir, node_uuid)
|
||||||
hostnode = ct.root_hartree.get_host_node_by_uuid(node_uuid)
|
|
||||||
table_keys = {
|
table_keys = {
|
||||||
'js': "/static/javascript.png",
|
'js': "/static/javascript.png",
|
||||||
'exe': "/static/exe.png",
|
'exe': "/static/exe.png",
|
||||||
|
@ -163,36 +151,21 @@ def hostnode_popup(node_uuid: str):
|
||||||
for url in hostnode.urls:
|
for url in hostnode.urls:
|
||||||
if lookyloo.sanejs.available and hasattr(url, 'body_hash') and url.body_hash in lookups:
|
if lookyloo.sanejs.available and hasattr(url, 'body_hash') and url.body_hash in lookups:
|
||||||
url.add_feature('sane_js_details', lookups[url.body_hash])
|
url.add_feature('sane_js_details', lookups[url.body_hash])
|
||||||
# TODO: Do something with it.
|
if lookups[url.body_hash] and isinstance(lookups[url.body_hash], list):
|
||||||
|
url.add_feature('sane_js_details_to_print', f'{" ".join(lookups[url.body_hash][0].split("|"))} and {len(lookups[url.body_hash])-1} other files')
|
||||||
urls.append(url)
|
urls.append(url)
|
||||||
return render_template('hostname_popup.html',
|
return render_template('hostname_popup.html',
|
||||||
|
tree_uuid=tree_uuid,
|
||||||
hostname_uuid=node_uuid,
|
hostname_uuid=node_uuid,
|
||||||
hostname=hostnode.name,
|
hostname=hostnode.name,
|
||||||
urls=urls,
|
urls=urls,
|
||||||
keys=table_keys)
|
keys=table_keys)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/tree/hostname/<string:node_uuid>', methods=['GET'])
|
@app.route('/tree/<string:tree_uuid>/url/<string:node_uuid>', methods=['GET'])
|
||||||
def hostnode_details(node_uuid: str):
|
def urlnode_details(tree_uuid: str, node_uuid: str):
|
||||||
with open(session["tree"], 'rb') as f:
|
capture_dir = lookyloo.lookup_capture_dir(tree_uuid)
|
||||||
ct = pickle.load(f)
|
urlnode = lookyloo.get_urlnode_from_tree(capture_dir, node_uuid)
|
||||||
hostnode = ct.root_hartree.get_host_node_by_uuid(node_uuid)
|
|
||||||
urls = []
|
|
||||||
for url in hostnode.urls:
|
|
||||||
if hasattr(url, 'body_hash'):
|
|
||||||
sane_js_r = lookyloo.sane_js_query(url.body_hash)
|
|
||||||
if sane_js_r.get('response'):
|
|
||||||
url.add_feature('sane_js_details', sane_js_r['response'])
|
|
||||||
print('######## SANEJS ##### ', url.sane_js_details)
|
|
||||||
urls.append(url.to_json())
|
|
||||||
return json.dumps(urls)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/tree/url/<string:node_uuid>', methods=['GET'])
|
|
||||||
def urlnode_details(node_uuid: str):
|
|
||||||
with open(session["tree"], 'rb') as f:
|
|
||||||
ct = pickle.load(f)
|
|
||||||
urlnode = ct.root_hartree.get_url_node_by_uuid(node_uuid)
|
|
||||||
to_return = BytesIO()
|
to_return = BytesIO()
|
||||||
got_content = False
|
got_content = False
|
||||||
if hasattr(urlnode, 'body'):
|
if hasattr(urlnode, 'body'):
|
||||||
|
@ -338,7 +311,7 @@ def tree(tree_uuid: str):
|
||||||
enable_mail_notification = True
|
enable_mail_notification = True
|
||||||
else:
|
else:
|
||||||
enable_mail_notification = False
|
enable_mail_notification = False
|
||||||
tree_json, start_time, user_agent, root_url, meta = load_tree(capture_dir)
|
tree_json, start_time, user_agent, root_url, meta = lookyloo.load_tree(capture_dir)
|
||||||
return render_template('tree.html', tree_json=tree_json, start_time=start_time,
|
return render_template('tree.html', tree_json=tree_json, start_time=start_time,
|
||||||
user_agent=user_agent, root_url=root_url, tree_uuid=tree_uuid,
|
user_agent=user_agent, root_url=root_url, tree_uuid=tree_uuid,
|
||||||
meta=meta, enable_mail_notification=enable_mail_notification)
|
meta=meta, enable_mail_notification=enable_mail_notification)
|
||||||
|
|
|
@ -93,7 +93,7 @@ d3.selection.prototype.moveToBack = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
function hostnode_click_popup(d) {
|
function hostnode_click_popup(d) {
|
||||||
window.open('/tree/hostname_popup/' + d.data.uuid, '_blank', 'width=700,height=500,left=200,top=100');
|
window.open('/tree/' + treeUUID + '/hostname_popup/' + d.data.uuid, '_blank', 'width=1024,height=768,left=200,top=100');
|
||||||
};
|
};
|
||||||
|
|
||||||
function ProcessChildMessage(message) {
|
function ProcessChildMessage(message) {
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<center>
|
<center>
|
||||||
<h3>{{ hostname }}</h3>
|
<h3>{{ hostname }}</h3>
|
||||||
<button type="button" class="btn btn-secondary" onclick="whereAmI()">Locate node on tree</button>
|
<button type="button" class="btn btn-secondary" onclick="whereAmI()">Locate node on tree</button>
|
||||||
<a href="{{ url_for('hostnode_details_text', node_uuid=hostname_uuid) }}" class="btn btn-info" role="button">Get URLs as text</a>
|
<a href="{{ url_for('hostnode_details_text', tree_uuid=tree_uuid, node_uuid=hostname_uuid) }}" class="btn btn-info" role="button">Get URLs as text</a>
|
||||||
</center>
|
</center>
|
||||||
<p>Click on the URL to get the content of the response</p>
|
<p>Click on the URL to get the content of the response</p>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
|
@ -22,6 +22,7 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>URL</th>
|
<th>URL</th>
|
||||||
|
<th>Known file</th>
|
||||||
{% for alt, path in keys.items() %}
|
{% for alt, path in keys.items() %}
|
||||||
<th><img src="{{ path }}" alt="{{ alt }}" width="21" height="21"/></th>
|
<th><img src="{{ path }}" alt="{{ alt }}" width="21" height="21"/></th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -31,7 +32,14 @@
|
||||||
{% for url in urls %}
|
{% for url in urls %}
|
||||||
<tr>
|
<tr>
|
||||||
<td style="white-space:nowrap">
|
<td style="white-space:nowrap">
|
||||||
<a href="{{ url_for('urlnode_details', node_uuid=url.uuid) }}">{{ url.name }}</a>
|
<a href="{{ url_for('urlnode_details', tree_uuid=tree_uuid, node_uuid=url.uuid) }}">{{ url.name }}</a>
|
||||||
|
</td>
|
||||||
|
<td><br/>
|
||||||
|
{% if url.sane_js_details_to_print %}
|
||||||
|
{{ url.sane_js_details_to_print }}
|
||||||
|
{% else %}
|
||||||
|
{{ url.sane_js_details }}
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% for key in keys.keys() %}
|
{% for key in keys.keys() %}
|
||||||
<td><br/>
|
<td><br/>
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{super()}}
|
{{super()}}
|
||||||
<script>
|
<script>
|
||||||
|
var treeUUID = "{{ tree_uuid }}";
|
||||||
var treeData = {{ tree_json | safe }};
|
var treeData = {{ tree_json | safe }};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue