194 lines
6.9 KiB
Bash
Executable File
194 lines
6.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Usage: ./extract-and-index-evolution-mail-storage <EWF image>
|
|
# or
|
|
# bash extract-and-index-evolution-mail-storage <EWF image>
|
|
# Description: (see scriptname!)
|
|
# Author: Patrick Neumann (patrick@neumannsland.de)
|
|
# Version: 0.9
|
|
# Date: 07.04.2019
|
|
# License: GPL3
|
|
# Warranty: This program is distributed WITHOUT ANY WARRANTY
|
|
# Todo: -
|
|
# Tests: Forensik Workstation(s):
|
|
# - macOS 10.13.6 + TSK 4.6.5 (Homebrew) + mu 1.0 (Homebrew)
|
|
# - ArchLinux + TSK 4.6.5 + mu 1.0 (AUR)
|
|
# Image(s):
|
|
# - VirtualBox VM with Linux Mint 19.1 + Evolution 3.28.5
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Simple checks for dependencies:
|
|
#-------------------------------------------------------------------------------
|
|
which ifind > /dev/null || ( echo "Please install sleuthkit!" ; exit 1 )
|
|
which mu > /dev/null || ( echo "Please install mu!" ; exit 1 )
|
|
|
|
#=== FUNCTION ==================================================================
|
|
# NAME: usage
|
|
# DESCRIPTION: Display help.
|
|
# PARAMETERS: -
|
|
#===============================================================================
|
|
usage () {
|
|
printf "Usage:\n"
|
|
printf " $0 <EWF image>\n"
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Check for one param:
|
|
#-------------------------------------------------------------------------------
|
|
if [ "${#}" -ne 1 ]; then
|
|
echo "Please provide exactly one parameter!"
|
|
usage
|
|
exit 2
|
|
fi
|
|
|
|
#=== CONFIGURATION (params) ====================================================
|
|
#-------------------------------------------------------------------------------
|
|
# Check and set image:
|
|
#-------------------------------------------------------------------------------
|
|
if ! file "${1}" | grep -F "EWF" > /dev/null ; then
|
|
echo "Please provide EWF image!"
|
|
usage
|
|
exit 3
|
|
fi
|
|
readonly IMG="${1}"
|
|
|
|
#=== CONFIGURATION (dynamic) ===================================================
|
|
#-------------------------------------------------------------------------------
|
|
# Find and set home directory:
|
|
#-------------------------------------------------------------------------------
|
|
echo "Searching for /home... please wait..."
|
|
|
|
PRE="./recovered"
|
|
offsets="$( mmls -aM "${IMG}" \
|
|
| awk '/^[[:digit:]]{3}:/ { print $3; }' \
|
|
| sed 's/0*//' )"
|
|
for offset in ${offsets} ; do
|
|
if fsstat -o "${offset}" "${IMG}" \
|
|
| grep -E "^Last mounted on: /home$" > /dev/null
|
|
then
|
|
home=""
|
|
readonly S_PRE=""
|
|
readonly T_PRE="${PRE}/home"
|
|
readonly OFS="${offset}"
|
|
elif fsstat -o "${offset}" "${IMG}" \
|
|
| grep -E "^Last mounted on: /$" > /dev/null
|
|
then
|
|
home="$( ifind -o "${offset}" -n "/home" "${IMG}" )"
|
|
if [ "${home}" != "File not found" ] ; then
|
|
readonly S_PRE="/home"
|
|
readonly T_PRE="${PRE}"
|
|
readonly OFS="${offset}"
|
|
fi
|
|
else
|
|
echo "/home not found!"
|
|
echo
|
|
echo "Hint:"
|
|
echo " 1.: assemble RAID and/or LVM"
|
|
echo " 2.: ewfacquire the file system of /home"
|
|
echo " 3.: use that image with ${0}"
|
|
exit 4
|
|
fi
|
|
done
|
|
|
|
#=== CONFIGURATION (user) ======================================================
|
|
#-------------------------------------------------------------------------------
|
|
# Select user:
|
|
#-------------------------------------------------------------------------------
|
|
printf "Please select a user:\n"
|
|
users="$( fls -o "${OFS}" "${IMG}" "${home}" | awk '{ print $NF; }' )"
|
|
select user in ${users} ; do
|
|
if [ -n "${user}" ] ; then
|
|
break
|
|
fi
|
|
done
|
|
|
|
#=== CONFIGURATION (static) ====================================================
|
|
readonly LOCAL=".local/share/evolution/mail"
|
|
readonly CACHE=".cache/evolution/mail"
|
|
DIRS="${S_PRE}/${user}/${LOCAL}/"
|
|
readonly DIRS="${DIRS} ${S_PRE}/${user}/${CACHE}/"
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# ... magic ...
|
|
#-------------------------------------------------------------------------------
|
|
echo "Extracting folders and files... please wait..."
|
|
|
|
for DIR in ${DIRS} ; do
|
|
#-----------------------------------------------------------------------------
|
|
# Store inode of DIR as start:
|
|
#-----------------------------------------------------------------------------
|
|
start="$( ifind -o "${OFS}" -n "${DIR}" "${IMG}" )"
|
|
|
|
if [ "${start}" = "File not found" ] ; then
|
|
echo "${DIR} not found!"
|
|
continue
|
|
fi
|
|
#-----------------------------------------------------------------------------
|
|
# Store directory paths recursively under start pipe separated as dirs:
|
|
#-----------------------------------------------------------------------------
|
|
dirs="$( fls -o "${OFS}" -u -D -r -m "${DIR}" "${IMG}" "${start}" )"
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Create target folder structure recursively:
|
|
#-----------------------------------------------------------------------------
|
|
IFS=$'\n'
|
|
for dir in ${dirs} ; do
|
|
path="$( echo "${dir}" | cut -d "|" -f 2 )"
|
|
|
|
# the subfolder between "cur" and the mail kill the RFC
|
|
if ! echo "${path}" \
|
|
| grep -E ".*/(cur|new|tmp)/.*" > /dev/null
|
|
then
|
|
if ! [ -d "${T_PRE}${path}" ] ; then
|
|
mkdir -p "${T_PRE}${path}"
|
|
fi
|
|
fi
|
|
done
|
|
unset IFS
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Store file names recursively under start pipe separated as files:
|
|
#-----------------------------------------------------------------------------
|
|
files="$( fls -o "${OFS}" -u -F -r -m "${DIR}" "${IMG}" "${start}" )"
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Extract files recursively into target folder(s):
|
|
#-----------------------------------------------------------------------------
|
|
IFS=$'\n'
|
|
for file in ${files} ; do
|
|
inode="$( echo "${file}" | cut -d "|" -f 3 )"
|
|
name="$( echo "${file}" | cut -d "|" -f 2 )"
|
|
|
|
if ! echo "${name}" \
|
|
| grep -E ".*/(cur|new|tmp)/.*/.*" > /dev/null
|
|
then
|
|
# Maildir format incl. colons is not possible with ntfs!
|
|
icat -o "${OFS}" "${IMG}" "${inode}" > "${T_PRE}${name/:/..}"
|
|
else
|
|
left="$( echo "${name}" | sed -E 's#(/cur|new|tmp)/.*#\1#' )"
|
|
right="$( echo "${name}" | sed -E 's#.*/(cur|new|tmp)/##' )"
|
|
# replace the last slash with a hyphen
|
|
icat -o "${OFS}" "${IMG}" "${inode}" > "${T_PRE}${left}/${right//\//-}"
|
|
fi
|
|
done
|
|
unset IFS
|
|
done
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Index the local and external mail storage:
|
|
#-------------------------------------------------------------------------------
|
|
echo "Indexing mails... please wait..."
|
|
|
|
mailboxes="$( find "${T_PRE}${S_PRE}/${user}" \
|
|
-type d -regex ".*\(cur\|new\|tmp\)" -exec dirname '{}' \; )"
|
|
|
|
IFS=$'\n'
|
|
for mailbox in ${mailboxes} ; do
|
|
# \""${mailbox}\"" because: spaces are more evil as expected
|
|
mu index --quiet --muhome="${PRE}/mu" --maildir=\""${mailbox}\""
|
|
done
|
|
unset IFS
|
|
|
|
echo "Done."
|
|
|
|
exit 0 |