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.

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
- UAC ships optional modifiers that deliberately change the system it is investigating, which is a direct break from the read-only rule that governs live forensic response.
- Every modifier snapshots the pre-change state first, so the record of what was altered survives in the evidence archive even when the system is modified.
- The entire runtime is POSIX shell with no dependencies, which is why it runs on ESXi hosts, OpenWrt routers, and NAS appliances where no agent can be installed.
- Collection logic lives in declarative YAML, so a contributor can add coverage for a new application without writing or reviewing a line of shell.
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.
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.
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:
- AIX
- ESXi
- FreeBSD
- Linux
- macOS
- NetBSD
- NetScaler
- OpenBSD
- Solaris
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.
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 area | Policy |
|---|---|
| /etc configuration | Excludes shadow, shadow-, master.passwd, spwd.db, gshadow, gshadow- |
| User accounts | Skips accounts with no login shell |
| System config files | ignore_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.
| Project | Model | Platform focus | Deployment | Best for |
|---|---|---|---|---|
| UAC | Portable script collector, YAML artifacts | Unix-like (broad) | Copy and run, no install | One-off triage on appliances and unusual hosts |
| GRR Rapid Response | Client/server remote forensics | Cross-platform | Agent plus server | Fleet-scale remote acquisition |
| Velociraptor | VQL query and hunt platform | Cross-platform | Agent plus server | Interactive fleet-wide hunts |
| osquery | SQL over OS state | Cross-platform | Agent plus management | Inventory and scheduled queries |
| KAPE | Target and module collection | Windows-primary | Local | Windows artifact triage |
| LiME / AVML | Memory acquisition | Linux | Local | Volatile 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.
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."





