From 14fec393716bd2954ee021013ef3f0962992b19f Mon Sep 17 00:00:00 2001 From: ghartmann <45218534+g-hartmann@users.noreply.github.com> Date: Tue, 31 Aug 2021 13:36:19 +0200 Subject: [PATCH 01/12] Fixes #10 --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index d76474f..f3f8bc7 100644 --- a/install.sh +++ b/install.sh @@ -396,7 +396,7 @@ 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 + if echo "$iface" | grep -Eq "(wlan[0-9]|wl[a-z0-9]{2,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. From 5c889cd0c6883fa99ce0c3bb0ca464a55868813f Mon Sep 17 00:00:00 2001 From: ranlo Date: Tue, 28 Sep 2021 12:42:55 +0300 Subject: [PATCH 02/12] Update config.yaml --- config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/config.yaml b/config.yaml index 2c87b8d..18b1bbb 100644 --- a/config.yaml +++ b/config.yaml @@ -52,6 +52,7 @@ frontend: user_lang: userlang update: updateoption choose_net: false + http_port: 80 # NETWORK - # Some elements related to the network configuration, such as From c745f3ce34c76d54ab01ce7541b39b90150b63cf Mon Sep 17 00:00:00 2001 From: ranlo Date: Tue, 28 Sep 2021 12:49:52 +0300 Subject: [PATCH 03/12] Update main.py - configurable listen port support configuration for listen port. Default to 80 if no configuration --- server/frontend/main.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server/frontend/main.py b/server/frontend/main.py index 76d2240..c95fe7e 100644 --- a/server/frontend/main.py +++ b/server/frontend/main.py @@ -46,7 +46,13 @@ 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 + print(port) if read_config(("frontend", "remote_access")): - app.run(host="0.0.0.0", port=80) + app.run(host="0.0.0.0", port=port) else: - app.run(port=80) + app.run(port=port) From 8da2a72df5253cee6e8babf6006aadea57125a36 Mon Sep 17 00:00:00 2001 From: ranlo Date: Tue, 28 Sep 2021 12:52:54 +0300 Subject: [PATCH 04/12] Update main.py --- server/frontend/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/server/frontend/main.py b/server/frontend/main.py index c95fe7e..d9a3c5d 100644 --- a/server/frontend/main.py +++ b/server/frontend/main.py @@ -51,7 +51,6 @@ if __name__ == '__main__': port = int(read_config(("frontend", "http_port"))) except: port = 80 - print(port) if read_config(("frontend", "remote_access")): app.run(host="0.0.0.0", port=port) else: From c170ae2fb2c6e9906cb6e126159ffa08119767dd Mon Sep 17 00:00:00 2001 From: Janik Besendorf Date: Tue, 12 Oct 2021 11:07:24 +0200 Subject: [PATCH 05/12] change hardcoded paths and introduce -f flag for calling analysis.py from frontende to skip device.json in report generation --- analysis/analysis.py | 116 ++++++++++++++---------- analysis/classes/report.py | 27 +++--- analysis/classes/zeekengine.py | 5 +- analysis/utils.py | 2 +- server/frontend/app/classes/analysis.py | 2 +- 5 files changed, 90 insertions(+), 62 deletions(-) diff --git a/analysis/analysis.py b/analysis/analysis.py index 345e82e..a00d203 100644 --- a/analysis/analysis.py +++ b/analysis/analysis.py @@ -16,60 +16,82 @@ import os containing a capture.pcap file. """ -if __name__ == "__main__": - if len(sys.argv) == 2: - capture_directory = sys.argv[1] - if os.path.isdir(capture_directory): - manager = Manager() - alerts = manager.dict() +def analyze(capture_directory,frontend=False): + if os.path.isdir(capture_directory): - def zeekengine(alerts): - zeek = ZeekEngine(capture_directory) - zeek.start_zeek() - alerts["zeek"] = zeek.retrieve_alerts() + manager = Manager() + alerts = manager.dict() - # 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=(',', ': '))) + def zeekengine(alerts): + zeek = ZeekEngine(capture_directory) + zeek.start_zeek() + alerts["zeek"] = zeek.retrieve_alerts() - # 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=(',', ': '))) + 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=(',', ': '))) - def snortengine(alerts): - suricata = SuricataEngine(capture_directory) - suricata.start_suricata() - alerts["suricata"] = suricata.get_alerts() + # 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=(',', ': '))) - # Start the engines. - p1 = Process(target=zeekengine, args=(alerts,)) - p2 = Process(target=snortengine, args=(alerts,)) - p1.start() - p2.start() + def snortengine(alerts): + suricata = SuricataEngine(capture_directory) + suricata.start_suricata() + alerts["suricata"] = suricata.get_alerts() - # Wait to their end. - p1.join() - p2.join() + # Start the engines. + p1 = Process(target=zeekengine, args=(alerts,)) + p2 = Process(target=snortengine, args=(alerts,)) + p1.start() + p2.start() - # 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=(',', ': '))) + # 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() - # Generate the report - report = Report(capture_directory) - report.generate_report() - else: - print("The directory doesn't exist.") else: - print("Please specify a capture directory in argument.") + 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) + else: + usage() + + else: + usage() + + + diff --git a/analysis/classes/report.py b/analysis/classes/report.py index 9146bb2..8d22680 100644 --- a/analysis/classes/report.py +++ b/analysis/classes/report.py @@ -13,7 +13,7 @@ from utils import get_config class Report(object): - def __init__(self, capture_directory): + def __init__(self, capture_directory, frontend): self.capture_directory = capture_directory self.alerts = self.read_json(os.path.join( capture_directory, "assets/alerts.json")) @@ -21,10 +21,13 @@ class Report(object): capture_directory, "assets/whitelist.json")) self.conns = self.read_json(os.path.join( capture_directory, "assets/conns.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")) + 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")) try: with open(os.path.join(self.capture_directory, "capture.pcap"), "rb") as f: self.capture_sha1 = hashlib.sha1(f.read()).hexdigest() @@ -204,16 +207,18 @@ class Report(object): """ header = "
" header += "
" - header += "


{}: {}
".format(self.template["device_name"], + if self.device is not None: + header += "


{}: {}
".format(self.template["device_name"], self.device["name"]) - header += "{}: {}
".format(self.template["device_mac"], + header += "{}: {}
".format(self.template["device_mac"], self.device["mac_address"]) header += "{} {}
".format(self.template["report_generated_on"], datetime.now().strftime("%d/%m/%Y - %H:%M:%S")) - header += "{}: {}s
".format(self.template["capture_duration"], - self.capinfos["Capture duration"]) - header += "{}: {}
".format(self.template["packets_number"], - self.capinfos["Number of packets"]) + if self.capinfos is not None: + header += "{}: {}s
".format(self.template["capture_duration"], + self.capinfos["Capture duration"]) + header += "{}: {}
".format(self.template["packets_number"], + self.capinfos["Number of packets"]) header += "{}: {}
".format( self.template["capture_sha1"], self.capture_sha1) header += "

" diff --git a/analysis/classes/zeekengine.py b/analysis/classes/zeekengine.py index 25eef4b..5ec3e07 100644 --- a/analysis/classes/zeekengine.py +++ b/analysis/classes/zeekengine.py @@ -236,6 +236,7 @@ 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] @@ -247,6 +248,7 @@ class ZeekEngine(object): "host": c["resolution"], "level": "Moderate", "id": "ACT-02"}) + except: pass @@ -443,11 +445,10 @@ class ZeekEngine(object): """ Start zeek and check the logs. """ - sp.Popen("cd {} && /opt/zeek/bin/zeek -Cr capture.pcap protocols/ssl/validate-certs".format( + sp.Popen("cd {} && zeek -Cr capture.pcap protocols/ssl/validate-certs".format( 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/") diff --git a/analysis/utils.py b/analysis/utils.py index 4ece0ee..be2196d 100644 --- a/analysis/utils.py +++ b/analysis/utils.py @@ -10,7 +10,7 @@ import os from functools import reduce # I'm not going to use an ORM for that. -parent = "/".join(sys.path[0].split("/")[:-1]) +parent = os.path.split(os.path.dirname(os.path.abspath(sys.argv[0])))[0] conn = sqlite3.connect(os.path.join(parent, "tinycheck.sqlite3")) cursor = conn.cursor() diff --git a/server/frontend/app/classes/analysis.py b/server/frontend/app/classes/analysis.py index ef1289e..71f0f64 100644 --- a/server/frontend/app/classes/analysis.py +++ b/server/frontend/app/classes/analysis.py @@ -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), "/tmp/{}".format(self.token)]) + [sys.executable, "{}/analysis/analysis.py".format(parent), "-f", "/tmp/{}".format(self.token)]) return {"status": True, "message": "Analysis started", "token": self.token} From 1c381f5b392f41f2912b47a7e02a8f2dd45cdcfc Mon Sep 17 00:00:00 2001 From: chebatory Date: Tue, 26 Oct 2021 06:34:27 -0400 Subject: [PATCH 06/12] fixing supply-chain attack vulnerability --- app/backend/package-lock.json | 2 +- app/backend/package.json | 2 +- app/frontend/package-lock.json | 2 +- app/frontend/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/backend/package-lock.json b/app/backend/package-lock.json index af8b754..f230eac 100644 --- a/app/backend/package-lock.json +++ b/app/backend/package-lock.json @@ -1,5 +1,5 @@ { - "name": "tinycheck-backend", + "name": "@kaspersky/tinycheck-backend", "version": "0.1.0", "lockfileVersion": 1, "requires": true, diff --git a/app/backend/package.json b/app/backend/package.json index d884f31..32c7432 100644 --- a/app/backend/package.json +++ b/app/backend/package.json @@ -1,5 +1,5 @@ { - "name": "tinycheck-backend", + "name": "@kaspersky/tinycheck-backend", "version": "0.1.0", "private": true, "scripts": { diff --git a/app/frontend/package-lock.json b/app/frontend/package-lock.json index f893086..3135948 100644 --- a/app/frontend/package-lock.json +++ b/app/frontend/package-lock.json @@ -1,5 +1,5 @@ { - "name": "tinycheck-new", + "name": "@kaspersky/tinycheck-new", "version": "0.1.0", "lockfileVersion": 1, "requires": true, diff --git a/app/frontend/package.json b/app/frontend/package.json index 126b0c8..aa2f3a0 100644 --- a/app/frontend/package.json +++ b/app/frontend/package.json @@ -1,5 +1,5 @@ { - "name": "tinycheck-new", + "name": "@kaspersky/tinycheck-new", "version": "0.1.0", "private": true, "scripts": { From 59c5f8a45b5c54fef997f380169b17ff147da4a3 Mon Sep 17 00:00:00 2001 From: chebatory Date: Tue, 26 Oct 2021 07:19:43 -0400 Subject: [PATCH 07/12] fixing supply-chain attack vulnerability --- app/backend/package.json | 1 - app/frontend/package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/app/backend/package.json b/app/backend/package.json index 32c7432..cae41be 100644 --- a/app/backend/package.json +++ b/app/backend/package.json @@ -1,7 +1,6 @@ { "name": "@kaspersky/tinycheck-backend", "version": "0.1.0", - "private": true, "scripts": { "serve": "vue-cli-service serve --copy --port=4201", "build": "vue-cli-service build", diff --git a/app/frontend/package.json b/app/frontend/package.json index aa2f3a0..99a3698 100644 --- a/app/frontend/package.json +++ b/app/frontend/package.json @@ -1,7 +1,6 @@ { "name": "@kaspersky/tinycheck-new", "version": "0.1.0", - "private": true, "scripts": { "serve": "vue-cli-service serve --copy --port=4202", "build": "vue-cli-service build", From ca980a74a3a4894485ed6782ee02c89d39058d8e Mon Sep 17 00:00:00 2001 From: PlazzmiK Date: Wed, 16 Feb 2022 20:14:51 +0100 Subject: [PATCH 08/12] Added six - Fixes issue #93 FIX for issue #93 : ImportError: cannot import name 'collections_abc' from 'six.moves' (unkown location) --- assets/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/requirements.txt b/assets/requirements.txt index a6affc2..11813be 100644 --- a/assets/requirements.txt +++ b/assets/requirements.txt @@ -16,3 +16,4 @@ qrcode netifaces weasyprint python-whois +six From bc98fa5d92f2080c2c0d5566ea45d1d2d25f78f1 Mon Sep 17 00:00:00 2001 From: tek Date: Tue, 12 Apr 2022 10:39:30 +0200 Subject: [PATCH 09/12] Updates IOC path --- config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.yaml b/config.yaml index 2c87b8d..8bf89c2 100644 --- a/config.yaml +++ b/config.yaml @@ -52,7 +52,7 @@ frontend: user_lang: userlang update: updateoption choose_net: false - + # NETWORK - # Some elements related to the network configuration, such as # the interfaces (updated during the install), the list of SSIDs @@ -77,6 +77,6 @@ network: watchers: iocs: - https://raw.githubusercontent.com/KasperskyLab/TinyCheck/main/assets/iocs.json - - https://raw.githubusercontent.com/Te-k/stalkerware-indicators/master/indicators-for-tinycheck.json + - https://raw.githubusercontent.com/Te-k/stalkerware-indicators/master/generated/indicators-for-tinycheck.json whitelists: - https://raw.githubusercontent.com/KasperskyLab/TinyCheck/main/assets/whitelist.json From b2bbb89baaf2d0d8bfba680edb74f49e053dffc7 Mon Sep 17 00:00:00 2001 From: Julio Poveda Date: Fri, 22 Apr 2022 15:26:34 -0400 Subject: [PATCH 10/12] Minor typo fix and pronoun change suggestion --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 01ffa2a..b8f6a4b 100644 --- a/README.md +++ b/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 his 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 their own extended Indicators of Compromise via a backend in order to detect some ghosts over the wire.

If you need more documentation on how to install it, use it and the internals, don't hesitate to take a look at the TinyCheck Wiki.

-

If you have any question about the projet, want to contribute or just send your feedback,
don't hesitate to contact us at tinycheck[@]kaspersky[.]com.

+

If you have any question about the project, want to contribute or just send your feedback,
don't hesitate to contact us at tinycheck[@]kaspersky[.]com.

### Use cases From 9bdcaf92cdcd0263914d1ec071db05e9bfe67218 Mon Sep 17 00:00:00 2001 From: felixaime Date: Sat, 4 Jun 2022 22:07:39 +0200 Subject: [PATCH 11/12] Adding iptables install for new Raspbian OS --- install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index f3f8bc7..8058a9e 100644 --- a/install.sh +++ b/install.sh @@ -264,7 +264,7 @@ change_hostname() { install_package() { # Install associated packages by using aptitude. - if [[ $1 == "dnsmasq" || $1 == "hostapd" || $1 == "tshark" || $1 == "sqlite3" || $1 == "suricata" || $1 == "unclutter" ]]; then + if [[ $1 == "dnsmasq" || $1 == "hostapd" || $1 == "tshark" || $1 == "sqlite3" || $1 == "suricata" || $1 == "unclutter" || $1 == "iptables" ]]; then apt-get install $1 -y elif [[ $1 == "zeek" ]]; then distrib=$(cat /etc/os-release | grep -E "^ID=" | cut -d"=" -f2) @@ -303,7 +303,8 @@ check_dependencies() { "/usr/bin/suricata" "/usr/bin/unclutter" "/usr/bin/sqlite3" - "/usr/bin/pip") + "/usr/bin/pip" + "/usr/sbin/iptables") echo -e "\e[39m[+] Checking dependencies...\e[39m" for bin in "${bins[@]}" From ab3f5b584e8d1d23650f638948f28734e62b4e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Aim=C3=A9?= Date: Sat, 4 Jun 2022 23:14:43 +0200 Subject: [PATCH 12/12] Update zeekengine.py --- analysis/classes/zeekengine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analysis/classes/zeekengine.py b/analysis/classes/zeekengine.py index 5ec3e07..2894f11 100644 --- a/analysis/classes/zeekengine.py +++ b/analysis/classes/zeekengine.py @@ -445,7 +445,7 @@ class ZeekEngine(object): """ Start zeek and check the logs. """ - sp.Popen("cd {} && zeek -Cr capture.pcap protocols/ssl/validate-certs".format( + sp.Popen("cd {} && /opt/zeek/bin/zeek -Cr capture.pcap protocols/ssl/validate-certs".format( self.working_dir), shell=True).wait() sp.Popen("cd {} && mv *.log assets/".format(self.working_dir), shell=True).wait()