Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2e9f86d333 | ||
|
74770b75e7 | ||
|
a1c0f12f21 | ||
|
2c9c4096ee | ||
|
11781dd0a0 | ||
|
08a4f26de4 |
@ -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"
|
16
README.md
@ -8,11 +8,11 @@ TinyCheck allows you to easily capture network communications from a smartphone
|
||||
|
||||
The idea of TinyCheck emerged in a meeting about stalkerware with a [French women's shelter](https://www.centre-hubertine-auclert.fr). During this meeting we talked about how to easily detect [stalkerware](https://stopstalkerware.org/) without installing very technical apps nor doing forensic analysis on the victim's smartphone. The initial concept was to develop a tiny kiosk device based on Raspberry Pi which can be used by non-tech people to test their smartphones against malicious communications issued by stalkerware or any spyware.
|
||||
|
||||
Of course, TinyCheck can also be used to spot any malicious communications from cybercrime to state-sponsored implants. It allows the end-user to push their own extended Indicators of Compromise via a backend in order to detect some ghosts over the wire.
|
||||
Of course, TinyCheck can also be used to spot any malicious communications from cybercrime to state-sponsored implants. It allows the end-user to push his own extended Indicators of Compromise via a backend in order to detect some ghosts over the wire.
|
||||
|
||||
<p align="center"><strong>If you need more documentation on how to install it, use it and the internals, don't hesitate to take a look at the <a href="https://github.com/KasperskyLab/TinyCheck/wiki">TinyCheck Wiki</a>.</strong></p>
|
||||
|
||||
<p align="center">If you have any question about the project, want to contribute or just send your feedback, <br />don't hesitate to contact us at tinycheck[@]kaspersky[.]com.</p>
|
||||
<p align="center">If you have any question about the projet, want to contribute or just send your feedback, <br />don't hesitate to contact us at tinycheck[@]kaspersky[.]com.</p>
|
||||
|
||||
### Use cases
|
||||
|
||||
@ -24,17 +24,17 @@ TinyCheck can be used in several ways by individuals and entities:
|
||||
|
||||
### Installation
|
||||
|
||||
Please check the few steps in the [Wiki's Installation Page](https://github.com/KasperskyLab/TinyCheck/wiki/TinyCheck-installation).
|
||||
Please check the few steps in the [Wiki's Installation Page](https://github.com/KasperskyLab/TinyCheck/wiki/TinyCheck-installation).
|
||||
|
||||
### Meet the frontend
|
||||
|
||||
The frontend - which can be accessed from `http://tinycheck.local` is a kind of tunnel which help the user throughout the process of network capture and reporting. It allows the user to setup a Wi-Fi connection to an existing Wi-Fi network, create an ephemeral Wi-Fi network, capture the communications and show a report to the user... in less than one minute, 5 clicks and without any technical knowledge.
|
||||
The frontend - which can be accessed from `http://tinycheck.local` is a kind of tunnel which help the user throughout the process of network capture and reporting. It allows the user to setup a Wi-Fi connection to an existing Wi-Fi network, create an ephemeral Wi-Fi network, capture the communications and show a report to the user... in less than one minute, 5 clicks and without any technical knowledge.
|
||||
|
||||
![Frontend](/assets/frontend.png)
|
||||
|
||||
### Meet the backend
|
||||
|
||||
Once installed, you can connect yourself to the TinyCheck backend by browsing the URL `https://tinycheck.local` and accepting the SSL self-signed certificate.
|
||||
Once installed, you can connect yourself to the TinyCheck backend by browsing the URL `https://tinycheck.local` and accepting the SSL self-signed certificate.
|
||||
|
||||
![Backend](/assets/backend.png)
|
||||
|
||||
@ -42,11 +42,9 @@ The backend allows you to edit the configuration of TinyCheck, add extended IOCs
|
||||
|
||||
### Special thanks
|
||||
|
||||
**Felix Aime**, for his idea and passion while developing and testing this project. Felix is a main contributor and we really appreciate his work on TinyCheck.
|
||||
|
||||
**People who provided some IOCs**
|
||||
**Guys who provided some IOCs**
|
||||
- [Cian Heasley](https://twitter.com/nscrutables) for his android stalkerwares IOC repo, available here: https://github.com/diskurse/android-stalkerware
|
||||
- [Echap](https://github.com/AssoEchap) for their stalkerwares awesome IOCs repo, available here: https://github.com/AssoEchap/stalkerware-indicators
|
||||
- [Te-k](https://twitter.com/tenacioustek) for his stalkerwares awesome IOCs repo, available here: https://github.com/Te-k/stalkerware-indicators
|
||||
- [Emilien](https://twitter.com/__Emilien__) for his Stratum rules, available here: https://github.com/kwouffe/cryptonote-hunt
|
||||
- [Costin Raiu](https://twitter.com/craiu) for his geo-tracker domains, available here: https://github.com/craiu/mobiletrackers/blob/master/list.txt
|
||||
|
||||
|
@ -16,82 +16,60 @@ import os
|
||||
containing a capture.pcap file.
|
||||
"""
|
||||
|
||||
|
||||
def analyze(capture_directory,frontend=False):
|
||||
if os.path.isdir(capture_directory):
|
||||
|
||||
manager = Manager()
|
||||
alerts = manager.dict()
|
||||
|
||||
def zeekengine(alerts):
|
||||
zeek = ZeekEngine(capture_directory)
|
||||
zeek.start_zeek()
|
||||
alerts["zeek"] = zeek.retrieve_alerts()
|
||||
|
||||
if not os.path.isdir(os.path.join(capture_directory, "assets")):
|
||||
os.mkdir(os.path.join(capture_directory, "assets"))
|
||||
# whitelist.json writing.
|
||||
with open(os.path.join(capture_directory, "assets/whitelist.json"), "w") as f:
|
||||
f.write(json.dumps(zeek.retrieve_whitelist(),
|
||||
indent=4, separators=(',', ': ')))
|
||||
|
||||
# conns.json writing.
|
||||
with open(os.path.join(capture_directory, "assets/conns.json"), "w") as f:
|
||||
f.write(json.dumps(zeek.retrieve_conns(),
|
||||
indent=4, separators=(',', ': ')))
|
||||
|
||||
def snortengine(alerts):
|
||||
suricata = SuricataEngine(capture_directory)
|
||||
suricata.start_suricata()
|
||||
alerts["suricata"] = suricata.get_alerts()
|
||||
|
||||
# Start the engines.
|
||||
p1 = Process(target=zeekengine, args=(alerts,))
|
||||
p2 = Process(target=snortengine, args=(alerts,))
|
||||
p1.start()
|
||||
p2.start()
|
||||
|
||||
# Wait to their end.
|
||||
p1.join()
|
||||
p2.join()
|
||||
|
||||
# Some formating and alerts.json writing.
|
||||
with open(os.path.join(capture_directory, "assets/alerts.json"), "w") as f:
|
||||
report = {"high": [], "moderate": [], "low": []}
|
||||
for alert in (alerts["zeek"] + alerts["suricata"]):
|
||||
if alert["level"] == "High":
|
||||
report["high"].append(alert)
|
||||
if alert["level"] == "Moderate":
|
||||
report["moderate"].append(alert)
|
||||
if alert["level"] == "Low":
|
||||
report["low"].append(alert)
|
||||
f.write(json.dumps(report, indent=4, separators=(',', ': ')))
|
||||
|
||||
# Generate the report
|
||||
report = Report(capture_directory,frontend)
|
||||
report.generate_report()
|
||||
|
||||
else:
|
||||
print("The directory doesn't exist.")
|
||||
|
||||
|
||||
def usage():
|
||||
print("""Usage: python analysis.py [capture_directory]
|
||||
where [capture_directory] is a directory containing a capture.pcap file
|
||||
analysis.py -f starts the analysis in frontend mode intended to be called by the TinyCheck frontend.""")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) == 2: #called manually without frontend
|
||||
analyze(sys.argv[1], False)
|
||||
elif len(sys.argv) == 3:
|
||||
if(sys.argv[1]) == "-f": #frontend mode
|
||||
analyze(sys.argv[2], True)
|
||||
if len(sys.argv) == 2:
|
||||
capture_directory = sys.argv[1]
|
||||
if os.path.isdir(capture_directory):
|
||||
|
||||
manager = Manager()
|
||||
alerts = manager.dict()
|
||||
|
||||
def zeekengine(alerts):
|
||||
zeek = ZeekEngine(capture_directory)
|
||||
zeek.start_zeek()
|
||||
alerts["zeek"] = zeek.retrieve_alerts()
|
||||
|
||||
# whitelist.json writing.
|
||||
with open(os.path.join(capture_directory, "assets/whitelist.json"), "w") as f:
|
||||
f.write(json.dumps(zeek.retrieve_whitelist(),
|
||||
indent=4, separators=(',', ': ')))
|
||||
|
||||
# conns.json writing.
|
||||
with open(os.path.join(capture_directory, "assets/conns.json"), "w") as f:
|
||||
f.write(json.dumps(zeek.retrieve_conns(),
|
||||
indent=4, separators=(',', ': ')))
|
||||
|
||||
def snortengine(alerts):
|
||||
suricata = SuricataEngine(capture_directory)
|
||||
suricata.start_suricata()
|
||||
alerts["suricata"] = suricata.get_alerts()
|
||||
|
||||
# Start the engines.
|
||||
p1 = Process(target=zeekengine, args=(alerts,))
|
||||
p2 = Process(target=snortengine, args=(alerts,))
|
||||
p1.start()
|
||||
p2.start()
|
||||
|
||||
# Wait to their end.
|
||||
p1.join()
|
||||
p2.join()
|
||||
|
||||
# Some formating and alerts.json writing.
|
||||
with open(os.path.join(capture_directory, "assets/alerts.json"), "w") as f:
|
||||
report = {"high": [], "moderate": [], "low": []}
|
||||
for alert in (alerts["zeek"] + alerts["suricata"]):
|
||||
if alert["level"] == "High":
|
||||
report["high"].append(alert)
|
||||
if alert["level"] == "Moderate":
|
||||
report["moderate"].append(alert)
|
||||
if alert["level"] == "Low":
|
||||
report["low"].append(alert)
|
||||
f.write(json.dumps(report, indent=4, separators=(',', ': ')))
|
||||
|
||||
# Generate the report
|
||||
report = Report(capture_directory)
|
||||
report.generate_report()
|
||||
else:
|
||||
usage()
|
||||
|
||||
print("The directory doesn't exist.")
|
||||
else:
|
||||
usage()
|
||||
|
||||
|
||||
|
||||
print("Please specify a capture directory in argument.")
|
||||
|
@ -13,7 +13,7 @@ from utils import get_config
|
||||
|
||||
class Report(object):
|
||||
|
||||
def __init__(self, capture_directory, frontend):
|
||||
def __init__(self, capture_directory):
|
||||
self.capture_directory = capture_directory
|
||||
self.alerts = self.read_json(os.path.join(
|
||||
capture_directory, "assets/alerts.json"))
|
||||
@ -21,13 +21,10 @@ class Report(object):
|
||||
capture_directory, "assets/whitelist.json"))
|
||||
self.conns = self.read_json(os.path.join(
|
||||
capture_directory, "assets/conns.json"))
|
||||
self.device = None
|
||||
self.capinfos = None
|
||||
if frontend:
|
||||
self.device = self.read_json(os.path.join(
|
||||
capture_directory, "assets/device.json"))
|
||||
self.capinfos = self.read_json(os.path.join(
|
||||
capture_directory, "assets/capinfos.json"))
|
||||
self.device = self.read_json(os.path.join(
|
||||
capture_directory, "assets/device.json"))
|
||||
self.capinfos = self.read_json(os.path.join(
|
||||
capture_directory, "assets/capinfos.json"))
|
||||
try:
|
||||
with open(os.path.join(self.capture_directory, "capture.pcap"), "rb") as f:
|
||||
self.capture_sha1 = hashlib.sha1(f.read()).hexdigest()
|
||||
@ -207,18 +204,16 @@ class Report(object):
|
||||
"""
|
||||
header = "<div class=\"header\">"
|
||||
header += "<div class=\"logo\"></div>"
|
||||
if self.device is not None:
|
||||
header += "<p><br /><strong>{}: {}</strong><br />".format(self.template["device_name"],
|
||||
header += "<p><br /><strong>{}: {}</strong><br />".format(self.template["device_name"],
|
||||
self.device["name"])
|
||||
header += "{}: {}<br />".format(self.template["device_mac"],
|
||||
header += "{}: {}<br />".format(self.template["device_mac"],
|
||||
self.device["mac_address"])
|
||||
header += "{} {}<br />".format(self.template["report_generated_on"],
|
||||
datetime.now().strftime("%d/%m/%Y - %H:%M:%S"))
|
||||
if self.capinfos is not None:
|
||||
header += "{}: {}s<br />".format(self.template["capture_duration"],
|
||||
self.capinfos["Capture duration"])
|
||||
header += "{}: {}<br />".format(self.template["packets_number"],
|
||||
self.capinfos["Number of packets"])
|
||||
header += "{}: {}s<br />".format(self.template["capture_duration"],
|
||||
self.capinfos["Capture duration"])
|
||||
header += "{}: {}<br />".format(self.template["packets_number"],
|
||||
self.capinfos["Number of packets"])
|
||||
header += "{}: {}<br />".format(
|
||||
self.template["capture_sha1"], self.capture_sha1)
|
||||
header += "</p>"
|
||||
|
@ -236,7 +236,6 @@ class ZeekEngine(object):
|
||||
pass
|
||||
|
||||
try: # Domain history check.
|
||||
|
||||
whois_record = whois.whois(c["resolution"])
|
||||
creation_date = whois_record.creation_date if type(
|
||||
whois_record.creation_date) is not list else whois_record.creation_date[0]
|
||||
@ -248,7 +247,6 @@ class ZeekEngine(object):
|
||||
"host": c["resolution"],
|
||||
"level": "Moderate",
|
||||
"id": "ACT-02"})
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -269,8 +267,8 @@ class ZeekEngine(object):
|
||||
for record in ParseZeekLogs(os.path.join(dir, "files.log"), output_format="json", safe_headers=False):
|
||||
if record is not None:
|
||||
f = {"filename": record["filename"],
|
||||
"ip_src": record["id.orig_h"],
|
||||
"ip_dst": record["id.resp_h"],
|
||||
"ip_src": record["tx_hosts"],
|
||||
"ip_dst": record["rx_hosts"],
|
||||
"mime_type": record["mime_type"],
|
||||
"sha1": record["sha1"]}
|
||||
if f not in self.files:
|
||||
@ -449,6 +447,7 @@ class ZeekEngine(object):
|
||||
self.working_dir), shell=True).wait()
|
||||
sp.Popen("cd {} && mv *.log assets/".format(self.working_dir),
|
||||
shell=True).wait()
|
||||
|
||||
self.fill_dns(self.working_dir + "/assets/")
|
||||
self.netflow_check(self.working_dir + "/assets/")
|
||||
self.ssl_check(self.working_dir + "/assets/")
|
||||
|
@ -1,128 +0,0 @@
|
||||
{
|
||||
"alerts": {
|
||||
"PROTO-01": {
|
||||
"title": "{} 协议被主机 {} 用于连接外部网络。",
|
||||
"description": "{} 协议一般只用于本地网络。请检查主机 {} ,结合其他警报确认是否存在威胁。"
|
||||
},
|
||||
"PROTO-02": {
|
||||
"title": "存在 {} 个前往 {} 的连接使用了大于或等于 {} 的端口号.",
|
||||
"description": "共有 {} 个目的地主机为 {} 的连接,使用了端口号 {} 。恶意流量时常使用非标准端口号。我们推荐对该主机地址的声誉进行检查,请参考其他警报并在互联网上搜索该地址。"
|
||||
},
|
||||
"PROTO-03": {
|
||||
"title": "前往主机 {} 的HTTP连接",
|
||||
"description": "您的设备向主机 {} 发起了未加密的HTTP连接。虽然此行为本身未必来自恶意软件,但大部分智能手机后台运行的软件不会发起未加密的HTTP连接。请在互联网上搜索该地址以检查该主机的声誉。"
|
||||
},
|
||||
"PROTO-04": {
|
||||
"title": "前往主机 {} 的HTTP连接,使用了非标准端口 ({}).",
|
||||
"description": "您的设备向主机 {} 发起了未加密的HTTP连接,并使用了非标准端口号 {} 。此行为相当异常。请在互联网上搜索该地址以检查该主机的声誉。"
|
||||
},
|
||||
"PROTO-05": {
|
||||
"title": "本次分析期间,主机地址 {} 在DNS解析请求中并未出现过。",
|
||||
"description": "这说明没有任何域名指向地址 {} ,或者被分析的设备缓存了之前的DNS解析结果。如果该地址出现在其他警报当中,请仔细检查。"
|
||||
},
|
||||
"IOC-01": {
|
||||
"title": "存在前往 {} ({}) 的连接,该地址被标记为 {} 。",
|
||||
"description": "主机 {} 由于恶意行为已被加入了黑明单。您的设备很可能已被感染,需要由IT安全人员进行深度分析。"
|
||||
},
|
||||
"IOC-02": {
|
||||
"title": "前往 {} 的连接目的地处于 {} 网段,该网段已被标记为 {} 。",
|
||||
"description": "服务器 {} 所处的网段存在大量已知的恶意活动。虽然该行为本身未必来自恶意软件,您仍然需要检查其他警告是否包含该地址。如果您有所怀疑,请在互联网上搜索该地址,以检查其是否恶意。"
|
||||
},
|
||||
"IOC-03": {
|
||||
"title": "向域名 {} 发起的DNS请求已被标记为 {} 。",
|
||||
"description": "在截获流量中出现的域名 {} 已被明确标记为恶意。您的设备很可能已被感染,需要由IT安全人员进一步进行深度分析。"
|
||||
},
|
||||
"IOC-04": {
|
||||
"title": "向域名 {} 发起的DNS请求已被标记为 {} 。",
|
||||
"description": "在截获流量中出现的域名 {} 已被明确标记为追踪器。您的设备上某个活动的应用正在追踪您的地理位置。"
|
||||
},
|
||||
"IOC-05": {
|
||||
"title": "向 {} 发起的DNS请求是一个免费DNS服务商。",
|
||||
"description": "在截获流量中出现的域名 {} 使用了免费的DNS服务。这类服务经常被网络犯罪份子或国家资助的黑客在行动中使用。后台运行的应用使用这类服务非常可疑,请仔细调查。"
|
||||
},
|
||||
"IOC-06": {
|
||||
"title": "向域名 {} 发起的DNS请求使用了可疑的顶级域名。",
|
||||
"description": "域名 {} 使用了可疑的顶级域名 ({}). 虽然这并不能确认域名被用于恶意用途,但这个不常见的顶级域名经常被网络犯罪份子或国家资助的黑客在行动中使用。请用搜索引擎在互联网上搜索该域名。如果存在其他警报指向该主机,则非常可疑。"
|
||||
},
|
||||
"IOC-07": {
|
||||
"title": "曾被用于 {} 活动的数字证书被用于与 {} 通信.",
|
||||
"description": "该数字证书 ({}) 与地址 {} 关联,已被明确标记为恶意。您的设备已被感染,需要对其进行取证分析。"
|
||||
},
|
||||
"IOC-08": {
|
||||
"title": "向 {} 发起的HTTP请求,已被标记为 {} 。",
|
||||
"description": "在截获流量中出现的域名 {} 已被明确标记为恶意。您的设备很可能已被感染,需要由IT安全人员进一步进行深度分析。"
|
||||
},
|
||||
"IOC-09": {
|
||||
"title": "向 {} 发起的HTTP请求,域名使用了一个免费DNS服务商。",
|
||||
"description": "域名 {} 使用了免费的DNS服务。这类服务经常被网络犯罪份子或国家资助的黑客在行动中使用。后台运行的应用使用这类服务非常可疑,请仔细调查。"
|
||||
},
|
||||
"IOC-10": {
|
||||
"title": "向 {} 发起的HTTP请求使用了可疑的顶级域名。",
|
||||
"description": "域名 {} 使用了可疑的顶级域名 ({}). 虽然这并不能确认域名被用于恶意用途,但这个不常见的顶级域名经常被网络犯罪份子或国家资助的黑客在行动中使用。请用搜索引擎在互联网上搜索该域名。如果存在其他警报指向该主机,则非常可疑。"
|
||||
},
|
||||
"ACT-01": {
|
||||
"title": "域名 {} 使用了可疑的域名解析服务器 ({}).",
|
||||
"description": "域名 {} 使用了可疑的域名解析服务器,该解析器已被明确标记为与恶意活动相关联。网络犯罪份子或国家资助的黑客经常使用此类服务,因为他们允许使用加密货币或匿名支付方式。建议对设备进行取证分析,以便该域名和与其相关的正在运行的应用程序进行深入调查。"
|
||||
},
|
||||
"ACT-02": {
|
||||
"title": "域名 {} 最近刚刚被注册 ({} 天前)。",
|
||||
"description": "域名 {} 非常新。虽然这本身不说明恶意行为,但黑客攻击者经常在每次攻击中使用新的设施,因此会使用刚刚注册的域名."
|
||||
},
|
||||
"SSL-01": {
|
||||
"title": "在非标准端口 ({}) 上发起SSL连接至 {}",
|
||||
"description": "在智能手机上,前往非标准端口的SSL连接非常罕见。虽然这可能来自非常正常的应用程序,但我们推荐检查域名 {} 的声誉。请检查该域名的WHOIS记录,对应的AS(Autonomous System, 自治领),域名注册日期,以及在互联网上搜索该域名。"
|
||||
},
|
||||
"SSL-02": {
|
||||
"title": "前往 {} 的SSL连接使用了免费证书。",
|
||||
"description": "免费SSL证书提供商 (例如 Let's Encrypt) 常被用于与恶意软件和钓鱼网页有关的后端远程控制服务器。我们建议仔细分析该证书对应的主机,检查其域名,注册日期,以及在互联网上的声誉。"
|
||||
},
|
||||
"SSL-03": {
|
||||
"title": "与 {} 关联的自签名证书",
|
||||
"description": "黑客攻击设施经常使用自签名证书。我们建议仔细分析该证书对应的主机 {} ,检查其域名 (如有) WHOIS记录,注册日期,以及在互联网上的声誉。"
|
||||
},
|
||||
"SSL-04": {
|
||||
"title": "数字证书包含域名 {} ,已被归类为 {}",
|
||||
"description": "一份被交换的证书中包括了域名 {} 。该域名已被明确标记为恶意。您的设备已确定被感染,需要交给专业团队进行进一步调查。"
|
||||
},
|
||||
"ADV-01": {
|
||||
"title": "请检查 {} 的其他警报",
|
||||
"description": "请检查主机 {} 的声誉,该地址可能存在恶意活动,在本次分析中已经触发了 {} 个警报。"
|
||||
},
|
||||
"SNORT-01": {
|
||||
"title": "Suricata 入侵检测规则被触发: {}",
|
||||
"description": "网络入侵检测规则被触发。您的设备很可能已经被入侵,或存在其他可疑行为。"
|
||||
}
|
||||
},
|
||||
"report": {
|
||||
"numbers": [
|
||||
"一",
|
||||
"二",
|
||||
"三",
|
||||
"四",
|
||||
"五",
|
||||
"六",
|
||||
"七",
|
||||
"八",
|
||||
"九"
|
||||
],
|
||||
"suspect_title": "可疑通信",
|
||||
"uncat_title": "未分类通信",
|
||||
"whitelist_title": "白名单通信",
|
||||
"protocol": "协议",
|
||||
"domain": "域名",
|
||||
"dst_ip": "目的地IP地址",
|
||||
"dst_port": "目的地端口号",
|
||||
"device_name": "设备名称",
|
||||
"device_mac": "设备 MAC 地址",
|
||||
"report_generated_on": "报告生成于",
|
||||
"capture_duration": "流量捕获时长",
|
||||
"packets_number": "数据包数量",
|
||||
"capture_sha1": "捕获的 SHA1 哈希",
|
||||
"report_for_the_capture": "捕获报告",
|
||||
"report_footer": "此报告由 Tinycheck 设备自动生成。如有任何疑问,错误报告或意见反馈,请联系 tinycheck@kaspersky.com 。",
|
||||
"high_msg": "您的设备很可能已被入侵,存在 {} 条高级警报.",
|
||||
"moderate_msg": "您有 {} 条中级警报,您的设备存在被入侵的可能性。请仔细检查这些警报。",
|
||||
"low_msg": "您只有 {} 条低级警报。请检查。",
|
||||
"none_msg": "情况正常,没有警报。如果存在未分类通信,请检查。"
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
{
|
||||
"alerts": {
|
||||
"PROTO-01": {
|
||||
"title": "{} komunikácia smeruje mimo lokálnej siete do {}.",
|
||||
"description": "Protokol {} sa bežne používa v interných sieťach. Skontrolujte, či hostiteľ {} nevygeneroval iné upozornenia, ktoré môžu naznačovať škodlivé správanie."
|
||||
},
|
||||
"PROTO-02": {
|
||||
"title": "{} pripojenie k {} cez port väčší alebo rovný {}.",
|
||||
"description": "Zistili sa {} pripojenia k {} na porte {}. Použitie neštandardného portu je niekedy vodítkom pre škodlivú aktivitu. Odporúčame vám skontrolovať reputáciu tohto hostiteľa pomocou vyhľadania na internete."
|
||||
},
|
||||
"PROTO-03": {
|
||||
"title": "Komunikácia HTTP bola uskutočnená s hostiteľom {}",
|
||||
"description": "Vaše zariadenie komunikovalo s hostiteľom {} pomocou nešifrovaného protokolu HTTP. Aj keď toto správanie nie je samo o sebe škodlivé, nie je bežné, aby aplikácie smartfónu spustené na pozadí vytvárali komunikáciu HTTP. Skontrolujte reputáciu hostiteľa pomocou vyhľadania na internete."
|
||||
},
|
||||
"PROTO-04": {
|
||||
"title": "S hostiteľom {} sa komunikovalo cez HTTP cez neštandardný port ({}).",
|
||||
"description": "Vaše zariadenie komunikovalo s hostiteľom {} cez port {} pomocou nešifrovaného protokolu HTTP. Toto správanie je dosť nezvyčajné. Overte si reputáciu hostiteľa pomocou vyhľadania na internete."
|
||||
},
|
||||
"PROTO-05": {
|
||||
"title": "Server {} nebol vyriešený v požiadavke DNS počas relácie.",
|
||||
"description": "To znamená, že server {} pravdepodobne nie je riešený žiadnym názvom domény alebo toto rozlíšenie už zariadenie uložilo do vyrovnávacej pamäte. Ak sa hostiteľ zobrazuje v iných upozorneniach, skontrolujte ho."
|
||||
},
|
||||
"IOC-01": {
|
||||
"title": "Bolo vytvorené pripojenie k {} ({}), ktoré je označené ako {}.",
|
||||
"description": "Hostiteľ {} bol explicitne zaradený na čiernu listinu kvôli škodlivej aktivite. Vaše zariadenie je pravdepodobne napadnuté a vyžaduje ďalšie preskúmanie odborníkmi na bezpečnosť IT."
|
||||
},
|
||||
"IOC-02": {
|
||||
"title": "Komunikácia s {} v rámci CIDR {}, ktorý je označený ako {}.",
|
||||
"description": "Server {} je hosťovaný v sieti známej škodlivou aktivitou. Aj keď toto správanie nie je vo svojej podstate škodlivé, mali by ste skontrolovať, či sa o tomto hostiteľovi nezmieňujú iné upozornenia. Ak máte pochybnosti, vyhľadajte tohto hostiteľa na internete, aby ste zistili, či je legitímny."
|
||||
},
|
||||
"IOC-03": {
|
||||
"title": "DNS požiadavka bola vytvorená pre {}, ktorý je označený ako {}.",
|
||||
"description": "Názov domény {} v zázname bol výslovne označený ako škodlivý. To znamená, že vaše zariadenie je pravdepodobne napadnuté a je potrebné ho ďalej preskúmať."
|
||||
},
|
||||
"IOC-04": {
|
||||
"title": "DNS požiadavka bola vytvorená pre {}, ktorý je označený ako {}.",
|
||||
"description": "Názov domény {} zobrazený v zázname bol explicitne označený ako tracker. Znamená to, že jedna z aktívnych aplikácií geograficky sleduje Vašu polohu."
|
||||
},
|
||||
"IOC-05": {
|
||||
"title": "DNS požiadavka bola vytvorená pre doménu {} pomocou bezplatnej služby DNS.",
|
||||
"description": "Názov domény {} používa bezplatnú službu DNS. Služby tohto typu často využívajú pri svojich operáciách počítačoví zločinci alebo útočníci podporovaní vládou. Je veľmi podozrivé, že aplikácia spustená na pozadí používa takúto službu. Preskúmajte to podrobnejšie."
|
||||
},
|
||||
"IOC-06": {
|
||||
"title": "Bola vykonaná DNS požiadavka pre doménu {} obsahujúca podozrivú TLD.",
|
||||
"description": "Názov domény {} používa podozrivú doménu najvyššej úrovne ({}). Aj keď nie je sama o sebe škodlivá, túto negenerickú TLD bežne používajú kyberzločinci a vládami sponzorované operácie. Overte túto doménu pomocou vyhľadania na internete. Ak je tento hostiteľ spomenutý v iných upozorneniach, považujte to za veľmi podozrivé."
|
||||
},
|
||||
"IOC-07": {
|
||||
"title": "Certifikát priradený k {} aktivitám sa našiel v komunikácii s {}.",
|
||||
"description": "Certifikát ({}) priradený k {} bol explicitne označený ako škodlivý. Znamená to, že vaše zariadenie je pravdepodobne napadnuté a potrebuje forenznú analýzu."
|
||||
},
|
||||
"IOC-08": {
|
||||
"title": "Na {} bola odoslaná požiadavka HTTP, ktorá je označená ako {}.",
|
||||
"description": "Názov domény {} zobrazený v zázname bol výslovne označený ako škodlivý. To znamená, že Vaše zariadenie je pravdepodobne napadnuté a vyžaduje si dôkladnú analýzu."
|
||||
},
|
||||
"IOC-09": {
|
||||
"title": "Do domény {} bola uskutočnená požiadavka HTTP pomocou bezplatnej služby DNS.",
|
||||
"description": "Názov domény {} používa bezplatnú službu DNS. Služby tohto typu často využívajú pri svojich operáciách počítačoví zločinci alebo útočníci podporovaní vládou. Je veľmi podozrivé, že aplikácia spustená na pozadí používa takúto službu. Preskúmajte to podrobnejšie."
|
||||
},
|
||||
"IOC-10": {
|
||||
"title": "Do domény {}, ktorá obsahuje podozrivú TLD, bola odoslaná požiadavka HTTP.",
|
||||
"description": "Názov domény {} používa podozrivú doménu najvyššej úrovne ({}). Túto negenerickú TLD, hoci sama o sebe nie je škodlivá, bežne používajú počítačoví zločinci a pri operáciách sponzorovaných vládou. Overte túto doménu pomocou vyhľadania na internete. Ak je tento hostiteľ uvedený v iných upozorneniach, považujte to za veľmi podozrivé."
|
||||
},
|
||||
"ACT-01": {
|
||||
"title": "Doména {} používa podozrivý názvový server ({}).",
|
||||
"description": "Názov domény {} používa názvový server, ktorý je explicitne spojený so škodlivou aktivitou. Mnoho kyberzločincov a štátom podporovaných útočníkov používa registrátorov tohto typu, pretože umožňujú použiť kryptomeny a anonymné platby. Odporúča sa ďalej skúmať túto doménu a súvisiacu spustenú aplikáciu pomocou forenznej analýzy telefónu."
|
||||
},
|
||||
"ACT-02": {
|
||||
"title": "Doména {} bola vytvorená nedávno (pred {} dňami).",
|
||||
"description": "Názov domény {} je nový. Aj keď to nie je vo svojej podstate škodlivé, útočníci často vytvoria novú infraštruktúru pre každú kampaň, čo môže viesť k použitiu novo registrovaných doménových mien."
|
||||
},
|
||||
"SSL-01": {
|
||||
"title": "SSL pripojenie na neštandardnom porte ({}) k {}",
|
||||
"description": "Je nezvyčajné, aby smartfóny vytvárali pripojenia SSL cez neštandardný port. Môže to byť legitímne, ale aj tak odporúčame skontrolovať reputáciu {}. Pozrite sa na jeho záznam WHOIS, súvisiaci autonómny systém, dátum jeho vytvorenia a vyhľadajte ho na internete."
|
||||
},
|
||||
"SSL-02": {
|
||||
"title": "SSL pripojenie k {} používa bezplatný certifikát.",
|
||||
"description": "Bezplatné certifikáty – ako napr Let's Encrypt – sú bežne používané príkazovými a riadiacimi servermi spojenými so škodlivými implantátmi a phishingovými webovými stránkami. Odporúčame overiť hostiteľa spojeného s týmto certifikátom. Pozrite sa na názov domény a dátum vytvorenia alebo si overte reputáciu hostiteľa na internete."
|
||||
},
|
||||
"SSL-03": {
|
||||
"title": "Certifikát priradený k {} je podpísaný sám sebou.",
|
||||
"description": "Používanie certifikátov s vlastným podpisom je v infraštruktúre útočníka rozšírené. Odporúčame skontrolovať hostiteľa {} priradeného k tomuto certifikátu. Ak to chcete urobiť, pozrite sa na názov jeho domény (ak ho má), záznam WHOIS, dátum vytvorenia a skontrolujte reputáciu hostiteľa na internete."
|
||||
},
|
||||
"SSL-04": {
|
||||
"title": "Certifikát obsahuje názov domény {}, kategorizovaný ako {}",
|
||||
"description": "Jeden z vymenených certifikátov obsahuje názov domény {}. Tento názov domény bol výslovne klasifikovaný ako škodlivý. Vaše zariadenie je určite napadnuté a malo by byť ďalej zanalyzované profesionálnym tímom."
|
||||
},
|
||||
"ADV-01": {
|
||||
"title": "Skontrolujte upozornenia pre {}",
|
||||
"description": "Skontrolujte prosím reputáciu hostiteľa {}, Zdá sa, že je to škodlivé, pretože to počas relácie vygenerovalo {} upozornení."
|
||||
},
|
||||
"SNORT-01": {
|
||||
"title": "Spustilo sa pravidlo Suricata: {}",
|
||||
"description": "Spustilo sa pravidlo zisťovania siete. Je pravdepodobné, že Vaše zariadenie bolo ohrozené alebo vykazuje podozrivé správanie."
|
||||
}
|
||||
},
|
||||
"report": {
|
||||
"numbers": [
|
||||
"jednu",
|
||||
"dva",
|
||||
"tri",
|
||||
"štyri",
|
||||
"päť",
|
||||
"šesť",
|
||||
"sedem",
|
||||
"osem",
|
||||
"deväť"
|
||||
],
|
||||
"suspect_title": "Podozrivá komunikácia",
|
||||
"uncat_title": "Nekategorizovaná komunikácia",
|
||||
"whitelist_title": "Komunikácia na bielej listine",
|
||||
"protocol": "Protokol",
|
||||
"domain": "Doména",
|
||||
"dst_ip": "Dst IP adresa",
|
||||
"dst_port": "Dst číslo portu",
|
||||
"device_name": "Názov zariadenia",
|
||||
"device_mac": "MAC adresa zariadenia",
|
||||
"report_generated_on": "Report vygenerovaný",
|
||||
"capture_duration": "Trvanie záznamu",
|
||||
"packets_number": "Počet paketov",
|
||||
"capture_sha1": "Záznam SHA1",
|
||||
"report_for_the_capture": "Report pre záznam",
|
||||
"report_footer": "Tento prehľad bol automaticky vygenerovaný zariadením Tinycheck. V prípade akýchkoľvek otázok, hlásení chýb alebo spätnej väzby nás kontaktujte na tinycheck@kaspersky.com.",
|
||||
"high_msg": "Vaše zariadenie je napadnuté a máte {} kritické/ú výstrahy/u.",
|
||||
"moderate_msg": "Máte {} mierne/u výstrahy/u, Vaše zariadenie môže byť napadnuté. Pozrite si ich pozorne.",
|
||||
"low_msg": "Máte iba {} nízke/u výstrahy/u, neváhajte ich skontrolovať.",
|
||||
"none_msg": "Všetko vyzerá dobre, žiadne upozornenia. Neváhajte a skontrolujte nekategorizovanú komunikáciu, ak existuje."
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ import os
|
||||
from functools import reduce
|
||||
|
||||
# I'm not going to use an ORM for that.
|
||||
parent = os.path.split(os.path.dirname(os.path.abspath(sys.argv[0])))[0]
|
||||
parent = "/".join(sys.path[0].split("/")[:-1])
|
||||
conn = sqlite3.connect(os.path.join(parent, "tinycheck.sqlite3"))
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
24008
app/backend/package-lock.json
generated
@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "@kaspersky/tinycheck-backend",
|
||||
"name": "tinycheck-backend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve --copy --port=4201",
|
||||
"build": "vue-cli-service build",
|
||||
|
@ -42,6 +42,9 @@
|
||||
<li class="menu-item">
|
||||
<span @click="$router.push('/iocs/misp')">MISP Instances</span>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<span @click="$router.push('/iocs/opencti')">OpenCTI Instances</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
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 |
@ -40,6 +40,12 @@ const routes = [
|
||||
component: () => import('../views/iocs-misp.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/iocs/opencti',
|
||||
name: 'iocs-opencti',
|
||||
component: () => import('../views/iocs-opencti.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/iocs/search',
|
||||
name: 'iocs-search',
|
||||
|
@ -111,7 +111,6 @@ export default {
|
||||
setTimeout(function (){
|
||||
this.switch_tab('instances')
|
||||
this.mispinst = { name:'', url:'',key:'', ssl:false }
|
||||
this.added = false
|
||||
}.bind(this), 2000);
|
||||
} else {
|
||||
this.error = response.data.message;
|
||||
|
170
app/backend/src/views/iocs-opencti.vue
Normal file
@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<div class="backend-content" id="content">
|
||||
<div class="column col-8 col-xs-12">
|
||||
<h3 class="s-title">Manage OpenCTI instances</h3>
|
||||
<ul class="tab tab-block">
|
||||
<li class="tab-item">
|
||||
<a href="#" v-on:click="switch_tab('addopencti')" v-bind:class="{ active: tabs.addopencti }">Add instance</a>
|
||||
</li>
|
||||
<li class="tab-item">
|
||||
<a href="#" v-on:click="switch_tab('instances')" v-bind:class="{ active: tabs.instances }">Existing instances</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="tabs.addopencti">
|
||||
<div class="misp-form">
|
||||
<label class="misp-label">Instance name</label><span></span>
|
||||
<input class="form-input" type="text" placeholder="CYBERACME OpenCTI" v-model="openctiinst.name" required>
|
||||
<label class="misp-label">Instance URL</label><span></span>
|
||||
<input class="form-input" type="text" placeholder="https://opencti.cyberacme.com" v-model="openctiinst.url" required>
|
||||
<label class="misp-label">Authentication key</label><span></span>
|
||||
<input class="form-input" type="text" placeholder="83114ab2-3570-493b-8caa-14ef1bcf8e9a" v-model="openctiinst.key" required>
|
||||
<label class="misp-label">Verify certificate? </label><span></span>
|
||||
<div style="flex:50%"><label class="form-switch">
|
||||
<input type="checkbox" v-model="openctiinst.ssl">
|
||||
<i class="form-icon"></i>
|
||||
</label></div>
|
||||
</div>
|
||||
<button class="btn-primary btn col-12" v-on:click="add_instance()">Add OpenCTI instance</button>
|
||||
<div class="form-group" v-if="added">
|
||||
<div class="toast toast-success">
|
||||
✓ OpenCTI instance added successfully.
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="error">
|
||||
<div class="toast toast-error">
|
||||
✗ OpenCTI instance not added. {{error}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="tabs.instances">
|
||||
<div v-if="instances.length">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Server</th>
|
||||
<th>Authkey</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="i in instances" v-bind:key="i.id">
|
||||
<td>{{ i.name }}</td>
|
||||
<td>{{ i.url.replace('https://', '') .replace('http://', '') }}</td>
|
||||
<td>{{ i.apikey.slice(0,5) }} [...] {{ i.apikey.slice(35,40) }}</td>
|
||||
<td>
|
||||
<span v-if="i.connected" class="misp-online tooltip" :data-tooltip="i.lastsync">✓ ONLINE</span>
|
||||
<span v-else class="misp-offline tooltip" :data-tooltip="i.lastsync">⚠ OFFLINE</span>
|
||||
</td>
|
||||
<td><button class="btn btn-sm" v-on:click="delete_instance(i)">Delete</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="empty">
|
||||
<div v-if="loading">
|
||||
<p class="empty-title h5">
|
||||
<span class="loading loading-lg"></span>
|
||||
</p>
|
||||
<p class="empty-subtitle">Testing and loading your OpenCTI instances.</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p class="empty-title h5">No OpenCTI instance found.</p>
|
||||
<p class="empty-subtitle">Do not hesitate to add a OpenCTI instance.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'manageopencti',
|
||||
data() {
|
||||
return {
|
||||
error:false,
|
||||
loading:false,
|
||||
added:false,
|
||||
openctiinst:{ name:'', url:'',key:'', ssl:false },
|
||||
instances:[],
|
||||
tabs: { "addopencti" : true, "instances" : false },
|
||||
jwt:""
|
||||
}
|
||||
},
|
||||
props: { },
|
||||
methods: {
|
||||
add_instance: function()
|
||||
{
|
||||
this.added = false;
|
||||
this.error = false;
|
||||
if (this.openctiinst.name && this.openctiinst.url && this.openctiinst.key)
|
||||
{
|
||||
axios.post(`/api/opencti/add`, { data: { instance: this.openctiinst } }, { headers: {'X-Token': this.jwt} }).then(response => {
|
||||
if(response.data.status){
|
||||
this.added = true;
|
||||
} else {
|
||||
this.error = response.data.message;
|
||||
}
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
}
|
||||
},
|
||||
delete_instance(elem)
|
||||
{
|
||||
axios.get(`/api/opencti/delete/${elem.id}`, { timeout: 10000, headers: {'X-Token': this.jwt} })
|
||||
.then(response => {
|
||||
if(response.data.status){
|
||||
this.instances = this.instances.filter(function(el) { return el != elem; });
|
||||
}
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
},
|
||||
get_opencti_instances()
|
||||
{
|
||||
this.loading = true;
|
||||
this.instances = []
|
||||
axios.get(`/api/opencti/get_all`, { timeout: 10000, headers: {'X-Token': this.jwt} })
|
||||
.then(response => {
|
||||
if(response.data.results){
|
||||
this.instances = response.data.results;
|
||||
this.instances.forEach(e => {
|
||||
var lastsync = parseInt((Date.now()/1000 - e.lastsync) / 86400)
|
||||
e.lastsync = (!lastsync)? "Synchronized today" : `Synchronized ${lastsync} day(s) ago`
|
||||
} )
|
||||
}
|
||||
this.loading = false
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
},
|
||||
switch_tab: function(tab) {
|
||||
|
||||
Object.keys(this.tabs).forEach(key => {
|
||||
if( key == tab ){
|
||||
this.tabs[key] = true
|
||||
if (key == "instances") this.get_opencti_instances();
|
||||
} else {
|
||||
this.tabs[key] = false
|
||||
}
|
||||
});
|
||||
},
|
||||
get_jwt(){
|
||||
axios.get(`/api/get-token`, { timeout: 10000 })
|
||||
.then(response => {
|
||||
if(response.data.token){
|
||||
this.jwt = response.data.token
|
||||
}
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
this.get_jwt();
|
||||
}
|
||||
}
|
||||
</script>
|
24810
app/frontend/package-lock.json
generated
@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "@kaspersky/tinycheck-new",
|
||||
"name": "tinycheck-new",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve --copy --port=4202",
|
||||
"build": "vue-cli-service build",
|
||||
|
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 |
@ -1,92 +0,0 @@
|
||||
{
|
||||
"home": {
|
||||
"welcome_msg": "欢迎使用 TinyCheck.",
|
||||
"help_msg": "我们将帮助您检查您的设备。",
|
||||
"start_btn": "开始!"
|
||||
},
|
||||
"analysis": {
|
||||
"question": "您是否希望对被捕获的通信进行分析?",
|
||||
"no_btn": "不,只保存数据",
|
||||
"yes_btn": "好的!",
|
||||
"please_wait_msg": "请耐心等待分析结果...",
|
||||
"some_time_msg": "是的,这需要花点时间..."
|
||||
},
|
||||
"capture": {
|
||||
"intercept_coms_msg": "正在截获通信: ",
|
||||
"stop_btn": "停止捕获"
|
||||
},
|
||||
"generate-ap": {
|
||||
"network_name": "网络名称",
|
||||
"network_password": "网络密码",
|
||||
"tap_msg": "点击白色帧,创建新网络",
|
||||
"generate_ap_msg": "我们刚刚为您创建了新的临时网络。",
|
||||
"error_msg1": "很不幸的是,我们在 <br /> 创建网络AP的过程中遇到了问题。",
|
||||
"error_msg2": "请确认您的设备上有两个Wi-FI网卡, <br /> 并重启设备以重试。",
|
||||
"restart_btn": "重启设备"
|
||||
},
|
||||
"report": {
|
||||
"show_full_report": "显示完整报告",
|
||||
"start_new_capture": "开始新的捕获",
|
||||
"save_capture": "保存捕获",
|
||||
"numbers": [
|
||||
"零",
|
||||
"一",
|
||||
"二",
|
||||
"三",
|
||||
"四",
|
||||
"五",
|
||||
"六",
|
||||
"七",
|
||||
"八",
|
||||
"九",
|
||||
"十",
|
||||
"十一"
|
||||
],
|
||||
"stalkerware_msg": "您的设备已被跟踪软件 <br /> 入侵,请检查完整报告.",
|
||||
"location_msg": "一个应用程序正在将您的<br /> 实时地理位置信息分享给第三方。",
|
||||
"fine_msg": "一切正常,没有警报。",
|
||||
"high_msg": "您有 {nb} 条高级警报,<br /> 看起来您的设备已被入侵。",
|
||||
"moderate_msg": "您有 {nb} 条中级警报, <br /> 您的设备有可能已被入侵。",
|
||||
"low_msg": "您只有 {nb} 条低级警报,<br /> 请检查它们。",
|
||||
"save_report": "保存报告",
|
||||
"report_of": "报告设备名:",
|
||||
"ip_address": "IP 地址:",
|
||||
"mac_address": "MAC 地址:",
|
||||
"pcap_sha1": "SHA1 :",
|
||||
"capture_started": "捕获开始时间:",
|
||||
"capture_ended": "捕获结束时间:",
|
||||
"high": "高",
|
||||
"moderate": "中",
|
||||
"low": "低"
|
||||
},
|
||||
"wifi-select": {
|
||||
"already_connected_question": "您似乎已经连接了一个Wi-Fi网络。<br /> 您希望使用当前的连接吗?",
|
||||
"no_btn": "不,使用其他连接",
|
||||
"yes_btn": "是的,使用此连接。",
|
||||
"wifi_name": "Wi-Fi 名称",
|
||||
"refresh_btn": "刷新网络列表",
|
||||
"not_connected": "您似乎没有连接至互联网。",
|
||||
"please_config": "请配置 Wi-Fi 连接。",
|
||||
"lets_do_btn": "好的,开始配置",
|
||||
"no_internet": "离线使用",
|
||||
"wifi_connected": "Wi-Fi 已连接",
|
||||
"wifi_not_connected": "Wi-Fi 未连接,请重试。",
|
||||
"tap_keyboard": "点击虚拟键盘开始",
|
||||
"wifi_password": "Wi-Fi 密码",
|
||||
"connect_to_it": "连接此网络"
|
||||
},
|
||||
"save-capture": {
|
||||
"please_connect": "请连接U盘以保存捕获。",
|
||||
"we_are_saving": "我们正在保存您的捕获。",
|
||||
"tap_msg": "您可以点击U盘以开始新的捕获。",
|
||||
"capture_download": "即将开始保存捕获数据...",
|
||||
"start_capture_btn": "开始进行新的捕获"
|
||||
},
|
||||
"update": {
|
||||
"tinycheck_needs": "TinyCheck 需要更新到新版本",
|
||||
"please_click": "请点击此按钮进行更新。",
|
||||
"the_process": "此过程需要花几分钟,请耐心等待...",
|
||||
"update_finished": "更新已完成,需要刷新界面...",
|
||||
"update_it": "现在更新"
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
{
|
||||
"home": {
|
||||
"welcome_msg": "Vitajte v TinyCheck.",
|
||||
"help_msg": "Pomôžeme Vám skontrolovať Vaše zariadenie.",
|
||||
"start_btn": "Poďme nato!"
|
||||
},
|
||||
"analysis": {
|
||||
"question": "Chcete analyzovať zachytenú komunikáciu?",
|
||||
"no_btn": "Nie, iba ju uložiť",
|
||||
"yes_btn": "Áno, poďme na to",
|
||||
"please_wait_msg": "Počkajte prosím, prebieha analýza...",
|
||||
"some_time_msg": "Áno, môže to chvíľu trvať..."
|
||||
},
|
||||
"capture": {
|
||||
"intercept_coms_msg": "Zaznamenávanie komunikácie z/zo ",
|
||||
"stop_btn": "Zastaviť záznam"
|
||||
},
|
||||
"generate-ap": {
|
||||
"network_name": "Názov siete",
|
||||
"network_password": "Sieťové heslo",
|
||||
"tap_msg": "Ťuknutím na biely rám vytvoríte novú sieť.",
|
||||
"generate_ap_msg": "Generujeme pre Vás pominuteľnú sieť.",
|
||||
"error_msg1": "Bohužiaľ, máme nejaké problémy <br />počas tvorby AP.",
|
||||
"error_msg2": "Overte si, či máte v zariadení dve rozhrania WiFi<br /> a skúste to znova reštartovať",
|
||||
"restart_btn": "Reštart zariadenia"
|
||||
},
|
||||
"report": {
|
||||
"show_full_report": "Zobraziť celú správu",
|
||||
"start_new_capture": "Spustiť nový záznam",
|
||||
"save_capture": "Uložiť záznam",
|
||||
"numbers": [
|
||||
"nula",
|
||||
"jednu",
|
||||
"dva",
|
||||
"tri",
|
||||
"štyri",
|
||||
"päť",
|
||||
"šesť",
|
||||
"sedem",
|
||||
"osem",
|
||||
"deväť",
|
||||
"desať",
|
||||
"jedenásť"
|
||||
],
|
||||
"stalkerware_msg": "Vaše zariadenie je ohrozené<br /> stalkerwarom. Prosím, skontrolujte správu.",
|
||||
"location_msg": "Aplikácia zdieľa Vašu<br /> súčasnú polohu s treťou stranou.",
|
||||
"fine_msg": "Všetko vyzerá dobre, žiadne výstrahy.",
|
||||
"high_msg": "Máte {nb} kritické/ú výstrahy/u.<br />Zdá sa, že Vaše zariadenie je ohrozené.",
|
||||
"moderate_msg": "Máte {nb} mierne/u výstrahy/u,<br />Vaše zariadenie môže byť ohrozené.",
|
||||
"low_msg": "Máte iba {nb} nízke/u výstrahy/u,<br /> neváhajte ich skontrolovať.",
|
||||
"save_report": "Uložiť report",
|
||||
"report_of": "Report z/zo",
|
||||
"ip_address": "IP adresa:",
|
||||
"mac_address": "MAC adresa:",
|
||||
"pcap_sha1": "SHA1:",
|
||||
"capture_started": "Záznam začal:",
|
||||
"capture_ended": "Záznam skončil:",
|
||||
"high": "vysoký",
|
||||
"moderate": "mierny",
|
||||
"low": "nízky"
|
||||
},
|
||||
"wifi-select": {
|
||||
"already_connected_question": "Zdá sa, že ste už pripojený k sieti.<br />Chcete použiť aktuálne pripojenie?",
|
||||
"no_btn": "Nie, použi inú",
|
||||
"yes_btn": "Áno, použite ju.",
|
||||
"wifi_name": "Názov Wi-Fi",
|
||||
"refresh_btn": "Obnoviť zoznam sieti",
|
||||
"not_connected": "Zdá sa, že nie ste pripojený na internet.",
|
||||
"please_config": "Prosím nakonfigurujte Wi-Fi pripojenie.",
|
||||
"lets_do_btn": "Ok, poďme na to.",
|
||||
"no_internet": "Použiť offline",
|
||||
"wifi_connected": "Wi-Fi pripojená",
|
||||
"wifi_not_connected": "Wi-Fi nie je pripojené. Prosím, zopakujte to znova.",
|
||||
"tap_keyboard": "Ťuknutím na virtuálnu klávesnicu môžete začať",
|
||||
"wifi_password": "Heslo Wi-Fi",
|
||||
"connect_to_it": "Pripojiť"
|
||||
},
|
||||
"save-capture": {
|
||||
"please_connect": "Prosím pripojte USB kľúč, aby ste si uložili záznam.",
|
||||
"we_are_saving": "Ukladáme záznam.",
|
||||
"tap_msg": "Môžete ťuknúť na USB kľúč, ak chcete spustiť nový záznam.",
|
||||
"capture_download": "Sťahovanie záznamu sa spúšťa...",
|
||||
"start_capture_btn": "Spustiť ďalší záznam"
|
||||
},
|
||||
"update": {
|
||||
"tinycheck_needs": "TinyCheck je potrebné aktualizovať na ďalšiu verziu",
|
||||
"please_click": "Kliknite na tlačidlo nižšie a aktualizujte ho.",
|
||||
"the_process": "Proces môže trvať niekoľko minút, prosím čakajte...",
|
||||
"update_finished": "Aktualizácia dokončená, poďme obnoviť rozhranie...",
|
||||
"update_it": "Aktualizovať teraz"
|
||||
}
|
||||
}
|
@ -14,7 +14,6 @@ export const i18n = new VueI18n({
|
||||
'ru': require('@/locales/ru.json'),
|
||||
'pt': require('@/locales/pt.json'),
|
||||
'it': require('@/locales/it.json'),
|
||||
'de': require('@/locales/de.json'),
|
||||
'sk': require('@/locales/sk.json')
|
||||
'de': require('@/locales/de.json')
|
||||
}
|
||||
})
|
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,19 @@
|
||||
pymisp==2.4.165.1
|
||||
sqlalchemy==1.4.48
|
||||
ipwhois==1.2.0
|
||||
netaddr==0.8.0
|
||||
flask==2.2.5
|
||||
flask_httpauth==4.8.0
|
||||
pyjwt==2.4.0
|
||||
psutil==5.8.0
|
||||
pydig==0.4.0
|
||||
pyudev==0.24.0
|
||||
pyyaml==5.3.1
|
||||
wifi==0.3.8
|
||||
qrcode==7.3.1
|
||||
netifaces==0.11.0
|
||||
weasyprint==59.0
|
||||
python-whois==0.8.0
|
||||
six==1.16.0
|
||||
ipwhois
|
||||
M2Crypto
|
||||
pyOpenSSL
|
||||
pydig
|
||||
pymisp
|
||||
pycti
|
||||
netaddr
|
||||
pyyaml
|
||||
flask
|
||||
flask_httpauth
|
||||
pyjwt
|
||||
sqlalchemy
|
||||
psutil
|
||||
pyudev
|
||||
wifi
|
||||
qrcode
|
||||
netifaces
|
||||
weasyprint
|
||||
python-whois
|
||||
|
@ -1,30 +1,41 @@
|
||||
CREATE TABLE "iocs" (
|
||||
"id" INTEGER UNIQUE,
|
||||
"value" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"tlp" TEXT NOT NULL,
|
||||
"tag" TEXT NOT NULL,
|
||||
"source" TEXT NOT NULL,
|
||||
"added_on" TEXT NOT NULL,
|
||||
"value" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"tlp" TEXT NOT NULL,
|
||||
"tag" TEXT NOT NULL,
|
||||
"source" TEXT NOT NULL,
|
||||
"added_on" NUMERIC NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
);
|
||||
|
||||
CREATE TABLE "whitelist" (
|
||||
"id" INTEGER UNIQUE,
|
||||
"element" TEXT NOT NULL UNIQUE,
|
||||
"type" TEXT NOT NULL,
|
||||
"source" TEXT NOT NULL,
|
||||
"added_on" TEXT NOT NULL,
|
||||
"element" TEXT NOT NULL UNIQUE,
|
||||
"type" TEXT NOT NULL,
|
||||
"source" TEXT NOT NULL,
|
||||
"added_on" INTEGER NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
);
|
||||
|
||||
CREATE TABLE "misp" (
|
||||
"id" INTEGER UNIQUE,
|
||||
"name" TEXT,
|
||||
"url" TEXT NOT NULL,
|
||||
"apikey" TEXT NOT NULL,
|
||||
"id" INTEGER UNIQUE,
|
||||
"name" TEXT,
|
||||
"url" TEXT NOT NULL,
|
||||
"apikey" TEXT NOT NULL,
|
||||
"verifycert" INTEGER NOT NULL DEFAULT 0,
|
||||
"added_on" TEXT NOT NULL,
|
||||
"last_sync" TEXT NOT NULL DEFAULT 0,
|
||||
"added_on" NUMERIC NOT NULL,
|
||||
"last_sync" NUMERIC NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
);
|
||||
|
||||
CREATE TABLE "octi" (
|
||||
"id" INTEGER UNIQUE,
|
||||
"name" TEXT,
|
||||
"url" TEXT NOT NULL,
|
||||
"apikey" TEXT NOT NULL,
|
||||
"verifycert" INTEGER NOT NULL DEFAULT 0,
|
||||
"added_on" NUMERIC NOT NULL,
|
||||
"last_sync" NUMERIC NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
);
|
||||
|
@ -52,8 +52,7 @@ frontend:
|
||||
user_lang: userlang
|
||||
update: updateoption
|
||||
choose_net: false
|
||||
http_port: 80
|
||||
|
||||
|
||||
# NETWORK -
|
||||
# Some elements related to the network configuration, such as
|
||||
# the interfaces (updated during the install), the list of SSIDs
|
||||
@ -61,7 +60,7 @@ frontend:
|
||||
#
|
||||
network:
|
||||
in: iface_in
|
||||
internet_check: https://8.8.8.8
|
||||
internet_check: https://1.1.1.1
|
||||
out: iface_out
|
||||
ssids:
|
||||
- wireless
|
||||
@ -78,6 +77,6 @@ network:
|
||||
watchers:
|
||||
iocs:
|
||||
- https://raw.githubusercontent.com/KasperskyLab/TinyCheck/main/assets/iocs.json
|
||||
- https://raw.githubusercontent.com/AssoEchap/stalkerware-indicators/master/generated/indicators-for-tinycheck.json
|
||||
- https://raw.githubusercontent.com/Te-k/stalkerware-indicators/master/indicators-for-tinycheck.json
|
||||
whitelists:
|
||||
- https://raw.githubusercontent.com/KasperskyLab/TinyCheck/main/assets/whitelist.json
|
||||
|
60
install.sh
@ -3,7 +3,7 @@
|
||||
CURRENT_USER="${SUDO_USER}"
|
||||
SCRIPT_PATH="$( cd "$(dirname "$0")" ; pwd -P )"
|
||||
HOST="$( hostname )"
|
||||
IFACES="$( ip a s | grep -Eo '[a-z0-9]{4,15}\: ' | grep -oE [a-z0-9]+ )"
|
||||
IFACES="$( ifconfig -a | grep -Eo '[a-z0-9]{4,14}\: ' | grep -oE [a-z0-9]+ )"
|
||||
IFACE_OUT=""
|
||||
IFACE_IN=""
|
||||
LOCALES=(en fr cat es ru pt de it)
|
||||
@ -29,7 +29,7 @@ check_operating_system() {
|
||||
error="\e[91m [✘] Need to be run on a Debian-like operating system, exiting.\e[39m"
|
||||
|
||||
if [[ -f "/etc/os-release" ]]; then
|
||||
if [[ $(cat /etc/os-release | grep -e "ID_LIKE=\"\?debian" -e "ID=debian") ]]; then
|
||||
if [[ $(cat /etc/os-release | grep "ID_LIKE=debian") ]]; then
|
||||
echo -e "\e[92m [✔] Debian-like operating system\e[39m"
|
||||
else
|
||||
echo -e "$error"
|
||||
@ -69,7 +69,7 @@ set_credentials() {
|
||||
read -s password2
|
||||
echo ""
|
||||
|
||||
if [ "$password1" == "$password2" ]; then
|
||||
if [ $password1 = $password2 ]; then
|
||||
password=$(echo -n "$password1" | sha256sum | cut -d" " -f1)
|
||||
sed -i "s/userlogin/$login/g" /usr/share/tinycheck/config.yaml
|
||||
sed -i "s/userpassword/$password/g" /usr/share/tinycheck/config.yaml
|
||||
@ -264,16 +264,12 @@ change_hostname() {
|
||||
|
||||
install_package() {
|
||||
# Install associated packages by using aptitude.
|
||||
if [[ $1 == "dnsmasq" || $1 == "hostapd" || $1 == "tshark" || $1 == "sqlite3" || $1 == "unclutter" || $1 == "swig" || $1 == "curl" ]]; then
|
||||
|
||||
if [[ $1 == "dnsmasq" || $1 == "hostapd" || $1 == "tshark" || $1 == "sqlite3" || $1 == "suricata" || $1 == "unclutter" ]]; then
|
||||
apt-get install $1 -y
|
||||
elif [[ $1 == "suricata" ]];then
|
||||
add-apt-repository ppa:oisf/suricata-stable
|
||||
apt-get install suricata -y
|
||||
elif [[ $1 == "zeek" ]]; then
|
||||
distrib=$(cat /etc/os-release | grep -E "^ID=" | cut -d"=" -f2)
|
||||
version=$(cat /etc/os-release | grep "VERSION_ID" | cut -d"\"" -f2)
|
||||
if [[ $distrib == "debian" ]]; then
|
||||
if [[ $distrib == "debian" || $distrib == "ubuntu" ]]; then
|
||||
echo "deb http://download.opensuse.org/repositories/security:/zeek/Debian_$version/ /" > /etc/apt/sources.list.d/security:zeek.list
|
||||
wget -nv "https://download.opensuse.org/repositories/security:zeek/Debian_$version/Release.key" -O Release.key
|
||||
elif [[ $distrib == "ubuntu" ]]; then
|
||||
@ -287,46 +283,27 @@ install_package() {
|
||||
rm Release.key && sudo apt-get update
|
||||
apt-get install zeek -y
|
||||
elif [[ $1 == "node" ]]; then
|
||||
curl -sL https://deb.nodesource.com/setup_14.x | bash -
|
||||
curl -sL https://deb.nodesource.com/setup_14.x | bash
|
||||
apt-get install -y nodejs
|
||||
elif [[ $1 == "dig" ]]; then
|
||||
apt-get install -y dnsutils
|
||||
elif [[ $1 == "pip" ]]; then
|
||||
apt-get install -y python3-pip
|
||||
elif [[ $1 == "dhcpcd" ]]; then
|
||||
apt-get install -y dhcpcd5
|
||||
fi
|
||||
}
|
||||
|
||||
check_dnsmasq() {
|
||||
readlink /etc/resolv.conf
|
||||
status=$?
|
||||
if [[ $status -eq 0 && -f "/usr/sbin/dnsmasq" ]]; then
|
||||
systemctl disable systemd-resolved
|
||||
systemctl stop systemd-resolved
|
||||
unlink /etc/resolv.conf
|
||||
echo "nameserver 8.8.8.8" | tee /etc/resolv.conf
|
||||
install_package dnsmasq
|
||||
else
|
||||
install_package dnsmasq
|
||||
fi
|
||||
}
|
||||
|
||||
check_dependencies() {
|
||||
# Check binary dependencies associated to the project.
|
||||
# If not installed, call install_package with the package name.
|
||||
check_dnsmasq
|
||||
bins=("/usr/sbin/hostapd"
|
||||
"/usr/sbin/dnsmasq"
|
||||
"/opt/zeek/bin/zeek"
|
||||
"/usr/bin/tshark"
|
||||
"/usr/bin/dig"
|
||||
"/usr/bin/suricata"
|
||||
"/usr/bin/unclutter"
|
||||
"/usr/bin/sqlite3"
|
||||
"/usr/bin/pip"
|
||||
"/usr/bin/swig"
|
||||
"/usr/sbin/dhcpcd"
|
||||
"/usr/bin/curl")
|
||||
"/usr/bin/pip")
|
||||
|
||||
echo -e "\e[39m[+] Checking dependencies...\e[39m"
|
||||
for bin in "${bins[@]}"
|
||||
@ -387,7 +364,7 @@ cleaning() {
|
||||
check_interfaces(){
|
||||
|
||||
# Get the current connected interface name.
|
||||
ciface="$(ip r l default |grep -Po '(?<=(dev ))(\S+)')"
|
||||
ciface="$(route | grep default | head -1 | grep -Eo '[a-z0-9]+$')"
|
||||
|
||||
# Setup of iface_out which can be any interface,
|
||||
# but needs to be connected now or in the future.
|
||||
@ -402,7 +379,7 @@ check_interfaces(){
|
||||
IFACES=( "${IFACES[@]/$ciface}" )
|
||||
for iface in $IFACES;
|
||||
do
|
||||
config="$(ip a s $iface)"
|
||||
config="$(ifconfig $iface)"
|
||||
echo -n "[?] Do you want to use $iface as a bridge to Internet (network/out) ? [Y/n] "
|
||||
read answer
|
||||
if [[ "$answer" =~ ^([yY][eE][sS]|[yY])$ ]]
|
||||
@ -419,9 +396,8 @@ check_interfaces(){
|
||||
# Wi-Fi interface with AP mode available.
|
||||
for iface in $IFACES;
|
||||
do
|
||||
if echo "$iface" | grep -Eq "(wlan[0-9]|wl[a-z0-9]{,20})"; then
|
||||
config="$(ip a s $iface)" # Get the iface logic configuration
|
||||
|
||||
if echo "$iface" | grep -Eq "(wlan[0-9]|wl[a-z0-9]{20})"; then
|
||||
config="$(ifconfig $iface)" # Get the iface logic configuration
|
||||
if echo "$config" | grep -qv "inet "; then # Test if not currently connected
|
||||
hw="$(iw $iface info | grep wiphy | cut -d" " -f2)" # Get the iface hardware id.
|
||||
info="$(iw phy$hw info)" # Get the iface hardware infos.
|
||||
@ -469,15 +445,9 @@ feeding_iocs() {
|
||||
}
|
||||
|
||||
reboot_box() {
|
||||
echo -e "\e[92m[+] The system is going to reboot, OK ?\e[39m"
|
||||
read answer
|
||||
if [[ "$answer" =~ ^([yY][eE][sS]|[yY])$ ]]
|
||||
then
|
||||
sleep 5
|
||||
reboot
|
||||
else
|
||||
exit
|
||||
fi
|
||||
echo -e "\e[92m[+] The system is going to reboot\e[39m"
|
||||
sleep 5
|
||||
reboot
|
||||
}
|
||||
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
|
45
server/backend/app/blueprints/octi.py
Normal file
@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from flask import Blueprint, jsonify, Response, request
|
||||
from app.decorators import require_header_token, require_get_token
|
||||
from app.classes.octi import OCTI
|
||||
|
||||
import json
|
||||
|
||||
octi_bp = Blueprint("octi", __name__)
|
||||
octi = OCTI()
|
||||
|
||||
|
||||
@octi_bp.route('/add', methods=['POST'])
|
||||
@require_header_token
|
||||
def add_instance():
|
||||
"""
|
||||
Parse and add a OpenCTI instance to the database.
|
||||
:return: status of the operation in JSON
|
||||
"""
|
||||
data = json.loads(request.data)
|
||||
res = octi.add_instance(data["data"]["instance"])
|
||||
return jsonify(res)
|
||||
|
||||
|
||||
@octi_bp.route('/delete/<octi_id>', methods=['GET'])
|
||||
@require_header_token
|
||||
def delete_instance(octi_id):
|
||||
"""
|
||||
Delete a OpenCTI instance by its id to the database.
|
||||
:return: status of the operation in JSON
|
||||
"""
|
||||
res = octi.delete_instance(octi_id)
|
||||
return jsonify(res)
|
||||
|
||||
|
||||
@octi_bp.route('/get_all', methods=['GET'])
|
||||
@require_header_token
|
||||
def get_all():
|
||||
"""
|
||||
Retreive a list of all OpenCTI instances.
|
||||
:return: list of OpenCTI instances in JSON.
|
||||
"""
|
||||
res = octi.get_instances()
|
||||
return jsonify({"results": [i for i in res]})
|
164
server/backend/app/classes/octi.py
Normal file
@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from app import db
|
||||
from app.db.models import OCTIInst
|
||||
from app.definitions import definitions as defs
|
||||
|
||||
from sqlalchemy.sql import exists
|
||||
from urllib.parse import unquote
|
||||
from flask import escape
|
||||
from pycti import OpenCTIApiClient, Infrastructure
|
||||
import re
|
||||
import time
|
||||
import sys
|
||||
|
||||
|
||||
class OCTI(object):
|
||||
def __init__(self):
|
||||
return None
|
||||
|
||||
def add_instance(self, instance):
|
||||
"""
|
||||
Parse and add a OpenCTI instance to the database.
|
||||
:return: status of the operation in JSON
|
||||
"""
|
||||
|
||||
url = instance["url"]
|
||||
name = instance["name"]
|
||||
apikey = instance["key"]
|
||||
verify = instance["ssl"]
|
||||
last_sync = int(time.time()-31536000) # One year
|
||||
|
||||
sameinstances = db.session.query(OCTIInst).filter(
|
||||
OCTIInst.url == url, OCTIInst.apikey == apikey)
|
||||
if sameinstances.count():
|
||||
return {"status": False,
|
||||
"message": "This OpenCTI instance already exists"}
|
||||
if name:
|
||||
if self.test_instance(url, apikey, verify):
|
||||
added_on = int(time.time())
|
||||
db.session.add(OCTIInst(name, escape(
|
||||
url), apikey, verify, added_on, last_sync))
|
||||
db.session.commit()
|
||||
return {"status": True,
|
||||
"message": "OpenCTI instance added"}
|
||||
else:
|
||||
return {"status": False,
|
||||
"message": "Please verify the connection to the OpenCTI instance"}
|
||||
else:
|
||||
return {"status": False,
|
||||
"message": "Please provide a name for your instance"}
|
||||
|
||||
@staticmethod
|
||||
def delete_instance(opencti_id):
|
||||
"""
|
||||
Delete a OpenCTI instance by its id in the database.
|
||||
:return: status of the operation in JSON
|
||||
"""
|
||||
if db.session.query(exists().where(OCTIInst.id == opencti_id)).scalar():
|
||||
db.session.query(OCTIInst).filter_by(id=opencti_id).delete()
|
||||
db.session.commit()
|
||||
return {"status": True,
|
||||
"message": "OpenCTI instance deleted"}
|
||||
else:
|
||||
return {"status": False,
|
||||
"message": "OpenCTI instance not found"}
|
||||
|
||||
def get_instances(self):
|
||||
"""
|
||||
Get OpenCTI instances from the database
|
||||
:return: generator of the records.
|
||||
"""
|
||||
for opencti in db.session.query(OCTIInst).all():
|
||||
opencti = opencti.__dict__
|
||||
yield {"id": opencti["id"],
|
||||
"name": opencti["name"],
|
||||
"url": opencti["url"],
|
||||
"apikey": opencti["apikey"],
|
||||
"verifycert": True if opencti["verifycert"] else False,
|
||||
"connected": self.test_instance(opencti["url"], opencti["apikey"], opencti["verifycert"]),
|
||||
"lastsync": opencti["last_sync"]}
|
||||
|
||||
@staticmethod
|
||||
def test_instance(url, apikey, verify):
|
||||
"""
|
||||
Test the connection of the OpenCTI instance.
|
||||
:return: generator of the records.
|
||||
"""
|
||||
try:
|
||||
OpenCTIApiClient(url, token=apikey, ssl_verify=verify)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def update_sync(opencti_id):
|
||||
"""
|
||||
Update the last synchronization date by the actual date.
|
||||
:return: bool, True if updated.
|
||||
"""
|
||||
try:
|
||||
misp = OCTIInst.query.get(int(opencti_id))
|
||||
misp.last_sync = int(time.time())
|
||||
db.session.commit()
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_iocs(opencti_id):
|
||||
"""
|
||||
Get all IOCs from specific OpenCTI instance
|
||||
:return: generator containing the IOCs.
|
||||
"""
|
||||
opencti = OCTIInst.query.get(int(opencti_id))
|
||||
if opencti is not None:
|
||||
if opencti.url and opencti.apikey:
|
||||
try:
|
||||
# Connect to OpenCTI instance and get network activity attributes.
|
||||
i = OpenCTIApiClient(
|
||||
opencti.url, opencti.apikey, opencti.verifycert)
|
||||
r = Infrastructure(i).list(getAll=True)
|
||||
except:
|
||||
print(
|
||||
"Unable to connect to the OpenCTI instance ({}/{}).".format(opencti.url, opencti.apikey))
|
||||
return []
|
||||
"""
|
||||
for attr in r["Attribute"]:
|
||||
if attr["type"] in ["ip-dst", "domain", "snort", "x509-fingerprint-sha1"]:
|
||||
|
||||
ioc = {"value": attr["value"],
|
||||
"type": None,
|
||||
"tag": "suspect",
|
||||
"tlp": "white"}
|
||||
|
||||
# Deduce the IOC type.
|
||||
if re.match(defs["iocs_types"][0]["regex"], attr["value"]):
|
||||
ioc["type"] = "ip4addr"
|
||||
elif re.match(defs["iocs_types"][1]["regex"], attr["value"]):
|
||||
ioc["type"] = "ip6addr"
|
||||
elif re.match(defs["iocs_types"][2]["regex"], attr["value"]):
|
||||
ioc["type"] = "cidr"
|
||||
elif re.match(defs["iocs_types"][3]["regex"], attr["value"]):
|
||||
ioc["type"] = "domain"
|
||||
elif re.match(defs["iocs_types"][4]["regex"], attr["value"]):
|
||||
ioc["type"] = "sha1cert"
|
||||
elif "alert " in attr["value"][0:6]:
|
||||
ioc["type"] = "snort"
|
||||
else:
|
||||
continue
|
||||
|
||||
if "Tag" in attr:
|
||||
for tag in attr["Tag"]:
|
||||
# Add a TLP to the IOC if defined in tags.
|
||||
tlp = re.search(
|
||||
r"^(?:tlp:)(red|green|amber|white)", tag['name'].lower())
|
||||
if tlp:
|
||||
ioc["tlp"] = tlp.group(1)
|
||||
|
||||
# Add possible tag (need to match TinyCheck tags)
|
||||
if tag["name"].lower() in [t["tag"] for t in defs["iocs_tags"]]:
|
||||
ioc["tag"] = tag["name"].lower()
|
||||
yield ioc
|
||||
"""
|
@ -29,6 +29,17 @@ class MISPInst(db.Model):
|
||||
self.last_sync = last_sync
|
||||
|
||||
|
||||
class OCTIInst(db.Model):
|
||||
def __init__(self, name, url, key, ssl, added_on, last_sync):
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.apikey = key
|
||||
self.verifycert = ssl
|
||||
self.added_on = added_on
|
||||
self.last_sync = last_sync
|
||||
|
||||
|
||||
db.mapper(Whitelist, db.Table('whitelist', db.metadata, autoload=True))
|
||||
db.mapper(Ioc, db.Table('iocs', db.metadata, autoload=True))
|
||||
db.mapper(MISPInst, db.Table('misp', db.metadata, autoload=True))
|
||||
db.mapper(OCTIInst, db.Table('octi', db.metadata, autoload=True))
|
||||
|
@ -1,191 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
import os
|
||||
import subprocess
|
||||
import platform
|
||||
import socket
|
||||
import pkg_resources
|
||||
import psutil
|
||||
|
||||
__author__ = 'Eugeny N Ablesov'
|
||||
__version__ = '1.0.17'
|
||||
|
||||
def collect_accounts_info():
|
||||
""" This call collects generic information about
|
||||
user accounts presented on system running TinyCheck.
|
||||
|
||||
No personal information collected or provided by this call.
|
||||
"""
|
||||
accs = { }
|
||||
users = psutil.users()
|
||||
for user in users:
|
||||
accs[user.name + '@' + user.host] = {
|
||||
'started': user.started,
|
||||
'term': user.terminal
|
||||
}
|
||||
alt_user = os.getenv('SUDO_USER', os.getenv('USER'))
|
||||
usr = 'root' if os.path.expanduser('~') == '/root' else alt_user
|
||||
pid = psutil.Process().pid
|
||||
term = psutil.Process().terminal() if 'Linux' in platform.system() else 'win'
|
||||
accs[usr + '@' + term] = { 'pid': pid }
|
||||
return accs
|
||||
|
||||
def collect_os_info():
|
||||
""" This call collects generic information about
|
||||
operating system running TinyCheck.
|
||||
|
||||
No personal information collected or provided by this call.
|
||||
"""
|
||||
os_info = { }
|
||||
os_info['system'] = platform.system()
|
||||
os_info['release'] = platform.release()
|
||||
os_info['version'] = platform.version()
|
||||
os_info['platform'] = platform.platform(aliased=True)
|
||||
if 'Windows' in os_info['system']:
|
||||
os_info['dist'] = platform.win32_ver()
|
||||
if 'Linux' in os_info['system']:
|
||||
os_info['dist'] = platform.libc_ver()
|
||||
return os_info
|
||||
|
||||
def collect_hardware_info():
|
||||
""" This call collects information about hardware running TinyCheck.
|
||||
|
||||
No personal information collected or provided by this call.
|
||||
"""
|
||||
hw_info = { }
|
||||
hw_info['arch'] = platform.architecture()
|
||||
hw_info['machine'] = platform.machine()
|
||||
hw_info['cpus'] = psutil.cpu_count(logical=False)
|
||||
hw_info['cores'] = psutil.cpu_count()
|
||||
hw_info['load'] = psutil.getloadavg()
|
||||
disk_info = psutil.disk_usage('/')
|
||||
hw_info['disk'] = {
|
||||
'total': disk_info.total,
|
||||
'used': disk_info.used,
|
||||
'free': disk_info.free
|
||||
}
|
||||
return hw_info
|
||||
|
||||
def collect_network_info():
|
||||
""" This call collects information about
|
||||
network configuration and state running TinyCheck.
|
||||
|
||||
No personal information collected or provided by this call.
|
||||
"""
|
||||
net_info = { }
|
||||
net_info['namei'] = socket.if_nameindex()
|
||||
addrs = psutil.net_if_addrs()
|
||||
state = psutil.net_io_counters(pernic=True)
|
||||
for interface in addrs.keys():
|
||||
net_info[interface] = { }
|
||||
int_info = state[interface]
|
||||
props = [p for p in dir(int_info)
|
||||
if not p.startswith("_")
|
||||
and not p == "index"
|
||||
and not p == "count"]
|
||||
for prop in props:
|
||||
net_info[interface][prop] = getattr(int_info, prop)
|
||||
return net_info
|
||||
|
||||
def collect_dependency_info(package_list):
|
||||
""" This call collects information about
|
||||
python packages required to run TinyCheck.
|
||||
|
||||
No personal information collected or provided by this call.
|
||||
"""
|
||||
dependencies = { }
|
||||
installed_packages = list(pkg_resources.working_set)
|
||||
installed_packages_list = sorted(["%s==%s"
|
||||
% (installed.key, installed.version)
|
||||
for installed in installed_packages])
|
||||
for pkg in installed_packages_list:
|
||||
[package_name, package_version] = pkg.split('==')
|
||||
if package_name in package_list:
|
||||
dependencies[package_name] = package_version
|
||||
return dependencies
|
||||
|
||||
def collect_db_tables_records_count(db_path, tables):
|
||||
result = { }
|
||||
for table in tables:
|
||||
query = 'SELECT COUNT(*) FROM %s' % (table)
|
||||
sqlite_call = subprocess.Popen(['sqlite3', db_path, query], stdout = subprocess.PIPE)
|
||||
stout, sterr = sqlite_call.communicate()
|
||||
val = stout.decode("utf-8")
|
||||
recs = int(val) if val else 0
|
||||
result[table] = recs
|
||||
return result
|
||||
|
||||
def collect_internal_state(db_path, tables, to_check):
|
||||
""" This call collects information about
|
||||
installed TinyCheck instance and its internal state.
|
||||
|
||||
No personal information collected or provided by this call.
|
||||
"""
|
||||
state_ = { }
|
||||
available = os.path.isfile(db_path)
|
||||
dbsize = 0
|
||||
state_['db'] = {
|
||||
'available': available,
|
||||
'size': dbsize
|
||||
}
|
||||
state_['db']['records'] = { }
|
||||
if available:
|
||||
state_['db']['size'] = os.stat(db_path).st_size
|
||||
state_['db']['records'] = collect_db_tables_records_count(db_path, tables)
|
||||
|
||||
services_ = { }
|
||||
for alias in to_check:
|
||||
status = subprocess.call(['systemctl', 'is-active', '--quiet', '%s' % (to_check[alias])])
|
||||
state = ''
|
||||
if status != 0:
|
||||
sysctl_call = subprocess.Popen(
|
||||
["systemctl", "status", "%s" % (to_check[alias]),
|
||||
r"|",
|
||||
"grep",
|
||||
r"''"],
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE)
|
||||
stout, sterr = sysctl_call.communicate()
|
||||
state = stout.decode("utf-8")
|
||||
errs = sterr.decode("utf-8")
|
||||
if "could not be found" in errs:
|
||||
state = 'Service not found'
|
||||
services_[alias] = {
|
||||
'running': status == 0,
|
||||
'status': status,
|
||||
'state': state
|
||||
}
|
||||
state_['svc'] = services_
|
||||
return state_
|
||||
|
||||
def main():
|
||||
print("TinyCheck diagnostics script.\nVersion: %s" % (__version__))
|
||||
print("")
|
||||
|
||||
db_path = '/usr/share/tinycheck/tinycheck.sqlite3'
|
||||
tables = ['iocs', 'whitelist', 'misp']
|
||||
services = { }
|
||||
services['frontend'] = 'tinycheck-frontend.service'
|
||||
services['backend'] = 'tinycheck-backend.service'
|
||||
services['kiosk'] = 'tinycheck-kiosk.service'
|
||||
services['watchers'] = 'tinycheck-watchers.service'
|
||||
|
||||
deps = [
|
||||
'pymisp', 'sqlalchemy', 'ipwhois',
|
||||
'netaddr', 'flask', 'flask_httpauth',
|
||||
'pyjwt', 'psutil', 'pydig', 'pyudev',
|
||||
'pyyaml', 'wifi', 'qrcode', 'netifaces',
|
||||
'weasyprint', 'python-whois', 'six' ]
|
||||
|
||||
diagnostics = { }
|
||||
diagnostics['acc'] = collect_accounts_info()
|
||||
diagnostics['os'] = collect_os_info()
|
||||
diagnostics['hw'] = collect_hardware_info()
|
||||
diagnostics['net'] = collect_network_info()
|
||||
diagnostics['deps'] = collect_dependency_info(deps)
|
||||
diagnostics['state'] = collect_internal_state(db_path, tables, services)
|
||||
report = { 'diagnostics': diagnostics }
|
||||
print(report)
|
||||
print("")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -7,9 +7,11 @@ from app.blueprints.ioc import ioc_bp
|
||||
from app.blueprints.whitelist import whitelist_bp
|
||||
from app.blueprints.config import config_bp
|
||||
from app.blueprints.misp import misp_bp
|
||||
from app.blueprints.octi import octi_bp
|
||||
import datetime
|
||||
import secrets
|
||||
import jwt
|
||||
from OpenSSL import SSL
|
||||
from app.utils import read_config
|
||||
from sys import path
|
||||
|
||||
@ -57,12 +59,14 @@ app.register_blueprint(ioc_bp, url_prefix='/api/ioc')
|
||||
app.register_blueprint(whitelist_bp, url_prefix='/api/whitelist')
|
||||
app.register_blueprint(config_bp, url_prefix='/api/config')
|
||||
app.register_blueprint(misp_bp, url_prefix='/api/misp')
|
||||
app.register_blueprint(octi_bp, url_prefix='/api/opencti')
|
||||
|
||||
if __name__ == '__main__':
|
||||
ssl_cert = "{}/{}".format(path[0], 'cert.pem')
|
||||
ssl_key = "{}/{}".format(path[0], 'key.pem')
|
||||
|
||||
if read_config(("backend", "remote_access")):
|
||||
app.run(host="0.0.0.0", port=443, ssl_context=(ssl_cert, ssl_key))
|
||||
app.run(host="0.0.0.0", port=443,
|
||||
ssl_context=(ssl_cert, ssl_key))
|
||||
else:
|
||||
app.run(port=443)
|
||||
app.run(port=443, ssl_context=(ssl_cert, ssl_key))
|
||||
|
@ -5,6 +5,7 @@ from app.utils import read_config
|
||||
from app.classes.iocs import IOCs
|
||||
from app.classes.whitelist import WhiteList
|
||||
from app.classes.misp import MISP
|
||||
from app.classes.octi import OCTI
|
||||
|
||||
import requests
|
||||
import json
|
||||
@ -37,13 +38,14 @@ def watch_iocs():
|
||||
if w["status"] == False:
|
||||
iocs = IOCs()
|
||||
iocs_list = []
|
||||
to_delete = []
|
||||
try:
|
||||
res = requests.get(w["url"], verify=False)
|
||||
if res.status_code == 200:
|
||||
content = json.loads(res.content)
|
||||
iocs_list = content["iocs"] if "iocs" in content else []
|
||||
to_delete = content["to_delete"] if "to_delete" in content else []
|
||||
iocs_list = content["iocs"] if "iocs" in content else [
|
||||
]
|
||||
to_delete = content["to_delete"] if "to_delete" in content else [
|
||||
]
|
||||
else:
|
||||
w["status"] = False
|
||||
except:
|
||||
@ -86,13 +88,14 @@ def watch_whitelists():
|
||||
if w["status"] == False:
|
||||
whitelist = WhiteList()
|
||||
elements = []
|
||||
to_delete = []
|
||||
try:
|
||||
res = requests.get(w["url"], verify=False)
|
||||
if res.status_code == 200:
|
||||
content = json.loads(res.content)
|
||||
elements = content["elements"] if "elements" in content else []
|
||||
to_delete = content["to_delete"] if "to_delete" in content else []
|
||||
elements = content["elements"] if "elements" in content else [
|
||||
]
|
||||
to_delete = content["to_delete"] if "to_delete" in content else [
|
||||
]
|
||||
else:
|
||||
w["status"] = False
|
||||
except:
|
||||
@ -137,13 +140,40 @@ def watch_misp():
|
||||
ioc["value"], "misp-{}".format(ist["id"]))
|
||||
misp.update_sync(ist["id"])
|
||||
instances.pop(i)
|
||||
if instances: time.sleep(60)
|
||||
if instances:
|
||||
time.sleep(60)
|
||||
|
||||
|
||||
def watch_opencti():
|
||||
"""
|
||||
Retrieve IOCs from OpenCTI instances. Each new element is
|
||||
tested and then added to the database.
|
||||
"""
|
||||
iocs, octi = IOCs(), OCTI()
|
||||
instances = [i for i in octi.get_instances()]
|
||||
|
||||
while instances:
|
||||
for i, ist in enumerate(instances):
|
||||
status = octi.test_instance(ist["url"],
|
||||
ist["apikey"],
|
||||
ist["verifycert"])
|
||||
if status:
|
||||
print("Testing...")
|
||||
# for ioc in octi.get_iocs(ist["id"]):
|
||||
# iocs.add(ioc["type"], ioc["tag"], ioc["tlp"],
|
||||
# ioc["value"], "octi-{}".format(ist["id"]))
|
||||
# octi.update_sync(ist["id"])
|
||||
instances.pop(i)
|
||||
if instances:
|
||||
time.sleep(60)
|
||||
|
||||
|
||||
p1 = Process(target=watch_iocs)
|
||||
p2 = Process(target=watch_whitelists)
|
||||
p3 = Process(target=watch_misp)
|
||||
p4 = Process(target=watch_octi)
|
||||
|
||||
p1.start()
|
||||
p2.start()
|
||||
p3.start()
|
||||
p4.start()
|
||||
|
@ -2,11 +2,11 @@ country_code=GB
|
||||
interface={IFACE}
|
||||
ssid={SSID}
|
||||
hw_mode=g
|
||||
channel={CHAN}
|
||||
channel=7
|
||||
auth_algs=1
|
||||
wpa=2
|
||||
wpa_passphrase={PASS}
|
||||
wpa_key_mgmt=WPA-PSK
|
||||
wpa_pairwise=TKIP
|
||||
rsn_pairwise=CCMP
|
||||
disassoc_low_ack=0
|
||||
disassoc_low_ack=0
|
@ -24,7 +24,7 @@ class Analysis(object):
|
||||
if self.token is not None:
|
||||
parent = "/".join(sys.path[0].split("/")[:-2])
|
||||
sp.Popen(
|
||||
[sys.executable, "{}/analysis/analysis.py".format(parent), "-f", "/tmp/{}".format(self.token)])
|
||||
[sys.executable, "{}/analysis/analysis.py".format(parent), "/tmp/{}".format(self.token)])
|
||||
return {"status": True,
|
||||
"message": "Analysis started",
|
||||
"token": self.token}
|
||||
|
@ -239,7 +239,7 @@ class Network(object):
|
||||
# Kill potential zombies of hostapd
|
||||
terminate_process("hostapd")
|
||||
|
||||
sp.Popen(["ip","link","set", self.iface_in, "up"]).wait()
|
||||
sp.Popen(["ifconfig", self.iface_in, "up"]).wait()
|
||||
sp.Popen(
|
||||
"/usr/sbin/hostapd /tmp/hostapd.conf > /tmp/hostapd.log", shell=True)
|
||||
|
||||
@ -298,22 +298,13 @@ class Network(object):
|
||||
shell=True).wait()
|
||||
|
||||
# Enable forwarding.
|
||||
|
||||
sp.Popen("nft add table nat",shell=True).wait()
|
||||
sp.Popen("nft 'add chain nat prerouting { type nat hook prerouting priority 100; }'",shell=True).wait()
|
||||
sp.Popen("nft 'add chain nat postrouting { type nat hook postrouting priority 100; }'",shell=True).wait()
|
||||
sp.Popen("nft add table ip filter",shell=True).wait()
|
||||
sp.Popen("nft 'add chain ip filter INPUT { type filter hook input priority 0; }'",shell=True).wait()
|
||||
|
||||
|
||||
sp.Popen(["nft","add","rule","ip","nat","postrouting","oifname",
|
||||
self.iface_out,"counter","masquerade"]).wait()
|
||||
sp.Popen(["iptables", "-A", "POSTROUTING", "-t", "nat", "-o",
|
||||
self.iface_out, "-j", "MASQUERADE"]).wait()
|
||||
|
||||
# Prevent the device to reach the 80 and 443 of TinyCheck.
|
||||
sp.Popen(["nft","add","rule","ip","filter","INPUT","iifname",self.iface_in,"ip",
|
||||
"protocol","tcp","ip","daddr","192.168.100.1","tcp","dport","{ 80,443}","counter","drop"]).wait()
|
||||
|
||||
|
||||
sp.Popen(["iptables", "-A", "INPUT", "-i", self.iface_in, "-d",
|
||||
"192.168.100.1", "-p", "tcp", "--match", "multiport", "--dports", "80,443", "-j" "DROP"]).wait()
|
||||
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
@ -323,15 +314,15 @@ class Network(object):
|
||||
This enable interfaces, with a simple check.
|
||||
:return: bool if everything goes well
|
||||
"""
|
||||
sh = sp.Popen(["ip" ,"a","s", iface],
|
||||
sh = sp.Popen(["ifconfig", iface],
|
||||
stdout=sp.PIPE, stderr=sp.PIPE)
|
||||
sh = sh.communicate()
|
||||
if b",UP" in sh[0]:
|
||||
if b"<UP," in sh[0]:
|
||||
return True # The interface is up.
|
||||
elif sh[1]:
|
||||
return False # The interface doesn't exists (most of the cases).
|
||||
else:
|
||||
sp.Popen(["ip","link","set", iface, "up"]).wait()
|
||||
sp.Popen(["ifconfig", iface, "up"]).wait()
|
||||
return True
|
||||
|
||||
def check_internet(self):
|
||||
@ -353,17 +344,12 @@ class Network(object):
|
||||
Deduce the channel to have for the AP in order to prevent
|
||||
kind of jamming between the two wifi interfaces.
|
||||
"""
|
||||
try:
|
||||
if self.iface_out[0] == "w":
|
||||
# Get the channel of the connected interface
|
||||
sh = sp.Popen(["iw", self.iface_out, "info"],
|
||||
stdout=sp.PIPE, stderr=sp.PIPE).communicate()
|
||||
res = re.search("channel ([0-9]{1,2})", sh[0].decode('utf8'))
|
||||
chn = res.group(1)
|
||||
|
||||
# Return a good candidate.
|
||||
return "11" if int(chn) < 7 else "1"
|
||||
else:
|
||||
return "1"
|
||||
except:
|
||||
return "1"
|
||||
# Get the channel of the connected interface
|
||||
sh = sp.Popen(["iw", self.iface_out, "info"],
|
||||
stdout=sp.PIPE, stderr=sp.PIPE).communicate()
|
||||
res = re.search("channel ([0-9]{1,2})", sh[0].decode('utf8'))
|
||||
chn = res.group(1)
|
||||
|
||||
# Return a good candidate.
|
||||
return "11" if int(chn) < 7 else "1"
|
||||
|
@ -46,12 +46,7 @@ app.register_blueprint(misc_bp, url_prefix='/api/misc')
|
||||
app.register_blueprint(update_bp, url_prefix='/api/update')
|
||||
|
||||
if __name__ == '__main__':
|
||||
port = ""
|
||||
try:
|
||||
port = int(read_config(("frontend", "http_port")))
|
||||
except:
|
||||
port = 80
|
||||
if read_config(("frontend", "remote_access")):
|
||||
app.run(host="0.0.0.0", port=port)
|
||||
app.run(host="0.0.0.0", port=80)
|
||||
else:
|
||||
app.run(port=port)
|
||||
app.run(port=80)
|
||||
|