Memory Analysis - AVML and Volatility

`type: Tool&Process Review`
#Computer Forensics
#Linux

TL;DR

This project consists of the procedure to create a memory image for a system using AVLM from Microsoft and perform analysis on that system using Volatility3 framework.

AVML (Microsoft)

AVML is a memory acquisition tool developed under microsoft and is a portable tool build in rust. This allows the software to be executed as a static binary and doesnt require any knowledge of the target OS distribution or the kernel without which tools like LiME wont work.

AVML internally uses the LiME output format (if compression is not used) for imaging and provides extended features allowing the image to be pushed to object storages.

Compression feature in AVML will be useful in terms of portability as exporting bigger RAM size machines will use a lot more memory.

Installation of AVML

The Software required in the Forensics system are:

  • wget

The current latest avml can be found in the github page in the release section. This project uses v0.11.0.

The software can be downloaded by running the command:

wget https://github.com/microsoft/avml/releases/download/v0.11.0/avml &&
chmod +x avml

Creating memory image with AVML

To create the memory image, we just need to run the command

sudo ./avml --compressed ./linux-image.compressed.mem

The compressed memory image should now be extracted and this can be done with the help of avml-convert command also provided by avml in the github release page. This can be installed by using the below command.

wget https://github.com/microsoft/avml/releases/download/v0.11.0/avml-convert &&
chmod +x avml-convert

Voalitlity 3

Installation

The Software required in the Forensics system are:

  • make
  • dwarfdump
  • gcc
  • linux-headers-KERNEL_VERSION

The KERNEL_VERSION can be obtained from the command uname

uname -r
# output: 5.15.0-67-generic

The important note is that the kernel release and the flavor of the Linux OS should exactly match inorder to perform analysis.

Creating debug symbols in volatility

First, we need to install the source code from the repository.

git clone https://github.com/volatilityfoundation/volatility3.git

Volatility3 made a move from profiles to symbol tables which is a major change from v2 to v3. Check all the changes here.

To get these symbol tables that are required as for the image, we can either find it in the Volatility isf-server.

The search term can be extracted from the volatility framework by running the below command.

python3 volatility3/vol.py -f ./linux-image.mem banners

# output: 
# 0xacbf6f7	Linux version 5.15.0-67-generic (buildd@lcy02-amd64-116) (gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #74-Ubuntu SMP Wed Feb 22 14:14:39 UTC 2023 (Ubuntu 5.15.0-67.74-generic 5.15.85)
# 0x27c00200	Linux version 5.15.0-67-generic (buildd@lcy02-amd64-116) (gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #74-Ubuntu SMP Wed Feb 22 14:14:39 UTC 2023 (Ubuntu 5.15.0-67.74-generic 5.15.85)
# .
# .

But if the string is not available(which in most case wont be available in custom environment of kernel like in my case), we need to build our own ISF (Intermediate Symbol Format) file. To achieve this, the volatility foundation provides us with tools required called dwarf2json that creates a dump json file.

git clone https://github.com/volatilityfoundation/dwarf2json.git && \
cd dwarf2json/
go build

To get this software, we can comiple from the source and to install this, we require golang. we can install the software using sudo apt-get install golang-go.

Once we install the golang compiler, we run go build in dwarf2json directory and use the compiled output to run the below command.

sudo ./dwarf2json linux --elf /usr/lib/debug/boot/vmlinux-$(uname -r) > output.json

Reference: https://volatility3.readthedocs.io/en/latest/symbol-tables.html

But when we do this in the real environment, we maynot have the kernel debug symbols readily donwloaded system. To download this, we need to add out own deb repos and the required GPG key into the keyrings.

codename=$(lsb_release -c | awk  '{print $2}')
sudo tee /etc/apt/sources.list.d/ddebs.list << EOF
deb http://ddebs.ubuntu.com/ ${codename}      main restricted universe multiverse
deb http://ddebs.ubuntu.com/ ${codename}-security main restricted universe multiverse
deb http://ddebs.ubuntu.com/ ${codename}-updates  main restricted universe multiverse
deb http://ddebs.ubuntu.com/ ${codename}-proposed main restricted universe multiverse
EOF

# FOR UBUNTU >= 16.04
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C8CAB6595FDFF622

# FOR UBUNTU older versions
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ECDCAD72428D7C01

Adding the keyring from public newtorks like university internet could be restricted.

Now update the repo and start installing the apt debug symbols according to the kernel.

sudo apt-get update && \
sudo apt-get install -y linux-image-$(uname -r)-dbgsym

The above will add the vmlinux-$(uname -r) into the required directory that can be used to construct the json formatted debug symbols.

The important point to note here is that the image maynot be built with minimal memory and for building large kernels for debug symbols would require atleast 8GiB main memory.

After building the output.json file finally, it should be moved to volatility3/volatility3/symbols and the symbols will be automatically parsed by the framework to perform on the required memory image.

Reference: https://hadibrais.wordpress.com/2017/03/13/installing-ubuntu-kernel-debugging-symbols/

Using Volatility

Once the symbol configuration is done, we can use the image created from avml to use it in analysing the information like programs running currently with linux.pstree.PsTree option and to check the network connections with linux.sockstart option.

Help command can be used to find out all the options that can be used.

python3 volatility3/vol.py -f ./linux-image.mem --help

Using Volshell

This code basically does the same thing as pslist outputted as in the plugin. But we have access to all the attributes of the task_list class.

from volatility3.plugins.linux import pslist
from volatility3.framework.objects import utility
from volatility3.framework.renderers import format_hints

print("OFFSET (V)      PID     TID     PPID    COMM")
for proc in pslist.PsList.list_tasks(self.context, self.config['kernel']):
    print(
            hex(format_hints.Hex(proc0.vol.offset)) , "\t" , 
            proc.pid, "\t" , 
            proc.parent.tgid,  "\t" , 
            proc.tgid, "\t" , 
            utility.array_to_string(proc.comm), "\t")
>>> dir(proc0)
# Output: ['VolTemplateProxy', '__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__mce_reserved', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__state', '__str__', '__subclasshook__', '__weakref__', '_abc_impl', '_add_process_layer', '_check_members', '_concrete_members', '_context', '_vol', 'acct_rss_mem1', 'acct_timexpd', 'acct_vm_mem1', 'active_memcg', 'active_mm', 'add_process_layer', 'alloc_lock', 'atomic_flags', 'audit_context', 'backing_dev_info', 'bio_list', 'blocked', 'bpf_ctx', 'bpf_storage', 'btrace_seq', 'cached_requested_key', 'capture_control', 'cast', 'cg_list', 'cgroups', 'children', 'clear_child_tid', 'closid', 'comm', 'compat_robust_list', 'core_cookie', 'core_node', 'core_occupation', 'cpu', 'cpus_mask', 'cpus_ptr', 'cpuset_mem_spread_rotor', 'cpuset_slab_spread_rotor', 'cred', 'curr_ret_depth', 'curr_ret_stack', 'default_timer_slack_ns', 'delays', 'dirty_paused_when', 'dl', 'exit_code', 'exit_signal', 'exit_state', 'files', 'flags', 'frozen', 'fs', 'ftrace_timestamp', 'futex_exit_mutex', 'futex_state', 'get_process_memory_sections', 'get_symbol_table_name', 'get_threads', 'group_leader', 'gtime', 'has_member', 'has_valid_member', 'has_valid_members', 'il_prev', 'in_eventfd', 'in_execve', 'in_iowait', 'in_memstall', 'in_ubsan', 'in_user_fault', 'io_context', 'io_uring', 'ioac', 'is_kernel_thread', 'is_thread_group_leader', 'is_user_thread', 'jobctl', 'journal_info', 'kmap_ctrl', 'kretprobe_instances', 'l1d_flush_kill', 'last_siginfo', 'last_sum_exec_runtime', 'last_switch_count', 'last_switch_time', 'last_task_numa_placement', 'last_wakee', 'loginuid', 'maj_flt', 'mce_addr', 'mce_count', 'mce_kflags', 'mce_kill_me', 'mce_ripv', 'mce_vaddr', 'mce_whole_page', 'member', 'memcg_in_oom', 'memcg_nr_pages_over_high', 'memcg_oom_gfp_mask', 'memcg_oom_order', 'mempolicy', 'mems_allowed', 'mems_allowed_seq', 'migration_disabled', 'migration_flags', 'migration_pending', 'min_flt', 'mm', 'nameidata', 'nivcsw', 'no_cgroup_migration', 'node_stamp', 'normal_prio', 'nr_cpus_allowed', 'nr_dirtied', 'nr_dirtied_pause', 'nsproxy', 'numa_faults', 'numa_faults_locality', 'numa_group', 'numa_migrate_retry', 'numa_pages_migrated', 'numa_preferred_nid', 'numa_scan_period', 'numa_scan_period_max', 'numa_scan_seq', 'numa_work', 'nvcsw', 'on_cpu', 'on_rq', 'oom_reaper_list', 'oom_reaper_timer', 'pagefault_disabled', 'parent', 'parent_exec_id', 'patch_state', 'pdeath_signal', 'pending', 'perf_event_ctxp', 'perf_event_list', 'perf_event_mutex', 'personality', 'pf_io_worker', 'pi_blocked_on', 'pi_lock', 'pi_state_cache', 'pi_state_list', 'pi_top_task', 'pi_waiters', 'pid', 'pid_links', 'plug', 'policy', 'posix_cputimers', 'posix_cputimers_work', 'preempt_notifiers', 'pref_node_fork', 'prev_cputime', 'prio', 'psi_flags', 'ptrace', 'ptrace_entry', 'ptrace_message', 'ptraced', 'ptracer_cred', 'pushable_dl_tasks', 'pushable_tasks', 'rcu', 'rcu_users', 'real_blocked', 'real_cred', 'real_parent', 'recent_used_cpu', 'reclaim_state', 'restart_block', 'restore_sigmask', 'ret_stack', 'rmid', 'robust_list', 'rseq', 'rseq_event_mask', 'rseq_sig', 'rss_stat', 'rt', 'rt_priority', 'sas_ss_flags', 'sas_ss_size', 'sas_ss_sp', 'saved_sigmask', 'sched_class', 'sched_contributes_to_load', 'sched_info', 'sched_migrated', 'sched_psi_wake_requeue', 'sched_remote_wakeup', 'sched_reset_on_fork', 'sched_task_group', 'se', 'seccomp', 'security', 'self_exec_id', 'sequential_io', 'sequential_io_avg', 'sessionid', 'set_child_tid', 'sibling', 'sighand', 'signal', 'splice_pipe', 'stack', 'stack_canary', 'stack_refcount', 'stack_vm_area', 'start_boottime', 'start_time', 'static_prio', 'stime', 'syscall_dispatch', 'sysvsem', 'sysvshm', 'task_frag', 'task_works', 'tasks', 'tgid', 'thread', 'thread_group', 'thread_info', 'thread_node', 'thread_pid', 'throttle_queue', 'timer_slack_ns', 'tlb_ubc', 'total_numa_faults', 'trace', 'trace_overrun', 'trace_recursion', 'tracing_graph_pause', 'trc_holdout_list', 'trc_ipi_to_cpu', 'trc_reader_checked', 'trc_reader_nesting', 'trc_reader_special', 'uclamp', 'uclamp_req', 'usage', 'use_memdelay', 'user_cpus_ptr', 'utask', 'utime', 'vfork_done', 'vmacache', 'vol', 'wake_cpu', 'wake_entry', 'wake_q', 'wakee_flip_decay_ts', 'wakee_flips', 'write']