Compare commits
4 Commits
main
...
ablesov/fi
Author | SHA1 | Date | |
---|---|---|---|
|
13ddb3b0bc | ||
|
daca707ad8 | ||
|
216db5d387 | ||
|
887e90cd06 |
@ -1,19 +0,0 @@
|
|||||||
# This workflow will improvise current file with AI genereated documentation and Create new PR
|
|
||||||
|
|
||||||
name: Snorkell.ai - Revolutionizing Documentation on GitHub
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: ["main"]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
Documentation:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Snorkell DocGen Client
|
|
||||||
uses: SingularityX-ai/snorkell-documentation-client@v1.0.0
|
|
||||||
with:
|
|
||||||
client_id: ${{ secrets.SNORKELL_CLIENT_ID }}
|
|
||||||
api_key: ${{ secrets.SNORKELL_API_KEY }}
|
|
||||||
branch_name: "main"
|
|
@ -1,478 +1,479 @@
|
|||||||
|
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from classes.parsezeeklogs import ParseZeekLogs
|
from classes.parsezeeklogs import ParseZeekLogs
|
||||||
from netaddr import IPNetwork, IPAddress
|
from netaddr import IPNetwork, IPAddress
|
||||||
from utils import get_iocs, get_config, get_whitelist
|
from utils import get_iocs, get_config, get_whitelist
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
import json
|
import json
|
||||||
import pydig
|
import pydig
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import whois
|
import whois
|
||||||
|
from security import safe_command
|
||||||
|
|
||||||
class ZeekEngine(object):
|
|
||||||
|
class ZeekEngine(object):
|
||||||
def __init__(self, capture_directory):
|
|
||||||
self.working_dir = capture_directory
|
def __init__(self, capture_directory):
|
||||||
self.alerts = []
|
self.working_dir = capture_directory
|
||||||
self.conns = []
|
self.alerts = []
|
||||||
self.ssl = []
|
self.conns = []
|
||||||
self.http = []
|
self.ssl = []
|
||||||
self.dns = []
|
self.http = []
|
||||||
self.files = []
|
self.dns = []
|
||||||
self.whitelist = []
|
self.files = []
|
||||||
|
self.whitelist = []
|
||||||
# Get analysis and userlang configuration
|
|
||||||
self.heuristics_analysis = get_config(("analysis", "heuristics"))
|
# Get analysis and userlang configuration
|
||||||
self.iocs_analysis = get_config(("analysis", "iocs"))
|
self.heuristics_analysis = get_config(("analysis", "heuristics"))
|
||||||
self.whitelist_analysis = get_config(("analysis", "whitelist"))
|
self.iocs_analysis = get_config(("analysis", "iocs"))
|
||||||
self.active_analysis = get_config(("analysis", "active"))
|
self.whitelist_analysis = get_config(("analysis", "whitelist"))
|
||||||
self.userlang = get_config(("frontend", "user_lang"))
|
self.active_analysis = get_config(("analysis", "active"))
|
||||||
|
self.userlang = get_config(("frontend", "user_lang"))
|
||||||
# Retreive IOCs.
|
|
||||||
if self.iocs_analysis:
|
# Retreive IOCs.
|
||||||
self.bl_cidrs = [[IPNetwork(cidr[0]), cidr[1]]
|
if self.iocs_analysis:
|
||||||
for cidr in get_iocs("cidr")]
|
self.bl_cidrs = [[IPNetwork(cidr[0]), cidr[1]]
|
||||||
self.bl_hosts = get_iocs("ip4addr") + get_iocs("ip6addr")
|
for cidr in get_iocs("cidr")]
|
||||||
self.bl_domains = get_iocs("domain")
|
self.bl_hosts = get_iocs("ip4addr") + get_iocs("ip6addr")
|
||||||
self.bl_freedns = get_iocs("freedns")
|
self.bl_domains = get_iocs("domain")
|
||||||
self.bl_nameservers = get_iocs("ns")
|
self.bl_freedns = get_iocs("freedns")
|
||||||
self.bl_tlds = get_iocs("tld")
|
self.bl_nameservers = get_iocs("ns")
|
||||||
|
self.bl_tlds = get_iocs("tld")
|
||||||
# Retreive whitelisted items.
|
|
||||||
if self.whitelist_analysis:
|
# Retreive whitelisted items.
|
||||||
self.wl_cidrs = [IPNetwork(cidr) for cidr in get_whitelist("cidr")]
|
if self.whitelist_analysis:
|
||||||
self.wl_hosts = get_whitelist("ip4addr") + get_whitelist("ip6addr")
|
self.wl_cidrs = [IPNetwork(cidr) for cidr in get_whitelist("cidr")]
|
||||||
self.wl_domains = get_whitelist("domain")
|
self.wl_hosts = get_whitelist("ip4addr") + get_whitelist("ip6addr")
|
||||||
|
self.wl_domains = get_whitelist("domain")
|
||||||
# Load template language
|
|
||||||
if not re.match("^[a-z]{2,3}$", self.userlang):
|
# Load template language
|
||||||
self.userlang = "en"
|
if not re.match("^[a-z]{2,3}$", self.userlang):
|
||||||
with open(os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "locales/{}.json".format(self.userlang))) as f:
|
self.userlang = "en"
|
||||||
self.template = json.load(f)["alerts"]
|
with open(os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "locales/{}.json".format(self.userlang))) as f:
|
||||||
|
self.template = json.load(f)["alerts"]
|
||||||
def fill_dns(self, dir):
|
|
||||||
"""
|
def fill_dns(self, dir):
|
||||||
Fill the DNS resolutions thanks to the dns.log.
|
"""
|
||||||
:return: nothing - all resolutions appended to self.dns.
|
Fill the DNS resolutions thanks to the dns.log.
|
||||||
"""
|
:return: nothing - all resolutions appended to self.dns.
|
||||||
if os.path.isfile(os.path.join(dir, "dns.log")):
|
"""
|
||||||
for record in ParseZeekLogs(os.path.join(dir, "dns.log"), output_format="json", safe_headers=False):
|
if os.path.isfile(os.path.join(dir, "dns.log")):
|
||||||
if record is not None:
|
for record in ParseZeekLogs(os.path.join(dir, "dns.log"), output_format="json", safe_headers=False):
|
||||||
if record["qtype_name"] in ["A", "AAAA"]:
|
if record is not None:
|
||||||
d = {"domain": record["query"],
|
if record["qtype_name"] in ["A", "AAAA"]:
|
||||||
"answers": record["answers"].split(",")}
|
d = {"domain": record["query"],
|
||||||
if d not in self.dns:
|
"answers": record["answers"].split(",")}
|
||||||
self.dns.append(d)
|
if d not in self.dns:
|
||||||
|
self.dns.append(d)
|
||||||
def netflow_check(self, dir):
|
|
||||||
"""
|
def netflow_check(self, dir):
|
||||||
Enrich and check the netflow from the conn.log against whitelist and IOCs.
|
"""
|
||||||
:return: nothing - all stuff appended to self.alerts
|
Enrich and check the netflow from the conn.log against whitelist and IOCs.
|
||||||
"""
|
:return: nothing - all stuff appended to self.alerts
|
||||||
max_ports = get_config(("analysis", "max_ports"))
|
"""
|
||||||
http_default_port = get_config(("analysis", "http_default_port"))
|
max_ports = get_config(("analysis", "max_ports"))
|
||||||
|
http_default_port = get_config(("analysis", "http_default_port"))
|
||||||
# Get the netflow from conn.log.
|
|
||||||
if os.path.isfile(os.path.join(dir, "conn.log")):
|
# Get the netflow from conn.log.
|
||||||
for record in ParseZeekLogs(os.path.join(dir, "conn.log"), output_format="json", safe_headers=False):
|
if os.path.isfile(os.path.join(dir, "conn.log")):
|
||||||
if record is not None:
|
for record in ParseZeekLogs(os.path.join(dir, "conn.log"), output_format="json", safe_headers=False):
|
||||||
c = {"ip_dst": record["id.resp_h"],
|
if record is not None:
|
||||||
"proto": record["proto"],
|
c = {"ip_dst": record["id.resp_h"],
|
||||||
"port_dst": record["id.resp_p"],
|
"proto": record["proto"],
|
||||||
"service": record["service"],
|
"port_dst": record["id.resp_p"],
|
||||||
"alert_tiggered": False}
|
"service": record["service"],
|
||||||
if c not in self.conns:
|
"alert_tiggered": False}
|
||||||
self.conns.append(c)
|
if c not in self.conns:
|
||||||
|
self.conns.append(c)
|
||||||
# Let's add some dns resolutions.
|
|
||||||
for c in self.conns:
|
# Let's add some dns resolutions.
|
||||||
c["resolution"] = self.resolve(c["ip_dst"])
|
for c in self.conns:
|
||||||
|
c["resolution"] = self.resolve(c["ip_dst"])
|
||||||
# Order the conns list by the resolution field.
|
|
||||||
self.conns = sorted(self.conns, key=lambda c: c["resolution"])
|
# Order the conns list by the resolution field.
|
||||||
|
self.conns = sorted(self.conns, key=lambda c: c["resolution"])
|
||||||
# Check for whitelisted assets, if any, delete the record.
|
|
||||||
if self.whitelist_analysis:
|
# Check for whitelisted assets, if any, delete the record.
|
||||||
|
if self.whitelist_analysis:
|
||||||
for i, c in enumerate(self.conns):
|
|
||||||
if c["ip_dst"] in [ip for ip in self.wl_hosts]:
|
for i, c in enumerate(self.conns):
|
||||||
self.whitelist.append(self.conns[i])
|
if c["ip_dst"] in [ip for ip in self.wl_hosts]:
|
||||||
self.conns[i] = False
|
self.whitelist.append(self.conns[i])
|
||||||
elif c["resolution"] in self.wl_domains:
|
self.conns[i] = False
|
||||||
self.whitelist.append(self.conns[i])
|
elif c["resolution"] in self.wl_domains:
|
||||||
self.conns[i] = False
|
self.whitelist.append(self.conns[i])
|
||||||
elif True in [c["resolution"].endswith("." + dom) for dom in self.wl_domains]:
|
self.conns[i] = False
|
||||||
self.whitelist.append(self.conns[i])
|
elif True in [c["resolution"].endswith("." + dom) for dom in self.wl_domains]:
|
||||||
self.conns[i] = False
|
self.whitelist.append(self.conns[i])
|
||||||
elif True in [IPAddress(c["ip_dst"]) in cidr for cidr in self.wl_cidrs]:
|
self.conns[i] = False
|
||||||
self.whitelist.append(self.conns[i])
|
elif True in [IPAddress(c["ip_dst"]) in cidr for cidr in self.wl_cidrs]:
|
||||||
self.conns[i] = False
|
self.whitelist.append(self.conns[i])
|
||||||
|
self.conns[i] = False
|
||||||
# Let's delete whitelisted connections.
|
|
||||||
self.conns = list(filter(lambda c: c != False, self.conns))
|
# Let's delete whitelisted connections.
|
||||||
|
self.conns = list(filter(lambda c: c != False, self.conns))
|
||||||
if self.heuristics_analysis:
|
|
||||||
for c in self.conns:
|
if self.heuristics_analysis:
|
||||||
# Check for UDP / ICMP (strange from a smartphone.)
|
for c in self.conns:
|
||||||
if c["proto"] in ["UDP", "ICMP"]:
|
# Check for UDP / ICMP (strange from a smartphone.)
|
||||||
c["alert_tiggered"] = True
|
if c["proto"] in ["UDP", "ICMP"]:
|
||||||
self.alerts.append({"title": self.template["PROTO-01"]["title"].format(c["proto"].upper(), c["resolution"]),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["PROTO-01"]["description"].format(c["proto"].upper(), c["resolution"]),
|
self.alerts.append({"title": self.template["PROTO-01"]["title"].format(c["proto"].upper(), c["resolution"]),
|
||||||
"host": c["resolution"],
|
"description": self.template["PROTO-01"]["description"].format(c["proto"].upper(), c["resolution"]),
|
||||||
"level": "Moderate",
|
"host": c["resolution"],
|
||||||
"id": "PROTO-01"})
|
"level": "Moderate",
|
||||||
# Check for use of ports over 1024.
|
"id": "PROTO-01"})
|
||||||
if c["port_dst"] >= max_ports:
|
# Check for use of ports over 1024.
|
||||||
c["alert_tiggered"] = True
|
if c["port_dst"] >= max_ports:
|
||||||
self.alerts.append({"title": self.template["PROTO-02"]["title"].format(c["proto"].upper(), c["resolution"], max_ports),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["PROTO-02"]["description"].format(c["proto"].upper(), c["resolution"], c["port_dst"]),
|
self.alerts.append({"title": self.template["PROTO-02"]["title"].format(c["proto"].upper(), c["resolution"], max_ports),
|
||||||
"host": c["resolution"],
|
"description": self.template["PROTO-02"]["description"].format(c["proto"].upper(), c["resolution"], c["port_dst"]),
|
||||||
"level": "Low",
|
"host": c["resolution"],
|
||||||
"id": "PROTO-02"})
|
"level": "Low",
|
||||||
# Check for use of HTTP.
|
"id": "PROTO-02"})
|
||||||
if c["service"] == "http" and c["port_dst"] == http_default_port:
|
# Check for use of HTTP.
|
||||||
c["alert_tiggered"] = True
|
if c["service"] == "http" and c["port_dst"] == http_default_port:
|
||||||
self.alerts.append({"title": self.template["PROTO-03"]["title"].format(c["resolution"]),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["PROTO-03"]["description"].format(c["resolution"]),
|
self.alerts.append({"title": self.template["PROTO-03"]["title"].format(c["resolution"]),
|
||||||
"host": c["resolution"],
|
"description": self.template["PROTO-03"]["description"].format(c["resolution"]),
|
||||||
"level": "Low",
|
"host": c["resolution"],
|
||||||
"id": "PROTO-03"})
|
"level": "Low",
|
||||||
|
"id": "PROTO-03"})
|
||||||
# Check for use of HTTP on a non standard port.
|
|
||||||
if c["service"] == "http" and c["port_dst"] != http_default_port:
|
# Check for use of HTTP on a non standard port.
|
||||||
c["alert_tiggered"] = True
|
if c["service"] == "http" and c["port_dst"] != http_default_port:
|
||||||
self.alerts.append({"title": self.template["PROTO-04"]["title"].format(c["resolution"], c["port_dst"]),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["PROTO-04"]["description"].format(c["resolution"], c["port_dst"]),
|
self.alerts.append({"title": self.template["PROTO-04"]["title"].format(c["resolution"], c["port_dst"]),
|
||||||
"host": c["resolution"],
|
"description": self.template["PROTO-04"]["description"].format(c["resolution"], c["port_dst"]),
|
||||||
"level": "Moderate",
|
"host": c["resolution"],
|
||||||
"id": "PROTO-04"})
|
"level": "Moderate",
|
||||||
# Check for non-resolved IP address.
|
"id": "PROTO-04"})
|
||||||
if c["ip_dst"] == c["resolution"]:
|
# Check for non-resolved IP address.
|
||||||
c["alert_tiggered"] = True
|
if c["ip_dst"] == c["resolution"]:
|
||||||
self.alerts.append({"title": self.template["PROTO-05"]["title"].format(c["ip_dst"]),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["PROTO-05"]["description"].format(c["ip_dst"]),
|
self.alerts.append({"title": self.template["PROTO-05"]["title"].format(c["ip_dst"]),
|
||||||
"host": c["ip_dst"],
|
"description": self.template["PROTO-05"]["description"].format(c["ip_dst"]),
|
||||||
"level": "Low",
|
"host": c["ip_dst"],
|
||||||
"id": "PROTO-05"})
|
"level": "Low",
|
||||||
|
"id": "PROTO-05"})
|
||||||
if self.iocs_analysis:
|
|
||||||
|
if self.iocs_analysis:
|
||||||
for c in self.conns:
|
|
||||||
# Check for blacklisted IP address.
|
for c in self.conns:
|
||||||
for host in self.bl_hosts:
|
# Check for blacklisted IP address.
|
||||||
if c["ip_dst"] == host[0]:
|
for host in self.bl_hosts:
|
||||||
c["alert_tiggered"] = True
|
if c["ip_dst"] == host[0]:
|
||||||
self.alerts.append({"title": self.template["IOC-01"]["title"].format(c["resolution"], c["ip_dst"], host[1].upper()),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["IOC-01"]["description"].format(c["ip_dst"]),
|
self.alerts.append({"title": self.template["IOC-01"]["title"].format(c["resolution"], c["ip_dst"], host[1].upper()),
|
||||||
"host": c["resolution"],
|
"description": self.template["IOC-01"]["description"].format(c["ip_dst"]),
|
||||||
"level": "High",
|
"host": c["resolution"],
|
||||||
"id": "IOC-01"})
|
"level": "High",
|
||||||
break
|
"id": "IOC-01"})
|
||||||
# Check for blacklisted CIDR.
|
break
|
||||||
for cidr in self.bl_cidrs:
|
# Check for blacklisted CIDR.
|
||||||
if IPAddress(c["ip_dst"]) in cidr[0]:
|
for cidr in self.bl_cidrs:
|
||||||
c["alert_tiggered"] = True
|
if IPAddress(c["ip_dst"]) in cidr[0]:
|
||||||
self.alerts.append({"title": self.template["IOC-02"]["title"].format(c["resolution"], cidr[0], cidr[1].upper()),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["IOC-02"]["description"].format(c["resolution"]),
|
self.alerts.append({"title": self.template["IOC-02"]["title"].format(c["resolution"], cidr[0], cidr[1].upper()),
|
||||||
"host": c["resolution"],
|
"description": self.template["IOC-02"]["description"].format(c["resolution"]),
|
||||||
"level": "Moderate",
|
"host": c["resolution"],
|
||||||
"id": "IOC-02"})
|
"level": "Moderate",
|
||||||
# Check for blacklisted domain.
|
"id": "IOC-02"})
|
||||||
for domain in self.bl_domains:
|
# Check for blacklisted domain.
|
||||||
if c["resolution"].endswith(domain[0]):
|
for domain in self.bl_domains:
|
||||||
if domain[1] != "tracker":
|
if c["resolution"].endswith(domain[0]):
|
||||||
c["alert_tiggered"] = True
|
if domain[1] != "tracker":
|
||||||
self.alerts.append({"title": self.template["IOC-03"]["title"].format(c["resolution"], domain[1].upper()),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["IOC-03"]["description"].format(c["resolution"]),
|
self.alerts.append({"title": self.template["IOC-03"]["title"].format(c["resolution"], domain[1].upper()),
|
||||||
"host": c["resolution"],
|
"description": self.template["IOC-03"]["description"].format(c["resolution"]),
|
||||||
"level": "High",
|
"host": c["resolution"],
|
||||||
"id": "IOC-03"})
|
"level": "High",
|
||||||
else:
|
"id": "IOC-03"})
|
||||||
c["alert_tiggered"] = True
|
else:
|
||||||
self.alerts.append({"title": self.template["IOC-04"]["title"].format(c["resolution"], domain[1].upper()),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["IOC-04"]["description"].format(c["resolution"]),
|
self.alerts.append({"title": self.template["IOC-04"]["title"].format(c["resolution"], domain[1].upper()),
|
||||||
"host": c["resolution"],
|
"description": self.template["IOC-04"]["description"].format(c["resolution"]),
|
||||||
"level": "Moderate",
|
"host": c["resolution"],
|
||||||
"id": "IOC-04"})
|
"level": "Moderate",
|
||||||
# Check for blacklisted FreeDNS.
|
"id": "IOC-04"})
|
||||||
for domain in self.bl_freedns:
|
# Check for blacklisted FreeDNS.
|
||||||
if c["resolution"].endswith("." + domain[0]):
|
for domain in self.bl_freedns:
|
||||||
c["alert_tiggered"] = True
|
if c["resolution"].endswith("." + domain[0]):
|
||||||
self.alerts.append({"title": self.template["IOC-05"]["title"].format(c["resolution"]),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["IOC-05"]["description"].format(c["resolution"]),
|
self.alerts.append({"title": self.template["IOC-05"]["title"].format(c["resolution"]),
|
||||||
"host": c["resolution"],
|
"description": self.template["IOC-05"]["description"].format(c["resolution"]),
|
||||||
"level": "Moderate",
|
"host": c["resolution"],
|
||||||
"id": "IOC-05"})
|
"level": "Moderate",
|
||||||
|
"id": "IOC-05"})
|
||||||
# Check for suspect tlds.
|
|
||||||
for tld in self.bl_tlds:
|
# Check for suspect tlds.
|
||||||
if c["resolution"].endswith(tld[0]):
|
for tld in self.bl_tlds:
|
||||||
c["alert_tiggered"] = True
|
if c["resolution"].endswith(tld[0]):
|
||||||
self.alerts.append({"title": self.template["IOC-06"]["title"].format(c["resolution"]),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["IOC-06"]["description"].format(c["resolution"], tld[0]),
|
self.alerts.append({"title": self.template["IOC-06"]["title"].format(c["resolution"]),
|
||||||
"host": c["resolution"],
|
"description": self.template["IOC-06"]["description"].format(c["resolution"], tld[0]),
|
||||||
"level": "Low",
|
"host": c["resolution"],
|
||||||
"id": "IOC-06"})
|
"level": "Low",
|
||||||
if self.active_analysis:
|
"id": "IOC-06"})
|
||||||
for c in self.conns:
|
if self.active_analysis:
|
||||||
try: # Domain nameservers check.
|
for c in self.conns:
|
||||||
name_servers = pydig.query(c["resolution"], "NS")
|
try: # Domain nameservers check.
|
||||||
if len(name_servers):
|
name_servers = pydig.query(c["resolution"], "NS")
|
||||||
for ns in self.bl_nameservers:
|
if len(name_servers):
|
||||||
if name_servers[0].endswith(".{}.".format(ns[0])):
|
for ns in self.bl_nameservers:
|
||||||
c["alert_tiggered"] = True
|
if name_servers[0].endswith(".{}.".format(ns[0])):
|
||||||
self.alerts.append({"title": self.template["ACT-01"]["title"].format(c["resolution"], name_servers[0]),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["ACT-01"]["description"].format(c["resolution"]),
|
self.alerts.append({"title": self.template["ACT-01"]["title"].format(c["resolution"], name_servers[0]),
|
||||||
"host": c["resolution"],
|
"description": self.template["ACT-01"]["description"].format(c["resolution"]),
|
||||||
"level": "Moderate",
|
"host": c["resolution"],
|
||||||
"id": "ACT-01"})
|
"level": "Moderate",
|
||||||
except:
|
"id": "ACT-01"})
|
||||||
pass
|
except:
|
||||||
|
pass
|
||||||
try: # Domain history check.
|
|
||||||
|
try: # Domain history check.
|
||||||
whois_record = whois.whois(c["resolution"])
|
|
||||||
creation_date = whois_record.creation_date if type(
|
whois_record = whois.whois(c["resolution"])
|
||||||
whois_record.creation_date) is not list else whois_record.creation_date[0]
|
creation_date = whois_record.creation_date if type(
|
||||||
creation_days = abs((datetime.now() - creation_date).days)
|
whois_record.creation_date) is not list else whois_record.creation_date[0]
|
||||||
if creation_days < 365:
|
creation_days = abs((datetime.now() - creation_date).days)
|
||||||
c["alert_tiggered"] = True
|
if creation_days < 365:
|
||||||
self.alerts.append({"title": self.template["ACT-02"]["title"].format(c["resolution"], creation_days),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["ACT-02"]["description"].format(c["resolution"]),
|
self.alerts.append({"title": self.template["ACT-02"]["title"].format(c["resolution"], creation_days),
|
||||||
"host": c["resolution"],
|
"description": self.template["ACT-02"]["description"].format(c["resolution"]),
|
||||||
"level": "Moderate",
|
"host": c["resolution"],
|
||||||
"id": "ACT-02"})
|
"level": "Moderate",
|
||||||
|
"id": "ACT-02"})
|
||||||
except:
|
|
||||||
pass
|
except:
|
||||||
|
pass
|
||||||
def files_check(self, dir):
|
|
||||||
"""
|
def files_check(self, dir):
|
||||||
Check on the files.log:
|
"""
|
||||||
* Check certificates sha1
|
Check on the files.log:
|
||||||
* [todo] Check possible binary data or APKs?
|
* Check certificates sha1
|
||||||
:return: nothing - all stuff appended to self.alerts
|
* [todo] Check possible binary data or APKs?
|
||||||
"""
|
:return: nothing - all stuff appended to self.alerts
|
||||||
|
"""
|
||||||
if not self.iocs_analysis:
|
|
||||||
return
|
if not self.iocs_analysis:
|
||||||
|
return
|
||||||
bl_certs = get_iocs("sha1cert")
|
|
||||||
|
bl_certs = get_iocs("sha1cert")
|
||||||
if os.path.isfile(os.path.join(dir, "files.log")):
|
|
||||||
for record in ParseZeekLogs(os.path.join(dir, "files.log"), output_format="json", safe_headers=False):
|
if os.path.isfile(os.path.join(dir, "files.log")):
|
||||||
if record is not None:
|
for record in ParseZeekLogs(os.path.join(dir, "files.log"), output_format="json", safe_headers=False):
|
||||||
f = {"filename": record["filename"],
|
if record is not None:
|
||||||
"ip_src": record["id.orig_h"],
|
f = {"filename": record["filename"],
|
||||||
"ip_dst": record["id.resp_h"],
|
"ip_src": record["id.orig_h"],
|
||||||
"mime_type": record["mime_type"],
|
"ip_dst": record["id.resp_h"],
|
||||||
"sha1": record["sha1"]}
|
"mime_type": record["mime_type"],
|
||||||
if f not in self.files:
|
"sha1": record["sha1"]}
|
||||||
self.files.append(f)
|
if f not in self.files:
|
||||||
|
self.files.append(f)
|
||||||
for f in self.files:
|
|
||||||
if f["mime_type"] == "application/x-x509-user-cert":
|
for f in self.files:
|
||||||
for cert in bl_certs: # Check for blacklisted certificate.
|
if f["mime_type"] == "application/x-x509-user-cert":
|
||||||
if f["sha1"] == cert[0]:
|
for cert in bl_certs: # Check for blacklisted certificate.
|
||||||
host = self.resolve(f["ip_src"])
|
if f["sha1"] == cert[0]:
|
||||||
self.alerts.append({"title": self.template["IOC-07"]["title"].format(cert[1].upper(), host),
|
host = self.resolve(f["ip_src"])
|
||||||
"description": self.template["IOC-07"]["description"].format(f["sha1"], host),
|
self.alerts.append({"title": self.template["IOC-07"]["title"].format(cert[1].upper(), host),
|
||||||
"host": host,
|
"description": self.template["IOC-07"]["description"].format(f["sha1"], host),
|
||||||
"level": "High",
|
"host": host,
|
||||||
"id": "IOC-07"})
|
"level": "High",
|
||||||
|
"id": "IOC-07"})
|
||||||
def http_check(self, dir):
|
|
||||||
"""
|
def http_check(self, dir):
|
||||||
Check on the http.log:
|
"""
|
||||||
* Blacklisted domain/tld from the Host header field.
|
Check on the http.log:
|
||||||
Can be used when no DNS query have been done during the session (already cached by the device.)
|
* Blacklisted domain/tld from the Host header field.
|
||||||
:return: nothing - all stuff appended to self.alerts
|
Can be used when no DNS query have been done during the session (already cached by the device.)
|
||||||
"""
|
:return: nothing - all stuff appended to self.alerts
|
||||||
|
"""
|
||||||
if os.path.isfile(os.path.join(dir, "http.log")):
|
|
||||||
for record in ParseZeekLogs(os.path.join(dir, "http.log"), output_format="json", safe_headers=False):
|
if os.path.isfile(os.path.join(dir, "http.log")):
|
||||||
if record is not None:
|
for record in ParseZeekLogs(os.path.join(dir, "http.log"), output_format="json", safe_headers=False):
|
||||||
c = {"host": record['host']}
|
if record is not None:
|
||||||
if c not in self.http:
|
c = {"host": record['host']}
|
||||||
self.http.append(c)
|
if c not in self.http:
|
||||||
|
self.http.append(c)
|
||||||
if self.iocs_analysis:
|
|
||||||
for c in self.http:
|
if self.iocs_analysis:
|
||||||
|
for c in self.http:
|
||||||
# If we already know the host form DNS, let's continue.
|
|
||||||
if c["host"] in [r["domain"] for r in self.dns]:
|
# If we already know the host form DNS, let's continue.
|
||||||
continue
|
if c["host"] in [r["domain"] for r in self.dns]:
|
||||||
|
continue
|
||||||
# Check for blacklisted domain.
|
|
||||||
for h in self.bl_domains:
|
# Check for blacklisted domain.
|
||||||
if h[1] != "tracker":
|
for h in self.bl_domains:
|
||||||
if c["host"].endswith(h[0]):
|
if h[1] != "tracker":
|
||||||
self.alerts.append({"title": self.template["IOC-08"]["title"].format(c["host"], h[1].upper()),
|
if c["host"].endswith(h[0]):
|
||||||
"description": self.template["IOC-08"]["description"].format(c["host"]),
|
self.alerts.append({"title": self.template["IOC-08"]["title"].format(c["host"], h[1].upper()),
|
||||||
"host": c["host"],
|
"description": self.template["IOC-08"]["description"].format(c["host"]),
|
||||||
"level": "High",
|
"host": c["host"],
|
||||||
"id": "IOC-08"})
|
"level": "High",
|
||||||
# Check for freedns.
|
"id": "IOC-08"})
|
||||||
for h in self.bl_freedns:
|
# Check for freedns.
|
||||||
if c["host"].endswith(h[0]):
|
for h in self.bl_freedns:
|
||||||
self.alerts.append({"title": self.template["IOC-09"]["title"].format(c["host"]),
|
if c["host"].endswith(h[0]):
|
||||||
"description": self.template["IOC-09"]["description"].format(c["host"]),
|
self.alerts.append({"title": self.template["IOC-09"]["title"].format(c["host"]),
|
||||||
"host": c["host"],
|
"description": self.template["IOC-09"]["description"].format(c["host"]),
|
||||||
"level": "Moderate",
|
"host": c["host"],
|
||||||
"id": "IOC-09"})
|
"level": "Moderate",
|
||||||
# Check for fancy TLD.
|
"id": "IOC-09"})
|
||||||
for h in self.bl_tlds:
|
# Check for fancy TLD.
|
||||||
if c["host"].endswith(h[0]):
|
for h in self.bl_tlds:
|
||||||
self.alerts.append({"title": self.template["IOC-10"]["title"].format(c["host"]),
|
if c["host"].endswith(h[0]):
|
||||||
"description": self.template["IOC-10"]["description"].format(c["host"], h[0]),
|
self.alerts.append({"title": self.template["IOC-10"]["title"].format(c["host"]),
|
||||||
"host": c["host"],
|
"description": self.template["IOC-10"]["description"].format(c["host"], h[0]),
|
||||||
"level": "Low",
|
"host": c["host"],
|
||||||
"id": "IOC-10"})
|
"level": "Low",
|
||||||
|
"id": "IOC-10"})
|
||||||
def ssl_check(self, dir):
|
|
||||||
"""
|
def ssl_check(self, dir):
|
||||||
Check on the ssl.log:
|
"""
|
||||||
* SSL connections which doesn't use the 443.
|
Check on the ssl.log:
|
||||||
* "Free" certificate issuer (taken from the config).
|
* SSL connections which doesn't use the 443.
|
||||||
* Self-signed certificates.
|
* "Free" certificate issuer (taken from the config).
|
||||||
* Blacklisted domain in the CN
|
* Self-signed certificates.
|
||||||
:return: nothing - all stuff appended to self.alerts
|
* Blacklisted domain in the CN
|
||||||
"""
|
:return: nothing - all stuff appended to self.alerts
|
||||||
ssl_default_ports = get_config(("analysis", "ssl_default_ports"))
|
"""
|
||||||
free_issuers = get_config(("analysis", "free_issuers"))
|
ssl_default_ports = get_config(("analysis", "ssl_default_ports"))
|
||||||
|
free_issuers = get_config(("analysis", "free_issuers"))
|
||||||
if os.path.isfile(os.path.join(dir, "ssl.log")):
|
|
||||||
for record in ParseZeekLogs(os.path.join(dir, "ssl.log"), output_format="json", safe_headers=False):
|
if os.path.isfile(os.path.join(dir, "ssl.log")):
|
||||||
if record is not None:
|
for record in ParseZeekLogs(os.path.join(dir, "ssl.log"), output_format="json", safe_headers=False):
|
||||||
c = {"host": record['id.resp_h'],
|
if record is not None:
|
||||||
"port": record['id.resp_p'],
|
c = {"host": record['id.resp_h'],
|
||||||
"issuer": record["issuer"] if "issuer" in record else "",
|
"port": record['id.resp_p'],
|
||||||
"validation_status": record["validation_status"],
|
"issuer": record["issuer"] if "issuer" in record else "",
|
||||||
"cn": record["server_name"] if "server_name" in record else ""}
|
"validation_status": record["validation_status"],
|
||||||
if c not in self.ssl:
|
"cn": record["server_name"] if "server_name" in record else ""}
|
||||||
self.ssl.append(c)
|
if c not in self.ssl:
|
||||||
|
self.ssl.append(c)
|
||||||
if self.heuristics_analysis:
|
|
||||||
for cert in self.ssl:
|
if self.heuristics_analysis:
|
||||||
host = self.resolve(cert["host"])
|
for cert in self.ssl:
|
||||||
|
host = self.resolve(cert["host"])
|
||||||
# If the associated host has not whitelisted, check the cert.
|
|
||||||
for c in self.conns:
|
# If the associated host has not whitelisted, check the cert.
|
||||||
if host in c["resolution"]:
|
for c in self.conns:
|
||||||
# Check for non generic SSL port.
|
if host in c["resolution"]:
|
||||||
if cert["port"] not in ssl_default_ports:
|
# Check for non generic SSL port.
|
||||||
c["alert_tiggered"] = True
|
if cert["port"] not in ssl_default_ports:
|
||||||
self.alerts.append({"title": self.template["SSL-01"]["title"].format(cert["port"], host),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["SSL-01"]["description"].format(host),
|
self.alerts.append({"title": self.template["SSL-01"]["title"].format(cert["port"], host),
|
||||||
"host": host,
|
"description": self.template["SSL-01"]["description"].format(host),
|
||||||
"level": "Moderate",
|
"host": host,
|
||||||
"id": "SSL-01"})
|
"level": "Moderate",
|
||||||
# Check Free SSL certificates.
|
"id": "SSL-01"})
|
||||||
if cert["issuer"] in free_issuers:
|
# Check Free SSL certificates.
|
||||||
c["alert_tiggered"] = True
|
if cert["issuer"] in free_issuers:
|
||||||
self.alerts.append({"title": self.template["SSL-02"]["title"].format(host),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["SSL-02"]["description"],
|
self.alerts.append({"title": self.template["SSL-02"]["title"].format(host),
|
||||||
"host": host,
|
"description": self.template["SSL-02"]["description"],
|
||||||
"level": "Moderate",
|
"host": host,
|
||||||
"id": "SSL-02"})
|
"level": "Moderate",
|
||||||
# Check for self-signed certificates.
|
"id": "SSL-02"})
|
||||||
if cert["validation_status"] == "self signed certificate in certificate chain":
|
# Check for self-signed certificates.
|
||||||
c["alert_tiggered"] = True
|
if cert["validation_status"] == "self signed certificate in certificate chain":
|
||||||
self.alerts.append({"title": self.template["SSL-03"]["title"].format(host),
|
c["alert_tiggered"] = True
|
||||||
"description": self.template["SSL-03"]["description"].format(host),
|
self.alerts.append({"title": self.template["SSL-03"]["title"].format(host),
|
||||||
"host": host,
|
"description": self.template["SSL-03"]["description"].format(host),
|
||||||
"level": "Moderate",
|
"host": host,
|
||||||
"id": "SSL-03"})
|
"level": "Moderate",
|
||||||
|
"id": "SSL-03"})
|
||||||
if self.iocs_analysis:
|
|
||||||
for cert in self.ssl:
|
if self.iocs_analysis:
|
||||||
# Check if the domain in the certificate haven't been blacklisted
|
for cert in self.ssl:
|
||||||
# This check can be good if the domain has already been cached by
|
# Check if the domain in the certificate haven't been blacklisted
|
||||||
# the device so it wont appear in self.dns.
|
# This check can be good if the domain has already been cached by
|
||||||
|
# the device so it wont appear in self.dns.
|
||||||
if any([cert["cn"].endswith(r["domain"]) for r in self.dns]):
|
|
||||||
continue
|
if any(cert["cn"].endswith(r["domain"]) for r in self.dns):
|
||||||
|
continue
|
||||||
for domain in self.bl_domains:
|
|
||||||
if domain[1] != "tracker":
|
for domain in self.bl_domains:
|
||||||
if cert["cn"].endswith(domain[0]):
|
if domain[1] != "tracker":
|
||||||
self.alerts.append({"title": self.template["SSL-04"]["title"].format(domain[0], domain[1].upper()),
|
if cert["cn"].endswith(domain[0]):
|
||||||
"description": self.template["SSL-04"]["description"].format(domain[0]),
|
self.alerts.append({"title": self.template["SSL-04"]["title"].format(domain[0], domain[1].upper()),
|
||||||
"host": domain[0],
|
"description": self.template["SSL-04"]["description"].format(domain[0]),
|
||||||
"level": "High",
|
"host": domain[0],
|
||||||
"id": "SSL-04"})
|
"level": "High",
|
||||||
|
"id": "SSL-04"})
|
||||||
def alerts_check(self):
|
|
||||||
"""
|
def alerts_check(self):
|
||||||
Leverage an advice to the user based on the trigered hosts
|
"""
|
||||||
:return: nothing - all generated alerts appended to self.alerts
|
Leverage an advice to the user based on the trigered hosts
|
||||||
"""
|
:return: nothing - all generated alerts appended to self.alerts
|
||||||
hosts = {}
|
"""
|
||||||
|
hosts = {}
|
||||||
for alert in [dict(t) for t in {tuple(d.items()) for d in self.alerts}]:
|
|
||||||
if alert["host"] not in hosts:
|
for alert in [dict(t) for t in {tuple(d.items()) for d in self.alerts}]:
|
||||||
hosts[alert["host"]] = 1
|
if alert["host"] not in hosts:
|
||||||
else:
|
hosts[alert["host"]] = 1
|
||||||
hosts[alert["host"]] += 1
|
else:
|
||||||
|
hosts[alert["host"]] += 1
|
||||||
for host, nb in hosts.items():
|
|
||||||
if nb >= get_config(("analysis", "max_alerts")):
|
for host, nb in hosts.items():
|
||||||
self.alerts.append({"title": self.template["ADV-01"]["title"].format(host),
|
if nb >= get_config(("analysis", "max_alerts")):
|
||||||
"description": self.template["ADV-01"]["description"].format(host, nb),
|
self.alerts.append({"title": self.template["ADV-01"]["title"].format(host),
|
||||||
"host": host,
|
"description": self.template["ADV-01"]["description"].format(host, nb),
|
||||||
"level": "Moderate",
|
"host": host,
|
||||||
"id": "ADV-01"})
|
"level": "Moderate",
|
||||||
|
"id": "ADV-01"})
|
||||||
def resolve(self, ip_addr):
|
|
||||||
"""
|
def resolve(self, ip_addr):
|
||||||
A simple method to retreive DNS names from IP addresses
|
"""
|
||||||
in order to replace them in alerts.
|
A simple method to retreive DNS names from IP addresses
|
||||||
|
in order to replace them in alerts.
|
||||||
:return: String - DNS record or IP Address.
|
|
||||||
"""
|
:return: String - DNS record or IP Address.
|
||||||
for record in self.dns:
|
"""
|
||||||
if ip_addr in record["answers"]:
|
for record in self.dns:
|
||||||
return record["domain"]
|
if ip_addr in record["answers"]:
|
||||||
return ip_addr
|
return record["domain"]
|
||||||
|
return ip_addr
|
||||||
def start_zeek(self):
|
|
||||||
"""
|
def start_zeek(self):
|
||||||
Start zeek and check the logs.
|
"""
|
||||||
"""
|
Start zeek and check the logs.
|
||||||
sp.Popen("cd {} && /opt/zeek/bin/zeek -Cr capture.pcap protocols/ssl/validate-certs".format(
|
"""
|
||||||
self.working_dir), shell=True).wait()
|
safe_command.run(sp.Popen, "cd {} && /opt/zeek/bin/zeek -Cr capture.pcap protocols/ssl/validate-certs".format(
|
||||||
sp.Popen("cd {} && mv *.log assets/".format(self.working_dir),
|
self.working_dir), shell=False).wait()
|
||||||
shell=True).wait()
|
safe_command.run(sp.Popen, "cd {} && mv *.log assets/".format(self.working_dir),
|
||||||
self.fill_dns(self.working_dir + "/assets/")
|
shell=False).wait()
|
||||||
self.netflow_check(self.working_dir + "/assets/")
|
self.fill_dns(self.working_dir + "/assets/")
|
||||||
self.ssl_check(self.working_dir + "/assets/")
|
self.netflow_check(self.working_dir + "/assets/")
|
||||||
self.http_check(self.working_dir + "/assets/")
|
self.ssl_check(self.working_dir + "/assets/")
|
||||||
self.files_check(self.working_dir + "/assets/")
|
self.http_check(self.working_dir + "/assets/")
|
||||||
self.alerts_check()
|
self.files_check(self.working_dir + "/assets/")
|
||||||
|
self.alerts_check()
|
||||||
def retrieve_alerts(self):
|
|
||||||
"""
|
def retrieve_alerts(self):
|
||||||
Retrieve alerts.
|
"""
|
||||||
:return: list - a list of alerts wihout duplicates.
|
Retrieve alerts.
|
||||||
"""
|
:return: list - a list of alerts wihout duplicates.
|
||||||
return [dict(t) for t in {tuple(d.items()) for d in self.alerts}]
|
"""
|
||||||
|
return [dict(t) for t in {tuple(d.items()) for d in self.alerts}]
|
||||||
def retrieve_whitelist(self):
|
|
||||||
"""
|
def retrieve_whitelist(self):
|
||||||
Retrieve whitelisted elements.
|
"""
|
||||||
:return: list - a list of whitelisted elements wihout duplicates.
|
Retrieve whitelisted elements.
|
||||||
"""
|
:return: list - a list of whitelisted elements wihout duplicates.
|
||||||
return [dict(t) for t in {tuple(d.items()) for d in self.whitelist}]
|
"""
|
||||||
|
return [dict(t) for t in {tuple(d.items()) for d in self.whitelist}]
|
||||||
def retrieve_conns(self):
|
|
||||||
"""
|
def retrieve_conns(self):
|
||||||
Retrieve not whitelisted elements.
|
"""
|
||||||
:return: list - a list of non-whitelisted elements wihout duplicates.
|
Retrieve not whitelisted elements.
|
||||||
"""
|
:return: list - a list of non-whitelisted elements wihout duplicates.
|
||||||
return self.conns
|
"""
|
||||||
|
return self.conns
|
||||||
|
24008
app/backend/package-lock.json
generated
Before Width: | Height: | Size: 674 KiB After Width: | Height: | Size: 805 KiB |
Before Width: | Height: | Size: 660 KiB After Width: | Height: | Size: 784 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 315 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 133 KiB |
24810
app/frontend/package-lock.json
generated
Before Width: | Height: | Size: 674 KiB After Width: | Height: | Size: 805 KiB |
Before Width: | Height: | Size: 660 KiB After Width: | Height: | Size: 784 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 28 KiB |
@ -1 +1,4 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="112" height="195" fill="none" viewBox="0 0 112 195"><line x1="3.5" x2="3.5" y1="3.5" y2="191.5" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="7"/><rect width="105" height="195" x="7" fill="#F7F8F9"/></svg>
|
<svg width="112" height="195" viewBox="0 0 112 195" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<line x1="3.5" y1="3.5" x2="3.50001" y2="191.5" stroke="black" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<rect x="7" width="105" height="195" fill="#F7F8F9"/>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 281 B After Width: | Height: | Size: 294 B |
@ -1 +1,16 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="loader-1" width="40" height="40" x="0" y="0" enable-background="new 0 0 40 40" version="1.1" viewBox="0 0 40 40" xml:space="preserve"><path fill="#000" d="M20.201,5.169c-8.254,0-14.946,6.692-14.946,14.946c0,8.255,6.692,14.946,14.946,14.946 s14.946-6.691,14.946-14.946C35.146,11.861,28.455,5.169,20.201,5.169z M20.201,31.749c-6.425,0-11.634-5.208-11.634-11.634 c0-6.425,5.209-11.634,11.634-11.634c6.425,0,11.633,5.209,11.633,11.634C31.834,26.541,26.626,31.749,20.201,31.749z" opacity=".2"/><path fill="#f7f8f9" d="M26.013,10.047l1.654-2.866c-2.198-1.272-4.743-2.012-7.466-2.012h0v3.312h0 C22.32,8.481,24.301,9.057,26.013,10.047z"><animateTransform attributeName="transform" attributeType="xml" dur="0.5s" from="0 20 20" repeatCount="indefinite" to="360 20 20" type="rotate"/></path></svg>
|
<svg version="1.1" id="loader-1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="40px" height="40px" viewBox="0 0 40 40" enable-background="new 0 0 40 40" xml:space="preserve">
|
||||||
|
<path opacity="0.2" fill="#000" d="M20.201,5.169c-8.254,0-14.946,6.692-14.946,14.946c0,8.255,6.692,14.946,14.946,14.946
|
||||||
|
s14.946-6.691,14.946-14.946C35.146,11.861,28.455,5.169,20.201,5.169z M20.201,31.749c-6.425,0-11.634-5.208-11.634-11.634
|
||||||
|
c0-6.425,5.209-11.634,11.634-11.634c6.425,0,11.633,5.209,11.633,11.634C31.834,26.541,26.626,31.749,20.201,31.749z"/>
|
||||||
|
<path fill="#f7f8f9" d="M26.013,10.047l1.654-2.866c-2.198-1.272-4.743-2.012-7.466-2.012h0v3.312h0
|
||||||
|
C22.32,8.481,24.301,9.057,26.013,10.047z">
|
||||||
|
<animateTransform attributeType="xml"
|
||||||
|
attributeName="transform"
|
||||||
|
type="rotate"
|
||||||
|
from="0 20 20"
|
||||||
|
to="360 20 20"
|
||||||
|
dur="0.5s"
|
||||||
|
repeatCount="indefinite"/>
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 874 B After Width: | Height: | Size: 970 B |
@ -1 +1,5 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="106" height="106" fill="none" viewBox="0 0 106 106"><circle cx="53" cy="53" r="53" fill="#40D8A1"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="10" d="M29 52.5L47.5 70.5"/><line x1="79" x2="48.071" y1="40.071" y2="71" stroke="#fff" stroke-linecap="round" stroke-width="10"/></svg>
|
<svg width="106" height="106" viewBox="0 0 106 106" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="53" cy="53" r="53" fill="#40D8A1"/>
|
||||||
|
<path d="M29 52.5L47.5 70.5" stroke="white" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<line x1="79" y1="40.0711" x2="48.0711" y2="71" stroke="white" stroke-width="10" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 363 B After Width: | Height: | Size: 377 B |
@ -1 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="548" height="199" fill="none" viewBox="0 0 548 199"><rect width="142" height="145" x="403" y="27" fill="#fff" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="6" rx="8"/><path fill="#000" d="M0 30C0 13.4315 13.4315 0 30 0H428C432.418 0 436 3.58172 436 8V191C436 195.418 432.418 199 428 199H30C13.4315 199 0 185.569 0 169V30Z"/><rect width="26" height="26" x="477" y="55" fill="#fff" stroke="#000" stroke-width="6"/><rect width="26" height="26" x="477" y="117" fill="#fff" stroke="#000" stroke-width="6"/></svg>
|
<svg width="548" height="199" viewBox="0 0 548 199" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="403" y="27" width="142" height="145" rx="8" fill="white" stroke="black" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M0 30C0 13.4315 13.4315 0 30 0H428C432.418 0 436 3.58172 436 8V191C436 195.418 432.418 199 428 199H30C13.4315 199 0 185.569 0 169V30Z" fill="black"/>
|
||||||
|
<rect x="477" y="55" width="26" height="26" fill="white" stroke="black" stroke-width="6"/>
|
||||||
|
<rect x="477" y="117" width="26" height="26" fill="white" stroke="black" stroke-width="6"/>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 583 B After Width: | Height: | Size: 602 B |
@ -1 +1,11 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin:auto;background:0 0;display:block;shape-rendering:auto" width="200" height="200" preserveAspectRatio="xMidYMid" viewBox="0 0 100 100"><circle cx="50" cy="50" r="0" fill="none" stroke="#f3f3f3" stroke-width="2"><animate attributeName="r" begin="-0.7462686567164178s" calcMode="spline" dur="1.4925373134328357s" keySplines="0 0.2 0.8 1" keyTimes="0;1" repeatCount="indefinite" values="0;30"/><animate attributeName="opacity" begin="-0.7462686567164178s" calcMode="spline" dur="1.4925373134328357s" keySplines="0.2 0 0.8 1" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></circle><circle cx="50" cy="50" r="0" fill="none" stroke="#d8dddf" stroke-width="2"><animate attributeName="r" calcMode="spline" dur="1.4925373134328357s" keySplines="0 0.2 0.8 1" keyTimes="0;1" repeatCount="indefinite" values="0;30"/><animate attributeName="opacity" calcMode="spline" dur="1.4925373134328357s" keySplines="0.2 0 0.8 1" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></circle></svg>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
||||||
|
<circle cx="50" cy="50" r="0" fill="none" stroke="#f3f3f3" stroke-width="2">
|
||||||
|
<animate attributeName="r" repeatCount="indefinite" dur="1.4925373134328357s" values="0;30" keyTimes="0;1" keySplines="0 0.2 0.8 1" calcMode="spline" begin="-0.7462686567164178s"></animate>
|
||||||
|
<animate attributeName="opacity" repeatCount="indefinite" dur="1.4925373134328357s" values="1;0" keyTimes="0;1" keySplines="0.2 0 0.8 1" calcMode="spline" begin="-0.7462686567164178s"></animate>
|
||||||
|
</circle>
|
||||||
|
<circle cx="50" cy="50" r="0" fill="none" stroke="#d8dddf" stroke-width="2">
|
||||||
|
<animate attributeName="r" repeatCount="indefinite" dur="1.4925373134328357s" values="0;30" keyTimes="0;1" keySplines="0 0.2 0.8 1" calcMode="spline"></animate>
|
||||||
|
<animate attributeName="opacity" repeatCount="indefinite" dur="1.4925373134328357s" values="1;0" keyTimes="0;1" keySplines="0.2 0 0.8 1" calcMode="spline"></animate>
|
||||||
|
</circle>
|
||||||
|
<!-- [ldio] generated by https://loading.io/ --></svg>
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 540 KiB After Width: | Height: | Size: 775 KiB |
Before Width: | Height: | Size: 324 KiB After Width: | Height: | Size: 405 KiB |
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 315 KiB |
@ -1,17 +1,20 @@
|
|||||||
pymisp==2.4.165.1
|
pymisp==2.4.165.1
|
||||||
sqlalchemy==1.4.48
|
sqlalchemy==1.4.48
|
||||||
ipwhois==1.2.0
|
ipwhois==1.2.0
|
||||||
netaddr==0.8.0
|
netaddr==0.8.0
|
||||||
flask==2.2.5
|
flask==1.1.2
|
||||||
flask_httpauth==4.8.0
|
flask_httpauth==4.8.0
|
||||||
pyjwt==2.4.0
|
pyjwt==1.7.1
|
||||||
psutil==5.8.0
|
psutil==5.8.0
|
||||||
pydig==0.4.0
|
pydig==0.4.0
|
||||||
pyudev==0.24.0
|
pyudev==0.24.0
|
||||||
pyyaml==5.3.1
|
pyyaml==5.3.1
|
||||||
wifi==0.3.8
|
wifi==0.3.8
|
||||||
qrcode==7.3.1
|
qrcode==7.3.1
|
||||||
netifaces==0.11.0
|
netifaces==0.11.0
|
||||||
weasyprint==59.0
|
weasyprint==59.0
|
||||||
python-whois==0.8.0
|
python-whois==0.8.0
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
|
security==1.2.1 \
|
||||||
|
--hash=sha256:4ca5f8cfc6b836e2192a84bb5a28b72c17f3cd1abbfe3281f917394c6e6c9238
|
||||||
|
--hash=sha256:0a9dc7b457330e6d0f92bdae3603fecb85394beefad0fd3b5058758a58781ded
|
||||||
|