Skip to content

Instantly share code, notes, and snippets.

@janstarke
Last active June 13, 2022 11:49
Show Gist options
  • Save janstarke/47217a7af9c383086490f04d7e92f20e to your computer and use it in GitHub Desktop.
Save janstarke/47217a7af9c383086490f04d7e92f20e to your computer and use it in GitHub Desktop.
create triage data from mounted Windows image
#!/bin/bash
trap "exit 1" TERM
export TOP_PID=$$
RIP=/usr/local/bin/rip
function tln2csv {
egrep '^[0-9]+\|' | awk -F '|' '{OFS="|";print 0,$5,0,0,0,0,0,-1,$1,-1,-1}' |mactime2 -b - -d -t "$TIMEZONE"
}
function usage {
echo "Usage: $0 [options] [<windows_mount_dir>}"
echo ""
echo "Options:"
echo " -t <timezone> convert timestamps from UTC to the given timezone"
echo " -l list availabel timezones"
echo " -h show this help information"
}
POSITIONAL_ARGS=()
TIMEZONE=UTC
while [[ $# -gt 0 ]]; do
case $1 in
-t)
TIMEZONE="$2"
shift
shift
;;
-l)
mactime2 -t list
exit 0
;;
-h)
usage
exit 0
;;
*)
POSITIONAL_ARGS+=("$1")
shift
;;
esac
done
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
if [ $# -ne "1" ]; then
usage
exit 1
fi
###########################################################
#
# Checks if a file exists and is readable. If it is, this
# function prints the name of the file.
# If <fail_if_missing> evaluates to true, the functions
# aborts this script if the file does not exist or
# if it is not readable
#
# Usage:
#
# check_file <filename> <fail_if_missing>
#
function check_file {
local FILE=$1
local FAIL_IF_MISSING=$2
if [ -r "$FILE" ]; then
echo "[+] found '$FILE'" >&2
echo "$FILE"
exit
fi
if $FAIL_IF_MISSING; then
echo "[-] missing '$FILE', aborting..." >&2
kill -s TERM $TOP_PID
fi
echo ""
}
###########################################################
###########################################################
#
# Usage:
#
# do_timeline <registry hive file> [<destination>]
#
# If you do not specify a destination, it will default to the
# basename of the registry hive file.
# In every case, destination will be prefixed by 'tln_' and
# suffixed by '.csv', so the remaining file will have the name
# tln_${destination}.csv
#
function do_timeline {
local FILE=$1
if [ "x$2" == "x" ]; then
BASENAME=$(basename "$FILE")
else
BASENAME=$2
fi
echo "[+] creating timeline for '$BASENAME'" >&2
$RIP -r "$FILE" -aT 2>/dev/null | egrep '^[0-9]+\|' >tln_$BASENAME.txt
cat tln_$BASENAME.txt | tln2csv >"tln_$BASENAME.csv"
BASENAME=
}
###########################################################
###########################################################
#
# Usage:
#
# host_info <path of SYSTEM hive> <path of SOFTWARE hive>
#
function host_info {
SYSTEM="$1"
SOFTWARE="$2"
rip -r "$SYSTEM" -p compname > compname.txt
rip -r "$SYSTEM" -p timezone > timezone.txt
rip -r "$SOFTWARE" -p msis > installed_software.txt
rip -r "$SOFTWARE" -p winver > winver.txt
rip -r "$SYSTEM" -p usbstor > usbstor.txt
rip -r "$SYSTEM" -p mountdev2 > mounted_devices.txt
}
###########################################################
###########################################################
#
# Usage:
#
# user_info <username> <path of ntuser.dat>
#
function user_info {
USER="$1"
NTUSER_DAT="$2"
rip -r "$NTUSER_DAT" -p run > "${USER}_run.txt"
rip -r "$NTUSER_DAT" -p cmdproc > "${USER}_cmdproc.txt"
}
###########################################################
###########################################################
#
# Usage:
#
# copy_user_file <username> <profiledir> <source_file_name>
#
function copy_user_file {
USER="$1"
PROFILEDIR="$2"
SRCFILE="$3"
BASE=$(echo "$SRCFILE" | cut -d . -f 1)
EXT=$(echo "$SRCFILE" | cut -d . -f 2)
CNT=1
for F in `find "$PROFILEDIR" -type f -iname "$SRCFILE"`; do
DSTFILE="${BASE}_${USER}_${CNT}.${EXT}"
if [ -r "$F" ]; then
echo "[+] exfiltrating '$F' from user $USER" >&2
cp "$F" "$DSTFILE"
else
echo "[-] file '$F' not found" >&2
fi
CNT=$(($CNT+1))
done
}
###########################################################
###########################################################
#
# Usage:
#
# registry_timeline <hive_file>
#
function registry_timeline {
FILE="$1"
HIVE=$(basename "$FILE")
if [ -r "$FILE" ]; then
echo "[+] creating a timeline of '$HIVE'" >&2
regdump -b "$FILE" | mactime2 -b - -d -t "$TIMEZONE" > "regtln_${HIVE}.csv"
else
echo "[-] file '$FILE' not found" >&2
fi
}
###########################################################
WIN_MOUNT=`realpath "$1"`
if [ ! -d "$WIN_MOUNT" ]; then
echo "'$WIN_MOUNT' is not a directory" >&2
exit 1
fi
SYSTEM="$(check_file "$WIN_MOUNT/Windows/System32/config/SYSTEM" true)"
SOFTWARE="$(check_file "$WIN_MOUNT/Windows/System32/config/SOFTWARE" true)"
SECURITY="$(check_file "$WIN_MOUNT/Windows/System32/config/SECURITY" true)"
#SYSCACHE="$(check_file "$WIN_MOUNT/Windows/System32/config/Syscache.hve" true)"
AMCACHE="$(check_file "$WIN_MOUNT/Windows/appcompat/Programs/Amcache.hve" false)"
if [ "x$AMCACHE" == "x" ]; then
AMCACHE="$(check_file "$WIN_MOUNT/Windows/AppCompat/Programs/Amcache.hve" false)"
fi
host_info "$SYSTEM" "$SOFTWARE"
for F in "$SYSTEM" "$SOFTWARE" "$SECURITY"; do
do_timeline "$F"
registry_timeline "$F"
done
if [ "x$AMCACHE" != "x" ]; then
do_timeline "$AMCACHE"
fi
if [ ! -d "$WIN_MOUNT/Users" ]; then
echo "[-] no Users directory found" >&2
exit 1
fi
while IFS= read -r D; do
USER=$(basename $D)
USER_DIR=$(realpath "$D")
echo "[+] found user '$USER'"
NTUSER_DAT=$(check_file "$USER_DIR/NTUSER.DAT" false)
USRCLASS_DAT=$(check_file "$USER_DIR/AppData/Local/Microsoft/Windows/UsrClass.dat" false)
if [ "x$NTUSER_DAT" != "x" ]; then
do_timeline "$NTUSER_DAT" "${USER}_ntuser"
else
echo "[-] missing file $NTUSER_DAT"
fi
if [ "x$USRCLASS_DAT" != "x" ]; then
do_timeline "$USRCLASS_DAT" "${USER}_usrclass"
else
echo "[-] missing file $USRCLASS_DAT"
fi
copy_user_file "$USER" "$USER_DIR" "ConsoleHost_history.txt"
done < <(find "$WIN_MOUNT/Users" -maxdepth 1 -mindepth 1 -type d)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment