uac: The Forensic Tool That Breaks the First Rule of Forensics

UAC collects evidence from Unix-like systems without installing anything. Then, on request, it changes the system it's investigating. Here's why that's the right call.

9 min read • View on GitHub • More from tclahr

A forensic examiner in a lab coat crouches over a wooden floor, lifting a hinged floorboard with one hand while holding a magnifying glass with the other. Beneath the lifted board, crosshatched darkness hides a small mechanical figure, a process that was invisible from above. A neat stack of sealed evidence bags and a clipboard sit untouched on a side table. The image makes the article's central argument visible: the examiner disturbs the scene deliberately, while the evidence-preservation apparatus waits beside them.
The examiner lifts the board to see what is hiding under it. The evidence bags stay sealed until the record is written.

The reception of UAC has been amazing, with the community actively contributing with valuable feedback, ideas, and suggestions. I am glad that UAC has been widely adopted and utilized by a diverse user base, encompassing security practitioners, expert witnesses, incident responders, and well-known DFIR instructors

Thiago Lahr, UAC creator · Help Net Security interview
Key Takeaways

The first rule of digital forensics

Live response has one commandment: observe, do not disturb. Reading a file updates its access time. Mounting a filesystem rewrites its superblock. Every command you run against a running machine leaves a mark on the machine.

This is not theoretical. When SANS Internet Storm Center tested UAC on Linux, the author compared file timelines before and after the run and watched the access times move. The fix is procedural: gather metadata first, run the collector second.

It would be best if you collected file times before running the rest of the script.

Tom Webb, SANS Internet Storm Center diary author · Live Linux IR with UAC

That is the orthodox position. A collector touches the evidence, so you sequence your work to touch it as little and as late as possible.

UAC breaks it anyway

UAC ships a class of artifacts the maintainers call modifiers. They are the opposite of read-only. They change the state of the system under investigation, and they do it on purpose.

The first one unmounts bind mounts over /proc/PID. Some rootkits hide a process by bind-mounting an empty directory over its entry in /proc. The process is running. ps cannot see it. The fix is to unmount the overlay and let the real directory show through.

artifacts:
  -
    description: Umount all bind mounted directories to /proc/PID.
    supported_os: [linux]
    collector: command
    foreach: mount | awk 'BEGIN { FS=" on "; } { print $2; }' | grep "/proc/[0-9]" | awk '{print $1}'
    command: umount "%line%"
    output_file: umount_%line%.txt

The second modifier sets kernel.ftrace_enabled=0 through sysctl, which blocks a class of loadable kernel module rootkits that hook syscalls through ftrace. Its own description reads like a confession: "Display kernel parameters before changing the system state."

That sentence is the whole design. Before either modifier touches anything, a set of snapshot artifacts records what the system looked like beforehand. The process modifier runs mount, ps, ps auxwww, ps auxwwwf, and ps -ef first. The ftrace modifier runs sysctl -a first. The pre-change state lands in the evidence archive no matter what happens next.

The guardrail in three moves: snapshot the state, change the state, verify the change. The dashed line shows that the pre-change record reaches the evidence archive whether or not the modifier runs.

A collector that runs on anything with a shell

Here is the constraint that makes the aggression affordable. UAC has no compiled binary. No agent. No Python. No runtime to install. It is POSIX shell and YAML, and if the host has a /bin/sh, UAC can run.

That is not nostalgia. It is the reason the tool reaches places a normal forensic workstation cannot. The project lists support for nine operating system families:

Beyond that list, the README points at NAS devices, OpenWrt routers, and IoT hardware. These are the machines where you cannot deploy an endpoint agent, often cannot install anything, and frequently have BusyBox instead of GNU coreutils. The only guaranteed interpreter is the one already on the box.

The portability costs something real. CONTRIBUTING.md states the rule plainly: "UAC is meant to run on any bourne shell, so some features like arrays, command substitution $(command) and tests [[ ... ]] are not fully supported by most of them." No arrays. No double-bracket tests. The maintainers accept the restricted dialect because the reach is worth more than the syntax.

A close-up of a small OpenWrt-style router with visible antenna and status LEDs sitting on a workbench, surrounded by a laptop with a blank screen, a hand-labelled USB stick, a serial cable, and a paper tag tied to the antenna. The tight composition focuses on the router and the USB stick rather than the room. It corrects the common assumption that forensic collection happens on a data-center workstation, showing that the whole toolkit is a stick and a shell.
No agent, no installer, no server. A USB stick, a shell, and the host you already have.

The collection logic is data, not code

Every artifact in UAC is a YAML entry. It declares a description, the operating systems it supports, a collector type, and the parameters for that collector. The engine does the work. An artifact for hashing executables walks /, skips proc and procfs, matches regular files with any execute bit set, and hashes them. The artifact only states intent.

The collector types are small and few: file copies files, command runs a command and saves stdout, hash finds and hashes matching files, and stat emits filesystem metadata for a bodyfile.

The cleverest detail is chaining. Take the shell history artifact. You cannot hard-code the path to a user's history file, because HISTFILE can point anywhere. So UAC collects it in two stages.

  -
    description: Parse config files to determine $HISTFILE.
    supported_os: [all]
    collector: command
    command: grep -E "HISTFILE=.*" %user_home%/.bashrc %user_home%/.bash_profile %user_home%/.profile /etc/bash.bashrc | sed -e 's|.*HISTFILE=||' -e 's|^~/|%user_home%/|'
    output_directory: %temp_directory%/files/shell
    output_file: bash_histfile.txt
  -
    description: Collect $HISTFILE.
    supported_os: [all]
    collector: file
    path: %temp_directory%/files/shell/bash_histfile.txt
    is_file_list: true

Stage one greps the shell config files for HISTFILE=, strips everything before the equals sign, rewrites a leading tilde into the expanded home placeholder, and writes the result to a temp file. Stage two points a file collector at that temp file and sets is_file_list: true, which tells the engine to treat each line as a path to copy. The output of one collection drives the next. It is a dataflow, expressed in data.

This is the mechanism behind the project's growth. The v3.4.0 release added artifacts for AI coding tools including Claude Code, Codex, Gemini, Copilot, and Cursor. Each one arrived as a standalone YAML file. Contributors added coverage without touching the engine. The creator later wrote that "AI tooling is becoming an increasingly important source of forensic evidence," and the YAML layer is what let the community act on that quickly.

What UAC refuses to collect

A tool willing to unmount /proc mounts and disable kernel tracing sounds reckless. The artifact catalog says otherwise. The same project draws hard lines about what it will not gather.

Artifact areaPolicy
/etc configurationExcludes shadow, shadow-, master.passwd, spwd.db, gshadow, gshadow-
User accountsSkips accounts with no login shell
System config filesignore_date_range: true, collected regardless of modification date

Password hashes are excluded from /etc collection even though analysts sometimes want them. No-login system accounts are skipped because their history is noise. System configuration is collected regardless of when it last changed, because a static config still matters. The team that will alter a system to expose a hidden process also knows which secrets to leave behind. The aggression is calibrated, not careless.

How it compares

UAC is not a platform. It has no server, no agent, and no fleet management. That is the point, and it is also the boundary. Here is where it sits against the tools people usually reach for.

ProjectModelPlatform focusDeploymentBest for
UACPortable script collector, YAML artifactsUnix-like (broad)Copy and run, no installOne-off triage on appliances and unusual hosts
GRR Rapid ResponseClient/server remote forensicsCross-platformAgent plus serverFleet-scale remote acquisition
VelociraptorVQL query and hunt platformCross-platformAgent plus serverInteractive fleet-wide hunts
osquerySQL over OS stateCross-platformAgent plus managementInventory and scheduled queries
KAPETarget and module collectionWindows-primaryLocalWindows artifact triage
LiME / AVMLMemory acquisitionLinuxLocalVolatile memory capture only

UAC is not a replacement for any of these. It is the tool you reach for when the host cannot accept an agent and you do not have a server to point it at. It composes with the memory tools rather than competing with them. A documented example folds an AVML memory-dump artifact into a standard UAC profile, so one run produces both the filesystem evidence and the memory image.

One maintainer, 1,400 commits

The contributor list tells an honest story. Of roughly 1,571 commits across the top ten contributors, tclahr authored about 1,429, or 91 percent. The next contributor sits at 43. The bus factor is one.

That is not a takedown. It is the shape of a project built by a working DFIR practitioner who needed the tool and kept maintaining it. The stale-PR automation exists because there is a steady stream of outside contributions to triage, and community members have added real coverage: systemd timer artifacts, and the AI tooling artifacts credited to walkingawire and ecapuano. But the core engine, the CI, and the release cadence all run through one person.

A WSJ hedcut-style editorial portrait of Thiago Lahr, creator of UAC, rendered in black ink on a pure white background. Stipple dots build the skin texture, fine hatching lines define the hair and clothing. The portrait accompanies his quote about the project's reception among security practitioners and DFIR instructors.

For anyone adopting UAC in a serious capacity, that is the number to weigh alongside the feature list. The tool is well tested, validated in CI, and used in real investigations. It is also one maintainer away from a hard problem.

The rule was always conditional

The first rule of forensics exists to protect the evidence. UAC does not ignore it. It treats the rule as a default rather than a law, and it pays the price of breaking it by writing down exactly what it changed and when.

Snapshot first, act second, record always. That is the protocol. It is why a tool can unmount a rootkit's hiding place and still hand you a defensible archive. The rule was never "never touch anything." It was "never touch anything without a record."