Volatility

Volatility is another command line forensics program, which is used for memory forensics. The default installation contains various profiles that can be used to specify the specific Windows build that a given dump of a memory image was made from. Building a volatility Docker image is relatively straightforward, although, as a Python program, it has dependencies on specific python modules, which can be installed through a combination of the OS package manager, apt in the case of Ubuntu, and the Python pip package manager. Since the vol.py script is run against an exported file, such as memdump.mem, it does not need special privileges and the container is configured to run as the volatility user.

FROM ubuntu:cosmic
LABEL maintainer "djds djds@ccs.neu.edu"

ENV DEBIAN_FRONTEND="noninteractive"

RUN mkdir /setup
WORKDIR /setup

RUN apt-get update && apt-get dist-upgrade -y \
    && apt-get install -y git ipython python \
    python-distorm3 python-openpyxl python-pil \
    python-pip python-pycryptodome python-setuptools \
    python-tz python-ujson python-yara

RUN pip install pycrypto \
    && git clone "https://github.com/volatilityfoundation/volatility" \
    && cd volatility \
    && python setup.py build \
    && python setup.py install

ARG GID
ARG ID

RUN groupadd -g "${GID}" volatility \
    && useradd -m -G audio,video,plugdev -u "${ID}" -g "${GID}" volatility \
    && mkdir -p /home/volatility/data \
    && chown -R volatility:volatility /home/volatility \
    && rm -rf /var/lib/apt/lists/* \
    && rm -rf /setup

WORKDIR /home/volatility/data

# Run as non privileged user
USER volatility

ENTRYPOINT ["/usr/local/bin/vol.py"]

The build script is again relatively simple, using a similar syntax to the one used to create the RegRipper container.

#!/bin/bash

set -euo pipefail

REGISTRY="${REGISTRY:-registry.gitlab.com/bghost/docker-forensics/}"

user='djds'
repo='volatility'
tag=${1:-latest}

# set environment vars
GID="${GID:-$(id -g)}"
ID="${ID:-$(id -u)}"

# build the container:
docker build \
    --build-arg GID="${GID}" \
    --build-arg ID="${ID}" \
    -t "${REGISTRY}${user}/${repo}:${tag}" .

if [[ "${PUSH:-}" == "true" ]]; then
    docker push "${REGISTRY}${user}/${repo}:${tag}"
fi

# clean up our host environment
unset {GID,ID}

Running the container can again be done as a script, to reduce the errors of typing out a long command line each time, or wrapped in a function.

#!/bin/bash

# Use djds/volatility:latest by default
tag="latest"

REGISTRY="${REGISTRY:-registry.gitlab.com/bghost/docker-forensics/}"
docker run --rm -it \
    -c 4 \
    -m 4096M \
    -v /etc/localtime:/etc/localtime:ro \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -e "DISPLAY=unix${DISPLAY}" \
    -v "${PWD}:/home/volatility/data" \
    -v /dev/shm:/dev/shm \
    --name volatility \
    "${REGISTRY}djds/volatility:${tag}" "${@}"

Since volatility has many different scans, which are useful to run on various systems, it can bel helpful to wrap the entire routine in another script, which will create an appropriate output directory, and continue if a give routine is unsupported by the selected --profile using the : builtin, which functions as a NULL operator, similar to the Python pass statement.

#!/bin/bash

set -euo pipefail

memory_image="memdump.mem"
memory_image_path="./"
output_dir="./memdump.mem_output"
profile='Win10x64_16299'

if [ ! -d "${output_dir}" ]; then
    mkdir "${output_dir}"
fi

volatility() {
    # Use djds/volitility:latest by default
    REGISTRY="${REGISTRY:-registry.gitlab.com/bghost/docker-forensics/}"
    tag='latest'
    docker run --rm -it \
        -c 4 \
        -m 4096M \
        -v /etc/localtime:/etc/localtime:ro \
        -v /tmp/.X11-unix:/tmp/.X11-unix \
        -e "DISPLAY=unix${DISPLAY}" \
        -v "$(pwd):/home/volatility/data" \
        -v /dev/shm:/dev/shm \
        --name volatility \
        "${REGISTRY}djds/volatility:${tag}" "${@}"
}


volatility -f "${memory_image_path}/${memory_image}" \
    --profile="${profile}" \
    imageinfo >"${output_dir}/imageinfo.txt" || :

volatility -f "${memory_image_path}/${memory_image}" \
    --profile="${profile}" \
    pslist >"${output_dir}/pslist.txt" || :

volatility -f "${memory_image_path}/${memory_image}" \
    --profile="${profile}" \
    psscan >"${output_dir}/psscan.txt" || :

volatility -f "${memory_image_path}/${memory_image}" \
    --profile="${profile}" \
    pstree >"${output_dir}/pstree.txt" || :

volatility -f "${memory_image_path}/${memory_image}" \
    --profile="${profile}" \
    psxview >"${output_dir}/psxview.txt" || :

volatility -f "${memory_image_path}/${memory_image}" \
    --profile="${profile}" \
    connections >"${output_dir}/connections.txt" || :

volatility -f "${memory_image_path}/${memory_image}" \
    --profile="${profile}" \
    connscan >"${output_dir}/connscan.txt" || :

volatility -f "${memory_image_path}/${memory_image}" \
    --profile="${profile}" \
    sockets >"${output_dir}/sockets.txt" || :

volatility -f "${memory_image_path}/${memory_image}" \
    --profile="${profile}" \
    sockscan >"${output_dir}/sockscan.txt" || :

volatility -f "${memory_image_path}/${memory_image}" \
    --profile="${profile}" \
    ldrmodules >"${output_dir}/ldrmodules.txt" || :

volatility -f "${memory_image_path}/${memory_image}" \
    --profile="${profile}" \
    malfind >"${output_dir}/malfind.txt" || :

printf "%s\n" "Complete."

By wrapping the volatility function in the above script, the script can be invoked as ./volatility.sh which will create a new container for each command from the volatility image, meaning that each routine is run with a fresh volatility installation, helping to avoid side effects from the return value of the previous routine. This script will also create the output directory spefied by the ${output_dir} variable if it does not yet exist, and create an appropriately-named text file with the output of the volatility routine inside this directory.

An example of running the container and passing the imageinfo routine against a memdump.mem file in the current directory without file redirection, yields the following results.

volatility -f "./memdump.mem" --profile="Win10x64_16299" imageinfo 
Volatility Foundation Volatility Framework 2.6.1
INFO    : volatility.debug    : Determining profile based on KDBG search...
          Suggested Profile(s) : Win10x64_16299
                     AS Layer1 : SkipDuplicatesAMD64PagedMemory (Kernel AS)
                     AS Layer2 : FileAddressSpace (/home/volatility/data/memdump.mem)
                      PAE type : No PAE
                           DTB : 0x1ab000L
                          KDBG : 0xf802f43e14d0L
          Number of Processors : 4
     Image Type (Service Pack) : 0
                KPCR for CPU 0 : 0xfffff802f31f2000L
                KPCR for CPU 1 : 0xffff9b01a9a40000L
                KPCR for CPU 2 : 0xffff9b01a9ad7000L
                KPCR for CPU 3 : 0xffff9b01a9b79000L
             KUSER_SHARED_DATA : 0xfffff78000000000L
           Image date and time : 2018-04-06 12:42:32 UTC+0000
     Image local date and time : 2018-04-06 08:42:32 -0400

A built version of this image can be pulled from the GitLab container registry:

$ docker pull registry.gitlab.com/bghost/docker-forensics/djds/volatility:latest