Compare commits

...

12 Commits

Author SHA1 Message Date
FORCHA PEARL 85d7d96383
Merge 4b89887f4b into cbd41b2aff 2024-04-23 22:53:30 +02:00
Rory Flynn cbd41b2aff
Mark 2.5.3 release (#490) 2024-04-19 17:23:55 +02:00
Rory Flynn 0509eaa162
Use backwards-compatible datetime.timezone.utc (#488) 2024-04-19 17:22:10 +02:00
Rory Flynn 59e6dff1e1
Fail builds on test failure (#489)
* Fail builds on test failure

* Deliberately fail a build to test

* Revert "Deliberately fail a build to test"

This reverts commit 666140a954.
2024-04-19 17:18:27 +02:00
Rory Flynn f1821d1a02
Mark release 2.5.2 (#486) 2024-04-18 16:53:41 +02:00
Rory Flynn 6c7ad0ac95
Convert timezone-aware datetimes automatically to UTC (#485) 2024-04-18 16:49:30 +02:00
tek 3a997d30d2 Updates SMS module to highlight new text of Apple notifications 2024-04-15 23:28:36 +02:00
tek 6f56939dd7 Requires latest cryptography version 2024-04-15 22:41:01 +02:00
Donncha Ó Cearbhaill 7a4946e2c6
Mark release 2.5.1 (#481)
Signed-off-by: Donncha Ó Cearbhaill <donncha.ocearbhaill@amnesty.org>
2024-04-11 11:14:42 +02:00
r-tx e1c4f4eb7a
Add more short urls (#479)
Co-authored-by: r-tx <r-tx@users.noreply.github.com>
2024-04-11 11:08:15 +02:00
Donncha Ó Cearbhaill f9d7b550dc
Add docs explaining how to seek expert help for forensic analysis (#476)
* Update forensic support links in the documentation

* Add expert help message to MVT output

* Add warning to disable ADB after an Android acquisition

* Include Developer Options in the ADB warning text
2024-04-08 18:47:59 +02:00
FORCHA 4b89887f4b
Fixed docker warnings
Removed empty lines within RUN command
2023-08-04 18:29:46 +01:00
11 changed files with 146 additions and 49 deletions

View File

@ -40,7 +40,9 @@ jobs:
- name: Safety checks
run: safety check
- name: Test with pytest and coverage
run: pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=mvt tests/ | tee pytest-coverage.txt
run: |
set -o pipefail
pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=mvt tests/ | tee pytest-coverage.txt
- name: Pytest coverage comment
continue-on-error: true # Workflows running on a fork can't post comments
uses: MishaKav/pytest-coverage-comment@main

View File

@ -13,7 +13,6 @@ ENV DEBIAN_FRONTEND=noninteractive
# ----------------------------
RUN apt update \
&& apt install -y python3 python3-pip libusb-1.0-0-dev wget unzip default-jre-headless adb \
# Install build tools for libimobiledevice
# ----------------------------------------
build-essential \
@ -27,7 +26,6 @@ RUN apt update \
libssl-dev \
sqlite3 \
pkg-config \
# Clean up
# --------
&& apt-get clean \
@ -41,17 +39,11 @@ RUN git clone https://github.com/libimobiledevice/libplist \
&& git clone https://github.com/libimobiledevice/libusbmuxd \
&& git clone https://github.com/libimobiledevice/libimobiledevice \
&& git clone https://github.com/libimobiledevice/usbmuxd \
&& cd libplist && ./autogen.sh && make && make install && ldconfig \
&& cd ../libimobiledevice-glue && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh --prefix=/usr && make && make install && ldconfig \
&& cd ../libusbmuxd && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh && make && make install && ldconfig \
&& cd ../libimobiledevice && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh --enable-debug && make && make install && ldconfig \
&& cd ../usbmuxd && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --runstatedir=/run && make && make install \
# Clean up.
&& cd .. && rm -rf libplist libimobiledevice-glue libusbmuxd libimobiledevice usbmuxd

View File

@ -26,7 +26,7 @@ MVT supports using public [indicators of compromise (IOCs)](https://github.com/m
>
> Reliable and comprehensive digital forensic support and triage requires access to non-public indicators, research and threat intelligence.
>
>Such support is available to civil society through [Amnesty International's Security Lab](https://www.amnesty.org/en/tech/) or through our forensic partnership with [Access Nows Digital Security Helpline](https://www.accessnow.org/help/).
>Such support is available to civil society through [Amnesty International's Security Lab](https://securitylab.amnesty.org/get-help/?c=mvt_docs) or through our forensic partnership with [Access Nows Digital Security Helpline](https://www.accessnow.org/help/).
More information about using indicators of compromise with MVT is available in the [documentation](https://docs.mvt.re/en/latest/iocs/).

View File

@ -21,7 +21,7 @@ MVT supports using [indicators of compromise (IOCs)](https://github.com/mvt-proj
Reliable and comprehensive digital forensic support and triage requires access to non-public indicators, research and threat intelligence.
Such support is available to civil society through [Amnesty International's Security Lab](https://securitylab.amnesty.org/contact-us/) or [Access Nows Digital Security Helpline](https://www.accessnow.org/help/).
Such support is available to civil society through [Amnesty International's Security Lab](https://securitylab.amnesty.org/get-help/?c=mvt_docs) or [Access Nows Digital Security Helpline](https://www.accessnow.org/help/).
More information about using indicators of compromise with MVT is available in the [documentation](iocs.md).

View File

@ -160,6 +160,27 @@ class Command:
def finish(self) -> None:
raise NotImplementedError
def _show_disable_adb_warning(self) -> None:
"""Warn if ADB is enabled"""
if type(self).__name__ in ["CmdAndroidCheckADB", "CmdAndroidCheckAndroidQF"]:
self.log.info(
"Please disable Developer Options and ADB (Android Debug Bridge) on the device once finished with the acquisition. "
"ADB is a powerful tool which can allow unauthorized access to the device."
)
def _show_support_message(self) -> None:
support_message = "Please seek reputable expert help if you have serious concerns about a possible spyware attack. Such support is available to human rights defenders and civil society through Amnesty International's Security Lab at https://securitylab.amnesty.org/get-help/?c=mvt"
if self.detected_count == 0:
self.log.info(
f"[bold]NOTE:[/bold] Using MVT with public indicators of compromise (IOCs) [bold]WILL NOT[/bold] automatically detect advanced attacks.\n\n{support_message}",
extra={"markup": True},
)
else:
self.log.warning(
f"[bold]NOTE: Detected indicators of compromise[/bold]. Only expert review can confirm if the detected indicators are signs of an attack.\n\n{support_message}",
extra={"markup": True},
)
def run(self) -> None:
try:
self.init()
@ -208,3 +229,6 @@ class Command:
self._store_timeline()
self._store_info()
self._show_disable_adb_warning()
self._show_support_message()

View File

@ -9,47 +9,65 @@ import requests
from tld import get_tld
SHORTENER_DOMAINS = [
"0rz.tw",
"1drv.ms",
"1link.in",
"1url.com",
"2big.at",
"2.gp",
"2pl.us",
"2tu.us",
"2ya.com",
"3.ly",
"4sq.com",
"4url.cc",
"6url.com",
"a.gg",
"a.nf",
"7.ly",
"a2a.me",
"abbrr.com",
"adf.ly",
"adjix.com",
"a.gg",
"alturl.com",
"a.nf",
"anon.to",
"apple.news",
"atu.ca",
"b23.ru",
"bacn.me",
"bc.vc",
"bfy.tw",
"binged.it",
"bit.do",
"bit.ly",
"bizj.us",
"bkite.com",
"bloat.me",
"budurl.com",
"buff.ly",
"buk.me",
"burnurl.com",
"c-o.in",
"chilp.it",
"chn.ge",
"clck.ru",
"cli.gs",
"clickmeter.com",
"cli.gs",
"c-o.in",
"cort.as",
"cut.ly",
"cutt.ly",
"cuturl.com",
"decenturl.com",
"dai.ly",
"dailym.ai",
"db.tt",
"decenturl.com",
"dfl8.me",
"digbig.com",
"digg.com",
"disq.us",
"dlvr.it",
"doiop.com",
"do.my",
"dwarfurl.com",
"dy.fi",
"easyuri.com",
@ -58,27 +76,35 @@ SHORTENER_DOMAINS = [
"esyurl.com",
"ewerl.com",
"fa.b",
"ff.im",
"fa.by",
"fb.me",
"fff.to",
"ff.im",
"fhurl.com",
"fire.to",
"firsturl.de",
"firsturl.net",
"flic.kr",
"flq.us",
"fly2.ws",
"fon.gs",
"forms.gle",
"fwd4.me",
"gdurl.com",
"gg.gg",
"gl.am",
"go.9nl.com",
"go2.me",
"go2cut.com",
"go2.me",
"go.9nl.com",
"goo.gl",
"goshrink.com",
"got.by",
"gowat.ch",
"gri.ms",
"gurl.es",
"hellotxt.com",
"hex.io",
"hongkiat.shorturl.com",
"hover.com",
"href.in",
"ht.ly",
@ -87,13 +113,15 @@ SHORTENER_DOMAINS = [
"hurl.it",
"hurl.me",
"hurl.ws",
"ibb.co",
"icanhaz.com",
"idek.net",
"inreply.to",
"is.gd",
"iscool.net",
"is.gd",
"iterasi.net",
"jijr.com",
"j.mp",
"jmp2.net",
"just.as",
"kissa.be",
@ -101,21 +129,23 @@ SHORTENER_DOMAINS = [
"klck.me",
"korta.nu",
"krunchd.com",
"lat.ms",
"liip.to",
"liltext.com",
"lin.cr",
"linkbee.com",
"linkbun.ch",
"liurl.cn",
"ln-s.net",
"ln-s.ru",
"lnkd.in",
"lnk.gd",
"lnk.in",
"lnkd.in",
"ln-s.net",
"ln-s.ru",
"loopt.us",
"lru.jp",
"lt.tl",
"lurl.no",
"lyhyt.eu",
"metamark.net",
"migre.me",
"minilien.com",
@ -123,52 +153,71 @@ SHORTENER_DOMAINS = [
"minurl.fr",
"moourl.com",
"myurl.in",
"nbcnews.to",
"ne1.net",
"njx.me",
"nn.nf",
"notlong.com",
"n.pr",
"nsfw.in",
"o-x.fr",
"nyti.ms",
"om.ly",
"onforb.es",
"on.mktw.net",
"ow.ly",
"o-x.fr",
"pca.st",
"pd.am",
"pic.gd",
"ping.fm",
"piurl.com",
"pnt.me",
"politi.co",
"poprl.com",
"post.ly",
"posted.at",
"post.ly",
"profile.to",
"q.gs",
"qicute.com",
"qlnk.net",
"qr.ae",
"qte.me",
"quip-art.com",
"rb6.me",
"rb.gy",
"read.bi",
"redir.ec",
"redirx.com",
"ri.ms",
"redr.me",
"reut.rs",
"rickroll.it",
"r.im",
"ri.ms",
"riz.gd",
"rsmonkey.com",
"ru.ly",
"rubyurl.com",
"ru.ly",
"s7y.us",
"safe.mn",
"sharein.com",
"sharetabs.com",
"shorl.com",
"short.ie",
"short.to",
"shortlinks.co.uk",
"shortna.me",
"short.to",
"shorturl.at",
"shorturl.com",
"shoturl.us",
"shout.to",
"shrinkify.com",
"shrinkster.com",
"shrt.st",
"shrten.com",
"shrt.st",
"shrunkin.com",
"shw.me",
"simurl.com",
"smsh.me",
"sn.im",
"snipr.com",
"snipurl.com",
@ -179,24 +228,30 @@ SHORTENER_DOMAINS = [
"starturl.com",
"sturly.com",
"su.pr",
"t.cn",
"t.co",
"tcrn.ch",
"tgr.ph",
"thrdl.es",
"tighturl.com",
"tiny.cc",
"tiny.pl",
"tiny123.com",
"tinyarro.ws",
"tiny.cc",
"tinylink.in",
"tiny.pl",
"tiny.tw",
"tinytw.it",
"tinyuri.ca",
"tinyurl.com",
"tinyvid.io",
"t.me",
"tnij.org",
"to.ly",
"tnw.to",
"togoto.us",
"to.ly",
"traceurl.com",
"tr.im",
"tr.my",
"traceurl.com",
"turo.us",
"tweetburner.com",
"twirl.at",
@ -206,49 +261,62 @@ SHORTENER_DOMAINS = [
"twiturl.de",
"twurl.cc",
"twurl.nl",
"u.mavrev.com",
"u.nu",
"u6e.de",
"ub0.cc",
"ukl.me.uk",
"u.mavrev.com",
"u.nu",
"updating.me",
"ur1.ca",
"url.co.uk",
"url.ie",
"url4.eu",
"urlao.com",
"urlbrief.com",
"url.co.uk",
"urlcover.com",
"urlcut.com",
"urlenco.de",
"urlhawk.com",
"url.ie",
"urlkiss.com",
"urlot.com",
"urlpire.com",
"urlx.ie",
"urlx.org",
"urlzen.com",
"use.my",
"u.to",
"v.gd",
"virl.com",
"vl.am",
"vurl.com",
"vzturl.com",
"w3t.org",
"wapo.st",
"wapurl.co.uk",
"wipi.es",
"wp.me",
"x.co",
"x.se",
"xaddr.com",
"x.co",
"xeeurl.com",
"xr.com",
"xrl.in",
"xrl.us",
"x.se",
"xurl.es",
"xurl.jp",
"xzb.cc",
"ye.pe",
"yep.it",
"yfrog.com",
"yhoo.it",
"ymlp.com",
"yuarel.com",
"yweb.com",
"zi.ma",
"zi.pe",
"zipmyurl.com",
"zurl.to",
"zurl.ws",
"zz.gd",
]

View File

@ -53,20 +53,20 @@ def convert_chrometime_to_datetime(timestamp: int) -> datetime.datetime:
def convert_datetime_to_iso(date_time: datetime.datetime) -> str:
"""Converts datetime to ISO string.
:param datetime: datetime.
:param datetime: datetime, naive or timezone aware
:type datetime: datetime.datetime
:returns: ISO datetime string in YYYY-mm-dd HH:MM:SS.ms format.
:rtype: str
"""
try:
return date_time.strftime("%Y-%m-%d %H:%M:%S.%f")
except Exception:
return ""
if date_time.tzinfo:
# Timezone aware object - convert to UTC
date_time = date_time.astimezone(tz=datetime.timezone.utc)
return date_time.strftime("%Y-%m-%d %H:%M:%S.%f")
def convert_unix_to_utc_datetime(
timestamp: Union[int, float, str]
timestamp: Union[int, float, str],
) -> datetime.datetime:
"""Converts a unix epoch timestamp to UTC datetime.

View File

@ -3,4 +3,4 @@
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
MVT_VERSION = "2.5.0"
MVT_VERSION = "2.5.3"

View File

@ -66,8 +66,11 @@ class SMS(IOSExtraction):
def check_indicators(self) -> None:
for message in self.results:
alert = "ALERT: State-sponsored attackers may be targeting your iPhone"
if message.get("text", "").startswith(alert):
alert_old = "ALERT: State-sponsored attackers may be targeting your iPhone"
alert_new = "ALERT: Apple detected a targeted mercenary spyware attack against your iPhone"
if message.get("text", "").startswith(alert_old) or message.get(
"text", ""
).startswith(alert_new):
self.log.warning(
"Apple warning about state-sponsored attack received on the %s",
message["isodate"],

View File

@ -31,7 +31,7 @@ install_requires =
iOSbackup >=0.9.923
adb-shell >=0.4.3
libusb1 >=3.0.0
cryptography >=38.0.1
cryptography >=42.0.5
pyyaml >=6.0
pyahocorasick >= 2.0.0

View File

@ -42,6 +42,14 @@ class TestDateConversions:
converted = convert_unix_to_utc_datetime(TEST_DATE_EPOCH)
assert convert_datetime_to_iso(converted) == TEST_DATE_ISO
def test_convert_timezone_aware_to_iso(self):
assert (
convert_datetime_to_iso(
datetime.strptime("2024-09-30 11:21:20+0200", "%Y-%m-%d %H:%M:%S%z")
)
== "2024-09-30 09:21:20.000000"
)
class TestHashes:
def test_hash_from_file(self):