first commit from a working website
This commit is contained in:
parent
2749d06a2d
commit
709d6e2f61
115
app.py
Normal file
115
app.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
# from dotenv import load_dotenv
|
||||
import secrets
|
||||
import socket
|
||||
import uuid
|
||||
from logging.config import dictConfig
|
||||
from pprint import pprint
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from flask import Flask, flash, redirect, render_template, request, session, url_for
|
||||
from markupsafe import escape
|
||||
|
||||
# from io import StringIO
|
||||
from validators import domain, email, ipv4, ipv6, url
|
||||
|
||||
from constants import *
|
||||
from host_lookup import abuseipdb, metadata, spf_dmarc, virustotal_api_test
|
||||
from upload import csv_parse
|
||||
|
||||
# import csv
|
||||
|
||||
dictConfig(LOGCONF)
|
||||
|
||||
# put this in a .flaskenv file: https://dev.to/kubona_my/dealing-with-environment-variables-in-flask-o1
|
||||
app = Flask(__name__)
|
||||
generate_secret = secrets.token_urlsafe(16)
|
||||
app.secret_key = generate_secret
|
||||
# app.debug = True
|
||||
|
||||
|
||||
class Info(object):
|
||||
def __init__(self, host):
|
||||
self.host = host
|
||||
self.ip_address = None
|
||||
self.host_type = metadata.check(self.host)
|
||||
self.metadata = metadata.lookup(self.host)
|
||||
self.emailsec = ()
|
||||
self.vt = {}
|
||||
self.abuseipdb = {}
|
||||
|
||||
def lookup(host):
|
||||
result = Info(host)
|
||||
if result.host_type == DOMAIN:
|
||||
result.ip_address = socket.gethostbyname(host)
|
||||
result.emailsec = spf_dmarc.lookup(host)
|
||||
result.vt = virustotal_api_test.analyse(result.host, result.host_type)
|
||||
result.abuseipdb = abuseipdb.analyse(result.ip_address)
|
||||
print("[DEBUGGING]")
|
||||
pprint(result.emailsec)
|
||||
elif result.host_type == URL:
|
||||
result.domain = urlparse(host).netloc
|
||||
result.ip_address = socket.gethostbyname(result.domain)
|
||||
result.vt = virustotal_api_test.analyse(result.host, result.host_type)
|
||||
result.abuseipdb = abuseipdb.analyse(result.ip_address)
|
||||
elif result.host_type == IPV4 or IPV6:
|
||||
result.vt = virustotal_api_test.analyse(result.host, result.host_type)
|
||||
result.abuseipdb = abuseipdb.analyse(host)
|
||||
return result
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
# logging example taken from https://betterstack.com/community/guides/logging/how-to-start-logging-with-flask/
|
||||
session["ctx"] = {"request_id": str(uuid.uuid4())}
|
||||
app.logger.info("A user visited the home page >>> %s", session["ctx"])
|
||||
|
||||
return redirect(url_for("lookup"))
|
||||
|
||||
|
||||
# refactor to handle form requests better: https://www.digitalocean.com/community/tutorials/how-to-use-web-forms-in-a-flask-application
|
||||
|
||||
|
||||
@app.route("/lookup", methods=["GET", "POST"])
|
||||
def lookup():
|
||||
host = ""
|
||||
host = escape(request.form.get("host"))
|
||||
session["ctx"] = {"request_id": str(uuid.uuid4())}
|
||||
# figure out how to start a session, maybe with a variable?
|
||||
# variable = session.get('something')
|
||||
if request.method == "GET":
|
||||
return render_template("lookup_options.html")
|
||||
elif request.method == "POST" and "host" in request.form:
|
||||
host = ""
|
||||
host = escape(request.form.get("host"))
|
||||
session["ctx"] = {"request_id": str(uuid.uuid4())}
|
||||
app.logger.info(
|
||||
"A user submitted a host to look up. | host: %s >>> %s",
|
||||
host,
|
||||
session["ctx"],
|
||||
)
|
||||
if not host:
|
||||
flash("Try again", "error")
|
||||
return render_template("lookup_options.html")
|
||||
elif host:
|
||||
result = Info.lookup(host)
|
||||
return render_template(
|
||||
"lookup_options.html",
|
||||
host=result.host,
|
||||
host_type=result.host_type,
|
||||
result=result,
|
||||
)
|
||||
elif request.method == "POST" and "file" in request.files:
|
||||
file = request.files["file"]
|
||||
extracted = csv_parse.extract(file)
|
||||
results = []
|
||||
for host in extracted:
|
||||
results.append(Info.lookup(host))
|
||||
print(results)
|
||||
return render_template("lookup_options.html")
|
||||
else:
|
||||
flash("No file!", "error")
|
||||
return render_template("lookup_options.html")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True)
|
26
constants.py
Normal file
26
constants.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
LOGCONF = {
|
||||
"version": 1,
|
||||
"formatters": {
|
||||
"default": {
|
||||
"format": "[%(asctime)s] %(levelname)s in %(module)s: %(message)s",
|
||||
}
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stdout",
|
||||
"formatter": "default",
|
||||
},
|
||||
"file": {
|
||||
"class": "logging.FileHandler",
|
||||
"filename": "flask.log",
|
||||
"formatter": "default",
|
||||
},
|
||||
},
|
||||
"root": {"level": "DEBUG", "handlers": ["console", "file"]},
|
||||
}
|
||||
|
||||
URL = "URL"
|
||||
DOMAIN = "domain"
|
||||
IPV4 = "IPv4"
|
||||
IPV6 = "IPv6"
|
41
host_lookup/abuseipdb.py
Normal file
41
host_lookup/abuseipdb.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
from base64 import decode
|
||||
import json
|
||||
import os
|
||||
import requests
|
||||
import requests_cache
|
||||
from dotenv import load_dotenv
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
class API_error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def environment():
|
||||
requests_cache.install_cache(expire_after=360, allowable_methods=("POST"))
|
||||
load_dotenv()
|
||||
api_key = os.getenv("ABUSEIPDB_API")
|
||||
return api_key
|
||||
|
||||
|
||||
def lookup(api_key, host):
|
||||
url = "https://api.abuseipdb.com/api/v2/check"
|
||||
payload = {"ipAddress": "", "maxAgeInDays": "90"}
|
||||
payload.update({"ipAddress": host})
|
||||
headers = {"Accept": "application/json", "Key": api_key}
|
||||
response = requests.request(
|
||||
method="GET", url=url, params=payload, headers=headers, verify=False
|
||||
) # TODO: remove SSL verify=False and add signed certificate if possible.
|
||||
# Figure out how caching functions here: https://requests-cache.readthedocs.io/en/stable/examples.html
|
||||
print(requests_cache.get_cache())
|
||||
print("Cached:")
|
||||
print("\n".join(requests_cache.get_cache().urls()))
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def analyse(host):
|
||||
api_key = environment()
|
||||
result = lookup(api_key, host)
|
||||
decoded_result = json.loads(result.text)
|
||||
return decoded_result
|
30
host_lookup/metadata.py
Normal file
30
host_lookup/metadata.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
from ipaddress import ip_address
|
||||
from whois import whois
|
||||
from ipwhois import IPWhois
|
||||
import validators
|
||||
from constants import URL, DOMAIN, IPV4, IPV6
|
||||
|
||||
|
||||
def check(host):
|
||||
if validators.url(host):
|
||||
host_type = URL
|
||||
elif validators.domain(host):
|
||||
host_type = DOMAIN
|
||||
elif validators.ip_address.ipv4(host):
|
||||
host_type = IPV4
|
||||
elif validators.ip_address.ipv6(host):
|
||||
host_type = IPV6
|
||||
return host_type
|
||||
|
||||
|
||||
# def lookup(host_type):
|
||||
def lookup(host):
|
||||
result = dict(whois(host))
|
||||
return result
|
||||
|
||||
|
||||
# result = whois(host_type[1])
|
||||
# return result, host_type[0]
|
||||
# obj = IPWhois(host_type[1])
|
||||
# res = obj.lookup_rdap()
|
||||
# return res, host_type[0]
|
3
host_lookup/otx_api.py
Normal file
3
host_lookup/otx_api.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Try to get historical telemetry like this page shows: https://otx.alienvault.com/indicator/ip/8.8.8.8
|
||||
# Apparently this API does not provide this information :( f.e. the below curl request does not provide information about historical OTX telemetry.
|
||||
# curl https://otx.alienvault.com/api/v1/indicators/url/http://www.freputation.com/spreputation_san_ponso/slides/IMG_0068.html/general -H "X-OTX-API-KEY: ec672963e435bb7a09c494534b79a6a7a273a5bde5ea560874cccd72e2bc76fc"
|
9
host_lookup/parse_URI.py
Normal file
9
host_lookup/parse_URI.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
# This module should extract any and all URIs (IPs or URLs) from copy and pasted text.
|
||||
|
||||
def parse(text):
|
||||
split_text = text.split()
|
||||
for URI in split_text:
|
||||
print(URI)
|
||||
|
||||
|
||||
|
9
host_lookup/spf_dmarc.py
Normal file
9
host_lookup/spf_dmarc.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from checkdmarc.dmarc import check_dmarc
|
||||
from checkdmarc.spf import check_spf
|
||||
import validators
|
||||
|
||||
|
||||
def lookup(host: str) -> tuple:
|
||||
result_dmarc = check_dmarc(host)
|
||||
result_spf = check_spf(host)
|
||||
return (result_dmarc, result_spf)
|
24
host_lookup/virustotal.py
Normal file
24
host_lookup/virustotal.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
import vt
|
||||
import os
|
||||
import requests
|
||||
import virustotal_python
|
||||
from dotenv import load_dotenv
|
||||
from pprint import pprint
|
||||
from base64 import urlsafe_b64encode
|
||||
|
||||
# todo: implement my own API request module to then try and cache the response (see -> https://realpython.com/caching-external-api-requests/#requests-cache)
|
||||
|
||||
def vt_lookup(URL):
|
||||
load_dotenv()
|
||||
api_key = os.getenv("VT_API")
|
||||
with virustotal_python.Virustotal(api_key) as vtotal:
|
||||
try:
|
||||
resp = vtotal.request("urls", data={"url": URL}, method="POST")
|
||||
print(resp)
|
||||
# Safe encode URL in base64 format
|
||||
# https://developers.virustotal.com/reference/url
|
||||
url_id = urlsafe_b64encode(URL.encode()).decode().strip("=")
|
||||
report = vtotal.request(f"urls/{url_id}")
|
||||
return report.data
|
||||
except virustotal_python.VirustotalError as err:
|
||||
print(f"Failed to send URL: {URL} for analysis and get the report: {err}")
|
77
host_lookup/virustotal_api_test.py
Normal file
77
host_lookup/virustotal_api_test.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
import json
|
||||
import os
|
||||
import requests
|
||||
from dotenv import load_dotenv
|
||||
from pprint import pprint
|
||||
from constants import URL, DOMAIN, IPV4, IPV6
|
||||
|
||||
# Would be nice to define some constants, f.e. for the various API urls, the headers, etc.
|
||||
|
||||
|
||||
def environment():
|
||||
load_dotenv()
|
||||
api_key = os.getenv("VT_API")
|
||||
return api_key
|
||||
|
||||
|
||||
# Unfortunately this works for actual URLs, not domains. See: https://docs.virustotal.com/reference/domain-info
|
||||
# This also doesn't work for IPv6 addresses, where the response_dict does not have a 'data' key. So I would have to revamp this module and make separate functions called based on host type (URL, IPv4 and -6, domain).
|
||||
|
||||
|
||||
def analysis_object(api_key, host):
|
||||
url = "https://www.virustotal.com/api/v3/urls"
|
||||
payload = {"url": ""}
|
||||
payload.update({"url": host})
|
||||
headers = {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/x-www-form-urlencoded",
|
||||
"x-apikey": api_key,
|
||||
}
|
||||
response = requests.post(url, data=payload, headers=headers)
|
||||
response_dict = json.loads(response.text)
|
||||
response_id = response_dict["data"]["id"]
|
||||
return response_id
|
||||
|
||||
|
||||
def analyse_domain(api_key, host):
|
||||
url = "https://www.virustotal.com/api/v3/domains/" + host
|
||||
headers = {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/x-www-form-urlencoded",
|
||||
"x-apikey": api_key,
|
||||
}
|
||||
analysis_response = requests.get(url, headers=headers)
|
||||
response_dict = json.loads(analysis_response.text)
|
||||
# Probably still need to turn the requests.get into a json like below
|
||||
return response_dict
|
||||
|
||||
|
||||
def analyse_URL(api_key, response_id):
|
||||
analysis_url = "https://www.virustotal.com/api/v3/analyses/{}".format(response_id)
|
||||
headers = {"accept": "application/json", "x-apikey": api_key}
|
||||
analysis_response = requests.get(analysis_url, headers=headers)
|
||||
analysis_dict = json.loads(analysis_response.text)
|
||||
# return analysis_response.text
|
||||
return analysis_dict
|
||||
|
||||
|
||||
def analyse_IP(api_key, host):
|
||||
analysis_url = "https://www.virustotal.com/api/v3/ip_addresses/{}".format(host)
|
||||
headers = {"accept": "application/json", "x-apikey": api_key}
|
||||
analysis_response = requests.get(analysis_url, headers=headers)
|
||||
analysis_dict = json.loads(analysis_response.text)
|
||||
# Implement this: https://docs.virustotal.com/reference/ip-info
|
||||
return analysis_dict
|
||||
|
||||
|
||||
def analyse(host, host_type):
|
||||
api_key = environment()
|
||||
if host_type == URL:
|
||||
response_id = analysis_object(api_key, host)
|
||||
result = analyse_URL(api_key, response_id)
|
||||
elif host_type == DOMAIN:
|
||||
result = analyse_domain(api_key, host)
|
||||
# elif for IPv4 and IPv6.
|
||||
elif host_type == IPV4 or IPV6:
|
||||
result = analyse_IP(api_key, host)
|
||||
return result
|
32
requirements.txt
Normal file
32
requirements.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
attrs==24.2.0
|
||||
blinker==1.8.2
|
||||
cattrs==24.1.0
|
||||
certifi==2024.7.4
|
||||
cffi==1.17.0
|
||||
charset-normalizer==3.3.2
|
||||
checkdmarc==5.5.0
|
||||
click==8.1.7
|
||||
cryptography==43.0.0
|
||||
dnspython==2.0.0
|
||||
expiringdict==1.2.2
|
||||
Flask==3.0.3
|
||||
idna==3.8
|
||||
ipwhois==1.2.0
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.4
|
||||
MarkupSafe==2.1.5
|
||||
platformdirs==4.2.2
|
||||
publicsuffixlist==1.0.2.20240827
|
||||
pycparser==2.22
|
||||
pyleri==1.4.3
|
||||
python-dateutil==2.9.0.post0
|
||||
python-dotenv==1.0.1
|
||||
python-whois==0.9.4
|
||||
requests==2.32.3
|
||||
requests-cache==1.2.1
|
||||
six==1.16.0
|
||||
timeout-decorator==0.5.0
|
||||
url-normalize==1.4.3
|
||||
urllib3==2.2.2
|
||||
validators==0.33.0
|
||||
Werkzeug==3.0.4
|
6
static/styles/style.css
Normal file
6
static/styles/style.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
.host_form {
|
||||
text-align: center;
|
||||
}
|
||||
.upload_form {
|
||||
text-align: center;
|
||||
}
|
103
templates/IPv4.html
Normal file
103
templates/IPv4.html
Normal file
|
@ -0,0 +1,103 @@
|
|||
{% if result %}
|
||||
<div>
|
||||
<table id="data" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>IP resolves to URL</th>
|
||||
<th>creation date</th>
|
||||
<th>registrar</th>
|
||||
<th>registrar's country of residence</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ result.metadata['domain_name'] }}</td>
|
||||
<td>{{ result.metadata['creation_date'] }}</td>
|
||||
<td>{{ result.metadata['registrar'] }}</td>
|
||||
<td>{{ result.metadata['country'] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="virustotal_stats" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Malicious</th>
|
||||
<th>Suspicious</th>
|
||||
<th>Undetected</th>
|
||||
<th>Harmless</th>
|
||||
<th>Timeout</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
{{
|
||||
result.vt['data']['attributes']['last_analysis_stats']['malicious']}}
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
result.vt['data']['attributes']['last_analysis_stats']['suspicious']
|
||||
}}
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
result.vt['data']['attributes']['last_analysis_stats']['undetected']
|
||||
}}
|
||||
</td>
|
||||
<td>
|
||||
{{ result.vt['data']['attributes']['last_analysis_stats']['harmless']
|
||||
}}
|
||||
</td>
|
||||
<td>
|
||||
{{ result.vt['data']['attributes']['last_analysis_stats']['timeout']
|
||||
}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="virustotal_all_vendors" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Vendor name</th>
|
||||
<th>Results</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for vendor,value in
|
||||
result.vt['data']['attributes']['last_analysis_results'].items() %}
|
||||
<tr>
|
||||
<td>{{ vendor }}</td>
|
||||
<td>{{ value['result'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="abuseipdb" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Abuse IPDB confidence score</th>
|
||||
<th>Total reports</th>
|
||||
<th>Last reported</th>
|
||||
<th>Tor or not</th>
|
||||
<th>Hostnames</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ result.abuseipdb.data.abuseConfidenceScore }}</td>
|
||||
<td>{{ result.abuseipdb.data.totalReports }}</td>
|
||||
<td>{{ result.abuseipdb.data.lastReportedAt }}</td>
|
||||
<td>{{ result.abuseipdb.data.isTor }}</td>
|
||||
<td>{{result.abuseipdb.data.hostnames }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% elif results %}
|
||||
<div>
|
||||
<p>VISUALIZE</p>
|
||||
</div>
|
||||
{% endif %}
|
80
templates/URL.html
Normal file
80
templates/URL.html
Normal file
|
@ -0,0 +1,80 @@
|
|||
{% if result %}
|
||||
<div>
|
||||
<table id="data" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>domain</th>
|
||||
<th>creation date</th>
|
||||
<th>registrar</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ result.metadata['domain_name'] }}</td>
|
||||
<td>{{ result.metadata['creation_date'] }}</td>
|
||||
<td>{{ result.metadata['registrar'] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="virustotal_stats" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Malicious</th>
|
||||
<th>Suspicous</th>
|
||||
<th>Undetected</th>
|
||||
<th>Harmless</th>
|
||||
<th>Timeout</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
{% for value in result.vt.data.attributes.stats.values() %}
|
||||
<td>{{ value }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="virustotal_all_vendors" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Vendor name</th>
|
||||
<th>Results</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for value in result.vt.data.attributes.results.values() if value.result == 'malicious' or value.result == 'malware' %}
|
||||
<tr>
|
||||
<td>{{ value.engine_name }}</td>
|
||||
<td>{{ value.result }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<table id="abuseipdb" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Abuse IPDB confidence score</th>
|
||||
<th>Total reports</th>
|
||||
<th>Last reported</th>
|
||||
<th>Tor or not</th>
|
||||
<th>Hostnames</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ result.abuseipdb.data.abuseConfidenceScore }}</td>
|
||||
<td>{{ result.abuseipdb.data.totalReports }}</td>
|
||||
<td>{{ result.abuseipdb.data.lastReportedAt }}</td>
|
||||
<td>{{ result.abuseipdb.data.isTor }}</td>
|
||||
<td>{{result.abuseipdb.data.hostnames }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% elif results %}
|
||||
<div>
|
||||
<p>VISUALIZE</p>
|
||||
</div>
|
||||
{% endif %}
|
109
templates/domain.html
Normal file
109
templates/domain.html
Normal file
|
@ -0,0 +1,109 @@
|
|||
{% if result %}
|
||||
<div>
|
||||
<p>domain</p>
|
||||
|
||||
<table id="data" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>URL</th>
|
||||
<th>creation date</th>
|
||||
<th>registrar</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ result.metadata['domain_name'] }}</td>
|
||||
<td>{{ result.metadata['creation_date'] }}</td>
|
||||
<td>{{ result.metadata['registrar'] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="spf_dmarc" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>DMARC record</th>
|
||||
<th>DMARC validity</th>
|
||||
<th>SPF record</th>
|
||||
<th>SPF validity</th>
|
||||
<th>SPF keys()</th>
|
||||
<th>SPF dns lookups</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ result.emailsec[0]['record'] }}</td>
|
||||
<td>{{ result.emailsec[0]['valid'] }}</td>
|
||||
<td>{{ result.emailsec[1]['record'] }}</td>
|
||||
<td>{{ result.emailsec[1]['valid'] }}</td>
|
||||
<td>{{ result.emailsec[1].keys() }}</td>
|
||||
{% for item in result.emailsec[1]['parsed']%}
|
||||
<td>{{ item }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table id="virustotal_stats" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Malicious</th>
|
||||
<th>Suspicous</th>
|
||||
<th>Undetected</th>
|
||||
<th>Harmless</th>
|
||||
<th>Timeout</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
{% for value in result.vt.data.attributes.last_analysis_stats.values()
|
||||
%}
|
||||
<td>{{ value }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="virustotal_all_vendors" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Vendor name</th>
|
||||
<th>Results</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for value in result.vt.data.attributes.last_analysis_results.values()
|
||||
%}
|
||||
<tr>
|
||||
<td>{{ value.engine_name }}</td>
|
||||
<td>{{ value.result }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="abuseipdb" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Abuse IPDB confidence score</th>
|
||||
<th>Total reports</th>
|
||||
<th>Last reported</th>
|
||||
<th>Tor or not</th>
|
||||
<th>Hostnames</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ result.abuseipdb.data.abuseConfidenceScore }}</td>
|
||||
<td>{{ result.abuseipdb.data.totalReports }}</td>
|
||||
<td>{{ result.abuseipdb.data.lastReportedAt }}</td>
|
||||
<td>{{ result.abuseipdb.data.isTor }}</td>
|
||||
<td>{{result.abuseipdb.data.hostnames }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% elif results %}
|
||||
<div>
|
||||
<p>VISUALIZE</p>
|
||||
</div>
|
||||
{% endif %}
|
8
templates/empty_form.html
Normal file
8
templates/empty_form.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
{% with messages=get_flashed_messages() %} {% if messages %}
|
||||
<p class="text-center">
|
||||
{% for message in messages %}
|
||||
<i class="fa-solid fa-circle-exclamation"></i>
|
||||
{{message}}
|
||||
<i class="fa-solid fa-circle-exclamation"></i>
|
||||
{% endfor %} {% endif %} {% endwith %}
|
||||
</p>
|
1
templates/index.html
Normal file
1
templates/index.html
Normal file
|
@ -0,0 +1 @@
|
|||
TESTIE
|
48
templates/layout.html
Normal file
48
templates/layout.html
Normal file
|
@ -0,0 +1,48 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Got something to look up? I got you!</title>
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="https://use.fontawesome.com/releases/v6.5.1/css/all.css"
|
||||
/>
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="https://cdn.datatables.net/1.10.25/css/dataTables.bootstrap5.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="{{ url_for('static', filename='styles/style.css')}}"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
{% block content %} {% endblock %}
|
||||
<script
|
||||
type="text/javascript"
|
||||
charset="utf8"
|
||||
src="https://code.jquery.com/jquery-3.6.0.min.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
charset="utf8"
|
||||
src="https://cdn.datatables.net/1.10.25/js/jquery.dataTables.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
charset="utf8"
|
||||
src="https://cdn.datatables.net/1.10.25/js/dataTables.bootstrap5.js"
|
||||
></script>
|
||||
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
97
templates/lookup_options.html
Normal file
97
templates/lookup_options.html
Normal file
|
@ -0,0 +1,97 @@
|
|||
{% extends "layout.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1 class="text-center">URL or IP lookup</h1>
|
||||
|
||||
<div class="host_form">
|
||||
<form host="/lookup" method="POST">
|
||||
<input type="text" name="host">
|
||||
<input type="SUBMIT" value="Type it in">
|
||||
</form>
|
||||
<p>OR</p>
|
||||
<form action="/lookup" host="/lookup" method="POST" enctype="multipart/form-data">
|
||||
<input type="file" name="file">
|
||||
<input type="SUBMIT" value="Upload a csv">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if host_type == "domain" %}
|
||||
|
||||
{% include "domain.html" %}
|
||||
|
||||
{% elif host_type == "URL" %}
|
||||
|
||||
{% include "URL.html" %}
|
||||
|
||||
{% elif host_type == "IPv4" %}
|
||||
|
||||
{% include "IPv4.html" %}
|
||||
|
||||
{% elif host_type == "IPv6" %}
|
||||
|
||||
{% include "IPv6.html" %}
|
||||
|
||||
{% else %}
|
||||
|
||||
<p class="text-center">Copy paste your URLs or IPs and press submit!</p>
|
||||
{% include "empty_form.html" %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#virustotal_all_vendors').DataTable({
|
||||
scrollY: 300,
|
||||
<!-- scrollX: 100, -->
|
||||
paging: false,
|
||||
searching: false,
|
||||
info: false,
|
||||
|
||||
});
|
||||
});
|
||||
$(document).ready(function () {
|
||||
$('#abuseipdb').DataTable({
|
||||
paging: false,
|
||||
searching: false,
|
||||
info: false
|
||||
});
|
||||
});
|
||||
$(document).ready(function () {
|
||||
$('#data').DataTable({
|
||||
paging: false,
|
||||
searching: false,
|
||||
info: false
|
||||
});
|
||||
});
|
||||
$(document).ready(function () {
|
||||
$('#virustotal_stats').DataTable({
|
||||
paging: false,
|
||||
searching: false,
|
||||
info: false,
|
||||
columnDefs: [ {
|
||||
targets: '_all',
|
||||
createdCell: function (td, cellData, rowData, row, col) {
|
||||
if (cellData > 0 && col == 0) {
|
||||
$(td).css({'background-color':'red','color':'white'});
|
||||
}
|
||||
if (cellData > 0 && col == 1) {
|
||||
$(td).css({'background-color':'orange','color':'white'});
|
||||
}
|
||||
if (cellData > 0 && col == 2) {
|
||||
$(td).css({'background-color':'grey','color':'white'});
|
||||
}
|
||||
if (cellData > 0 && col == 3) {
|
||||
$(td).css({'background-color':'green','color':'white'});
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
/// Trying out some more cellData searching, this time adding some text
|
||||
</script>
|
||||
{% endblock %}
|
58
templates/lookup_results.html
Normal file
58
templates/lookup_results.html
Normal file
|
@ -0,0 +1,58 @@
|
|||
{% extends "layout.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Results!</h1>
|
||||
|
||||
<p>What you looked up:</p>
|
||||
|
||||
<ul>
|
||||
<li>URL or IP: {{ URI_type }} </li>
|
||||
</ul>
|
||||
|
||||
<table id="data" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>URL</th>
|
||||
<th>creation date</th>
|
||||
<th>registrar</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ looked_up.domain_name }}</td>
|
||||
<td>{{ looked_up.creation_date }}</td>
|
||||
<td>{{ looked_up.registrar }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="virustotal_all_vendors" class="table table-striped">
|
||||
<thead>
|
||||
|
||||
<tr>
|
||||
<th>Vendor name</th>
|
||||
<th>Results</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for value in virustotal_results.attributes.last_analysis_results.values() %}
|
||||
<tr>
|
||||
<td>{{ value.engine_name }}</td>
|
||||
<td>{{ value.result }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{# Below JavaScript table magic from: https://blog.miguelgrinberg.com/post/beautiful-interactive-tables-for-your-flask-templates #}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#virustotal_all_vendors').DataTable();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
15
templates/test.html
Normal file
15
templates/test.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
<table id="test" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>domain</th>
|
||||
<th>creation date</th>
|
||||
<th>registrar</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ results.metadata['domain_name'] }}</td>
|
||||
<td>{{ results.metadata['creation_date'] }}</td>
|
||||
<td>{{ results.metadata['registrar'] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
0
templates/upload.html~
Normal file
0
templates/upload.html~
Normal file
22
upload/csv_parse.py
Normal file
22
upload/csv_parse.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
import csv
|
||||
from io import StringIO
|
||||
from validators import ipv4, ipv6, url, domain
|
||||
|
||||
|
||||
def extract(uploaded):
|
||||
hosts = []
|
||||
content = uploaded.read()
|
||||
decoded = content.decode("utf-8")
|
||||
file = StringIO(decoded)
|
||||
csv_data = csv.reader(file, delimiter=",")
|
||||
for row in csv_data:
|
||||
for value in row:
|
||||
if url(value):
|
||||
hosts.append(value)
|
||||
elif domain(value):
|
||||
hosts.append(value)
|
||||
elif ipv4(value):
|
||||
hosts.append(value)
|
||||
elif ipv6(value):
|
||||
hosts.append(value)
|
||||
return hosts
|
Loading…
Reference in New Issue
Block a user