mirror of https://github.com/mvt-project/mvt.git
Compare commits
5 Commits
4da5d25409
...
ef5cc5b579
Author | SHA1 | Date |
---|---|---|
Nikolaos Tosis | ef5cc5b579 | |
github-actions[bot] | da743a2878 | |
Rory Flynn | 4681b57adc | |
Rory Flynn | bb7a22ed0b | |
Nikolaos Tosis | 75f956de0b |
|
@ -7,11 +7,27 @@ Before proceeding, please note that MVT requires Python 3.6+ to run. While it sh
|
|||
First install some basic dependencies that will be necessary to build all required tools:
|
||||
|
||||
```bash
|
||||
sudo apt install python3 python3-pip libusb-1.0-0 sqlite3
|
||||
sudo apt install python3 python3-venv python3-pip sqlite3 libusb-1.0-0
|
||||
```
|
||||
|
||||
*libusb-1.0-0* is not required if you intend to only use `mvt-ios` and not `mvt-android`.
|
||||
|
||||
(Recommended) Set up `pipx`
|
||||
|
||||
For Ubuntu 23.04 or above:
|
||||
```bash
|
||||
sudo apt install pipx
|
||||
pipx ensurepath
|
||||
```
|
||||
|
||||
For Ubuntu 22.04 or below:
|
||||
```
|
||||
python3 -m pip install --user pipx
|
||||
python3 -m pipx ensurepath
|
||||
```
|
||||
|
||||
Other distributions: check for a `pipx` or `python-pipx` via your package manager.
|
||||
|
||||
When working with Android devices you should additionally install [Android SDK Platform Tools](https://developer.android.com/studio/releases/platform-tools). If you prefer to install a package made available by your distribution of choice, please make sure the version is recent to ensure compatibility with modern Android devices.
|
||||
|
||||
## Dependencies on macOS
|
||||
|
@ -21,7 +37,7 @@ Running MVT on macOS requires Xcode and [homebrew](https://brew.sh) to be instal
|
|||
In order to install dependencies use:
|
||||
|
||||
```bash
|
||||
brew install python3 libusb sqlite3
|
||||
brew install python3 pipx libusb sqlite3
|
||||
```
|
||||
|
||||
*libusb* is not required if you intend to only use `mvt-ios` and not `mvt-android`.
|
||||
|
@ -42,24 +58,43 @@ It is recommended to try installing and running MVT from [Windows Subsystem Linu
|
|||
|
||||
## Installing MVT
|
||||
|
||||
If you haven't done so, you can add this to your `.bashrc` or `.zshrc` file in order to add locally installed PyPI binaries to your `$PATH`:
|
||||
### Installing from PyPI with pipx (recommended)
|
||||
1. Install `pipx` following the instructions above for your OS/distribution. Make sure to run `pipx ensurepath` and open a new terminal window.
|
||||
2. ```bash
|
||||
pipx install mvt
|
||||
```
|
||||
|
||||
You now should have the `mvt-ios` and `mvt-android` utilities installed. If you run into problems with these commands not being found, ensure you have run `pipx ensurepath` and opened a new terminal window.
|
||||
|
||||
### Installing from PyPI directly into a virtual environment
|
||||
You can use `pipenv`, `poetry` etc. for your virtual environment, but the provided example is with the built-in `venv` tool:
|
||||
|
||||
1. Create the virtual environment in a folder in the current directory named `env`:
|
||||
```bash
|
||||
export PATH=$PATH:~/.local/bin
|
||||
python3 -m venv env
|
||||
```
|
||||
|
||||
Then you can install MVT directly from [PyPI](https://pypi.org/project/mvt/)
|
||||
|
||||
2. Activate the virtual environment:
|
||||
```bash
|
||||
pip3 install mvt
|
||||
source env/bin/activate
|
||||
```
|
||||
|
||||
If you want to have the latest features in development, you can install MVT directly from the source code. If you installed MVT previously from pypi, you should first uninstall it using `pip3 uninstall mvt` and then install from the source code:
|
||||
3. Install `mvt` into the virtual environment:
|
||||
```bash
|
||||
pip install mvt
|
||||
```
|
||||
|
||||
The `mvt-ios` and `mvt-android` utilities should now be available as commands whenever the virtual environment is active.
|
||||
|
||||
### Installing from git source with pipx
|
||||
If you want to have the latest features in development, you can install MVT directly from the source code in git.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/mvt-project/mvt.git
|
||||
cd mvt
|
||||
pip3 install .
|
||||
pipx install --force git+https://github.com/mvt-project/mvt.git
|
||||
```
|
||||
|
||||
You now should have the `mvt-ios` and `mvt-android` utilities installed.
|
||||
|
||||
**Notes:**
|
||||
1. The `--force` flag is necessary to force the reinstallation of the package.
|
||||
2. To revert to using a PyPI version, it will be necessary to `pipx uninstall mvt` first.
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
from tkinter import *
|
||||
from tkinter.ttk import Treeview,Combobox,Style
|
||||
from tkinter import filedialog,messagebox
|
||||
import pip._internal as pip
|
||||
import platform
|
||||
import sys,os
|
||||
import logging,requests
|
||||
|
||||
from rich.logging import RichHandler
|
||||
#mvt module imports, if not exit install it
|
||||
try:
|
||||
from mvt.ios.decrypt import DecryptBackup
|
||||
from mvt.ios.modules.backup import BACKUP_MODULES
|
||||
from mvt.ios.modules.fs import FS_MODULES
|
||||
from mvt.ios.modules.mixed import MIXED_MODULES
|
||||
from mvt.common.module import run_module,save_timeline
|
||||
from mvt.common.indicators import Indicators, IndicatorsFileBadFormat
|
||||
except ImportError:
|
||||
pip.main(['install', 'mvt'])
|
||||
from mvt.ios.decrypt import DecryptBackup
|
||||
from mvt.ios.modules.backup import BACKUP_MODULES
|
||||
from mvt.ios.modules.fs import FS_MODULES
|
||||
from mvt.ios.modules.mixed import MIXED_MODULES
|
||||
from mvt.common.indicators import Indicators, IndicatorsFileBadFormat
|
||||
from mvt.common.module import run_module,save_timeline
|
||||
|
||||
# Setup logging using Rich.
|
||||
LOG_FORMAT = "[%(name)s] %(message)s"
|
||||
logging.basicConfig(level="INFO", format=LOG_FORMAT, handlers=[
|
||||
RichHandler(show_path=False, log_time_format="%X")])
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# If is windows, use the default path as default
|
||||
backupFolder:str =''
|
||||
if platform.system()=='Windows':
|
||||
backupFolder = os.path.expanduser('~\Apple') + '\MobileSync\Backup'
|
||||
elif platform.system()=='Darwin':
|
||||
backupFolder = 'C:\\'
|
||||
else:
|
||||
sys.exit("Operating system is not compatible")
|
||||
|
||||
#use the correct slashes according to OS
|
||||
decryptedFolder:str =''
|
||||
resultsFolder:str =''
|
||||
if platform.system()=='Windows':
|
||||
decryptedFolder = 'C:/'
|
||||
elif platform.system()=='Darwin':
|
||||
decryptedFolder = 'C:\\'
|
||||
else:
|
||||
sys.exit("Operating system is not compatible")
|
||||
#function to download automatic the STIX files
|
||||
def dowload_STIX(output):
|
||||
pegasus = 'https://raw.githubusercontent.com/AmnestyTech/investigations/master/2021-07-18_nso/pegasus.stix2'
|
||||
predator = 'https://raw.githubusercontent.com/AmnestyTech/investigations/master/2021-12-16_cytrox/cytrox.stix2'
|
||||
r1 = requests.get(pegasus, allow_redirects=False)
|
||||
r2 = requests.get(predator, allow_redirects=False)
|
||||
h = open(output + 'pegasus.stix2', 'wb').write(r1.content)
|
||||
h = open(output +'cytrox.stix2', 'wb').write(r2.content)
|
||||
return [output + 'pegasus.stix2',output +'cytrox.stix2']
|
||||
|
||||
def browseFileAction(event,obj, id):
|
||||
global backupFolder,decryptedFolder,resultsFolder
|
||||
searchPathDirectory = filedialog.askdirectory(title="select", initialdir=backupFolder)
|
||||
#update the path in the label's text
|
||||
obj["text"] = searchPathDirectory
|
||||
#update the #Global variable
|
||||
if id=="INPUT":
|
||||
backupFolder = searchPathDirectory
|
||||
else :
|
||||
#set output and results folder
|
||||
decryptedFolder = searchPathDirectory
|
||||
if platform.system()=='Windows':
|
||||
resultsFolder = decryptedFolder + '/Results/'
|
||||
elif platform.system()=='Darwin':
|
||||
resultsFolder = decryptedFolder + '\Results\\'
|
||||
else:
|
||||
sys.exit("Operating system is not compatible")
|
||||
|
||||
#use underscore to avoid conflict
|
||||
def check_backup__(output, backup_path,iocs):
|
||||
|
||||
log.info("Checking iTunes backup located at: %s", backup_path)
|
||||
|
||||
if output and not os.path.exists(output):
|
||||
try:
|
||||
os.makedirs(output)
|
||||
except Exception as e:
|
||||
log.critical("Unable to create output folder %s: %s", output, e)
|
||||
os.exit(1)
|
||||
|
||||
indicators = Indicators(log=log)
|
||||
for ioc_path in iocs:
|
||||
try:
|
||||
indicators.parse_stix2(ioc_path)
|
||||
except IndicatorsFileBadFormat as e:
|
||||
log.critical(e)
|
||||
os.exit(1)
|
||||
log.info("Loaded a total of %d indicators", indicators.ioc_count)
|
||||
|
||||
timeline = []
|
||||
timeline_detected = []
|
||||
for backup_module in BACKUP_MODULES + MIXED_MODULES:
|
||||
|
||||
m = backup_module(base_folder=backup_path, output_folder=output, fast_mode=False,
|
||||
log=logging.getLogger(backup_module.__module__))
|
||||
m.is_backup = True
|
||||
|
||||
if iocs:
|
||||
m.indicators = indicators
|
||||
m.indicators.log = m.log
|
||||
|
||||
run_module(m)
|
||||
timeline.extend(m.timeline)
|
||||
timeline_detected.extend(m.timeline_detected)
|
||||
|
||||
if output:
|
||||
if len(timeline) > 0:
|
||||
save_timeline(timeline, os.path.join(output, "timeline.csv"))
|
||||
if len(timeline_detected) > 0:
|
||||
save_timeline(timeline_detected, os.path.join(output, "timeline_detected.csv"))
|
||||
log.info("Checking completed")
|
||||
|
||||
def run(event,arg, id):
|
||||
global backupFolder,decryptedFolder,resultsFolder
|
||||
password = arg.get()
|
||||
if len(password)==0:
|
||||
messagebox.showerror("Password required", "Please enter first your backup password.")
|
||||
return
|
||||
|
||||
backup = DecryptBackup(backupFolder, decryptedFolder)
|
||||
backup.decrypt_with_password(password)
|
||||
#start decryption
|
||||
backup.process_backup()
|
||||
l = dowload_STIX(resultsFolder)
|
||||
#check the stored links against the STIX
|
||||
check_backup__(resultsFolder,decryptedFolder,l)
|
||||
|
||||
|
||||
app = Tk()
|
||||
#Create first frame for some infos
|
||||
frameInfo = LabelFrame(app, text="Info",pady=5)
|
||||
frameInfo.grid(row=0, column=0,padx=5,sticky=W)
|
||||
lblRootFolder = Label(frameInfo, text='Select backup folder: ',
|
||||
font=('normal', 11),padx=10)
|
||||
lblRootFolder.grid(row=0, column=0)
|
||||
lblRootFolderName = Label(frameInfo, text=backupFolder,
|
||||
font=('normal', 11),width=40,wraplength=280)
|
||||
lblRootFolderName.grid(row=0, column=1)
|
||||
browseButton = Button(frameInfo, text = 'Browse', width = 14)
|
||||
browseButton.grid(row=0, column=2,pady=4)
|
||||
outputFolder = Button(frameInfo, text='Browse', width=14)
|
||||
outputFolder.grid(row=1, column=2,padx=4)
|
||||
lblSessionStatus = Label(frameInfo, text='Browse decrypted path: ',
|
||||
font=('normal', 11))
|
||||
lblSessionStatus.grid(row=1, column=0,padx=10)
|
||||
lblSessionStatusMessage = Label(frameInfo, text=decryptedFolder,
|
||||
font=('normal', 11),width=40,wraplength=280)
|
||||
lblSessionStatusMessage.grid(row=1, column=1)
|
||||
|
||||
labelEnterPassword = Label(frameInfo, text='Enter your backup password: ',
|
||||
font=('normal', 11),padx=10)
|
||||
labelEnterPassword.grid(row=3, column=0)
|
||||
|
||||
passwordEnrty = Entry(frameInfo, show="*", width=15)
|
||||
passwordEnrty.grid(row=3, column=2,padx=4)
|
||||
|
||||
|
||||
runButton = Button(frameInfo,text='Run analysis', width=14)
|
||||
runButton.grid(row=4, column=0,padx=4)
|
||||
|
||||
# Create bind
|
||||
#connect browse button with the action function.
|
||||
browseButton.bind("<ButtonRelease-1>",lambda event, objToLabel=lblRootFolderName: browseFileAction(event,objToLabel, 'INPUT') )
|
||||
outputFolder.bind("<ButtonRelease-1>",lambda event, objToLabel=lblSessionStatusMessage: browseFileAction(event,objToLabel, 'OUTPUT') )
|
||||
runButton.bind("<ButtonRelease-1>",lambda event, objToLabel=passwordEnrty: run(event,objToLabel, 'buttonRelease1') )
|
||||
|
||||
#Create and call main window
|
||||
app.title('Predator/Pegasus iOS scanner GUI')
|
||||
app.geometry('720x150')
|
||||
# Add Some Style
|
||||
style = Style()
|
||||
|
||||
|
||||
# Pick A Theme
|
||||
style.theme_use('default')
|
||||
|
||||
# Main window
|
||||
#add some action before closing window.
|
||||
def on_closing():
|
||||
app.destroy()
|
||||
app.protocol("WM_DELETE_WINDOW",on_closing)
|
||||
# Start program
|
||||
app.mainloop()
|
|
@ -1027,5 +1027,9 @@
|
|||
{
|
||||
"version": "17.3",
|
||||
"build": "21D50"
|
||||
},
|
||||
{
|
||||
"version": "17.3.1",
|
||||
"build": "21D61"
|
||||
}
|
||||
]
|
|
@ -69,9 +69,9 @@ class Calls(IOSExtraction):
|
|||
"isodate": convert_mactime_to_iso(row[0]),
|
||||
"duration": row[1],
|
||||
"location": row[2],
|
||||
"number": row[3].decode("utf-8")
|
||||
if row[3] and row[3] is bytes
|
||||
else row[3],
|
||||
"number": (
|
||||
row[3].decode("utf-8") if row[3] and row[3] is bytes else row[3]
|
||||
),
|
||||
"provider": row[4],
|
||||
}
|
||||
)
|
||||
|
|
|
@ -56,7 +56,9 @@ class SMSAttachments(IOSExtraction):
|
|||
def check_indicators(self) -> None:
|
||||
for attachment in self.results:
|
||||
# Check for known malicious filenames.
|
||||
if self.indicators.check_file_path(attachment["filename"]):
|
||||
if self.indicators and self.indicators.check_file_path(
|
||||
attachment["filename"]
|
||||
):
|
||||
self.detected.append(attachment)
|
||||
|
||||
if (
|
||||
|
|
Loading…
Reference in New Issue