Qubes OS as a Lab Setup
After using Qubes-Whonix as a daily driver for a while (and feeling
"reasonably secure"), I decided to experiment with using it as a security research lab. Apparently this is not a common use, based on the limited discussion on forums or elsewhere. This made me wonder if it was an obviously bad idea and I was just missing somethin, or whether it is actually a viable setup. So I built a Qubes lab in order to find out.
This is not a how-to so much as a post about architectural decisions, tradeoffs, and unexpected behaviors specific to using Qubes as a lab. Primarily for reverse engineering malware, but flexible and extensible enough to be pointed at whatever.
Different people will have different needs and uses, necessitating unique design choices, configurations, and tooling.
Do what you want. In doing so, hopefully some of this might be useful to you.
Primer
Qubes is a desktop operating system that uses isolated Xen-based virtual machines to achieve security by compartmentalization. One of the project's tenets is to "distrust the infrastructure and be free". Being free is cool, but in this context, the more important dictate is to distrust the infrastructure, meaning anything and everything external to the host machine. Off-the-shelf testing distributions like Kali, Parrot, or REMnux, prioritize curating toolsets first and user security/anonymity/privacy (i.e., safety) second, at least at a level required for riskier work. Maybe the fairer statement is that Qubes appears to take user safety more seriously than other comparable OSes. To be even fairer, this is just a hunch (for now).
Basic Architecture
In any case, my design priorties were, in order: 1. isolation, 2. repeatability, 3. extensibility, and 4. scalability.
For color, that means 1. collect and observe dangerous software without impacting my own environment, 2. set up and tear down ad nauseum without friction or fatigue; automate as much boring stuff as possible, 3. build or install new capabilities easily; perform updates and upgrades easily; remove unwanted software easily, and 4. accomodate a large enough sample size to do interesting research, efficiently.
Unlike traditional malware labs that often only provide one or two layers of isolation, i.e., between host(s) and virtual machine(s), without much well-defined scoping, I attempted to maximize isolation in a pipeline with six separate application (AppVM) Qubes. These, in turn, are based on one of three templates that act as golden images for configuring an operating system and installing software. Persistent filesystems only occur in AppVMs. Execution takes place in ephemeral, disposable Qubes (burn after use). For performance and reducing attack surface, I wanted each Qube - whether template, appVM, or disposable - to be as minimal as possible. Only two qubes have access to networking: lab-ingest for acquiring samples and lab-net-dvm for dynamic analysis.
| NAME |
PURPOSE |
NETWORKING |
RULE(S) |
| tpl-lab-base |
Minimal trusted baseline |
none |
Template; Debian 13; no analytical tools; no networking by default |
| tpl-lab-analysis |
Analysis tooling |
none |
Template; standard tools: Ghidra, YARA, Binwalk, jq, ripgrep |
| tpl-lab-dvm |
Disposable execution |
none |
Template; standard tools: auditd, bpftrace, strace, ltrace, tcpdump, procps |
| lab-ingest |
controlled acquisition of samples |
sys-firewall |
AppVM; never read or execute files |
| lab-samples |
store malware corpus |
none |
AppVM; storage only; no execution |
| lab-workbench |
human analysis machine |
none |
AppVM |
| lab-evidence |
artifact storage |
none |
AppVM; PCAPs, logs, dropped files, writeups |
| lab-collector |
indexing metadata, triage |
none |
AppVM |
| lab-net |
"fake Internet" |
- |
AppVM |
| lab-offline-dvm |
default execution environment |
none |
Disposable |
| lab-net-dvm |
"networked" execution |
lab-net |
Disposable; C2 observation, beacon analysis |
Next: Implementation
Storage Planning and Management turned out to need some trial and error. First, a 20GB archive from
VX Underground failed to download because of insufficient disk space configured in the
lab-ingest AppVM. Easy enough to change, but there was a cascading effect across Qubes after downloading and extracting several archives:
lab-ingestcan and should be purged regularly to preserve space, but the allocations needed for
lab-samples,
lab-evidence, and
lab-collector may vary. For instance, for a given behavior or file type or research question, I may need an arbitrary range of space between 300GB-1TB in lab-samples alone to scan across a large volume of files.
Tool Installation requires temporary networking for templates, which is not recommended. Calls from a package manager or downloads from Tor Browser could (should?) have been done in a disposable Qube, with the software then transferred via
qvm-copy or
qvm-move. This might be a change I make later, for the sake of attack surface, the next time I grab more tooling.
Next: Test Runs
Non Qubes Things
Some of this is just plain old GNU/Linux/UNIX stuff that comes with the territory. Some is a matter of preference, taste, or aeshethics. I will not dive into too much, but will mention in passing.
Symlinks may be needed here and there to make a program available as a global command. For example, symlink /usr/local/bin/ghidra to /opt/ghidra/ghidraRun allows loading Ghidra without needing to type its path. Small things, but worth the effort.
Dependency Resolution is always a thing: whichever versions of tools you want may be incompatible with stable releases of whatever distro(s) you are using in Qubes. It is what it is.
Telemetry: auditd requires targeted rules, since defaults generate huge amounts of noise. eBPF is cool but bpftrace is probably okay enough at live syscall tracing and process monitoring for the vast majority of cases. Zeek, raw PCAP, or both - who's to say, really?
Future Work?
Automation
Most everything so far has been manual effort, for love of the game. Essentially, all of it could be automated. Cron jobs in dom0 could orchestrate downstream cron jobs in the AppVMs to perform any of the pipeline functions, including triage, reporting, and generating signatures. The disassembly and debugging is the fun part, though, so I may keep that for some old fashioned human effort.