Hardened CTI Environment
Purpose
Configure a hardened cyber threat intelligence (CTI) environment to reduce attack surface and attributable information that could lead back to the researcher. This threat model will focus on security and privacy alike.
Virtualization Software Selection
VMWare, Oracle VirtualBox, or QEMU are all options. VMWare and VirtualBox favor ease of configuration, however there are many QEMU front-ends that are available for simplifying/automating the use of QEMU. While most analysts will largely be confined to doing the majority of their research from a guest VM on top of their host, some situations may allow for access to a private virtual private server (VPS) that provides separation or a proxy for intelligence probing.
OS Selection
For the host OS, we are going to assume that the analyst is confined to Windows or MacOS. From here, we will focus on making the guest virtual machine (VM) or VPS follow minimalism with regards to the codebase and services.
Alpine Linux is the selection of choice due to their focus on security, minimalism, and ease of deployment. The MUSL codebase along with the OpenRC service manager allows for a non-standard environment without adding unneeded complexity and undesirable vulnerability trends.
To complement the environment, Alpine even has specialized kernels to fit particular use-cases. The following configuration, virt.x86_64.config is more fitting to our use-case with a dedicated guest VM. While the configuration certainly is not as hardened as a project such as the plague-kernel, it provides a reduced attack surface by gutting unnecessary hardware support and still has ground over other security projects such as the LTS kernel used by Whonix. For the lofty and ambitious, custom kernels can still be imported to Alpine.
Note: Alpine is not the only valid option. One could deploy PlagueOS, Whonix, or their own tailored distribution.
Obtain & Validate
Follow Alpine’s Verification process.
wget https://alpinelinux.org/keys/ncopa.asc
wget https://dl-cdn.alpinelinux.org/alpine/v3.16/releases/x86_64/alpine-virt-3.16.2-x86_64.iso.asc
gpg --import ncopa.asc
gpg --verify alpine-virt-*.iso.asc alpine-virt-*.iso
Results should appears as follows:
gpg: using RSA key 0482D84022F52DF1C4E7CD43293ACD0907D9495A
gpg: Good signature from "Natanael Copa <ncopa@alpinelinux.org>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 0482 D840 22F5 2DF1 C4E7 CD43 293A CD09 07D9 495A
Base configuration
Once Alpine has been booted, execute the setup-alpine -f answers.txt
Content of answers.txt:
KEYMAPOPTS="us us"
HOSTNAMEOPTS=host
DEVDOPTS=mdev
INTERFACESOPTS="-ar"
# DNSOPTS="-d 10.8.8.1" ## Not honored when using dhcp
TIMEZONEOPTS="UTC"
PROXYOPTS=none
APKREPOSOPTS="-1 -c"
USEROPTS="-a -u -g audio,video,wheel,netdev user"
SSHDOPTS="openssh"
NTPOPTS=none
DISKOPTS="-em sys -s 0"
Reboot & detach the mounted disk image
Proceed to configure the desktop environment with the following command:
setup-xorg-base gnome gnome-terminal gedit
rc-update add gdm
Note: I prefer Gnome due to both the aesthetics and the environment being solely Wayland rather than Xorg. Wayland has a better security model and provides window isolation. Many of the minimalist Windows Managers such as SwayWM lay claim to a pure Wayland environment, however the Wayland security model is largely ignored in implementation. At the time of writing, Gnome does still contain some Xorg dependencies in version 42. Version 43 removes the dependencies.
Hardening
Staging various configuration files
Note: There are many different ways to stage configs. This is simply using wget to pull PlagueOS’s pre-compiled configs.
### Modprobe blacklist
wget https://0xacab.org/optout/plagueos/-/raw/master/base_configs/etc/modprobe.d/blacklist.conf -P /etc/modprobe.d/
### Misc restrictions via sysctl
wget https://0xacab.org/optout/plagueos/-/raw/master/base_configs/etc/sysctl.d/10_kernel.conf -P /etc/sysctl.d/
wget https://0xacab.org/optout/plagueos/-/raw/master/base_configs/etc/sysctl.d/20_network.conf -P /etc/sysctl.d/
wget https://0xacab.org/optout/plagueos/-/raw/master/base_configs/etc/sysctl.d/30_userspace.conf -P /etc/sysctl.d/
### Block soft and hard core dumps
wget https://0xacab.org/optout/plagueos/-/raw/master/base_configs/etc/security/limits.conf -P /etc/security/
### Add hardened boot parameters
wget https://0xacab.org/optout/plagueos/-/raw/master/base_configs/etc/default/grub -P /etc/grub.d/
### Hardened SSH configuration
wget https://0xacab.org/optout/plagueos/-/raw/master/base_configs/etc/ssh/sshd_config -P /etc/ssh/
### misc. additions and increased password hashing rounds
wget https://0xacab.org/optout/plagueos/-/raw/master/base_configs/etc/login.defs -P /etc/
### Set generic machine ID
echo "b08dfa6083e7567a1921a715000001fb" > /etc/machine-id
### Clear banners
> /etc/issue
> /etc/motd
Additional sources of entropy
apk add jitterentropy-library haveged haveged-openrc
echo "jitterentropy_rng" > /etc/modules-load.d/jitterentropy.conf
Configure Awall
This is Alpine’s default front-end for interfacing with IPTables.
apk add awall
Add the following content to /etc/awall/optional/default.json:
{
"description": "block all inbound, allow essential outbound",
"variable": { "internet_if": "eth0" },
"zone": {
"internet": { "iface": "$internet_if" }
},
"policy": [
{ "in": "internet", "action": "drop" },
{ "action": "reject" }
],
"filter": [
{
"in": "_fw",
"out": "internet",
"service": [ "dns", "http", "https", "ntp" ],
"action": "accept"
}
]
}
Enable the new ruleset:
awall enable default
awall activate
Confirm that awall is running with:
awall list
Install GrapheneOS’s Hardened Memory Allocator (hmalloc)
apk add git make gcc libc-dev g++
git clone https://github.com/GrapheneOS/hardened_malloc.git
cd hardened_malloc
make \
CONFIG_SLAB_QUARANTINE_RANDOM_LENGTH=0 \
CONFIG_SLAB_QUARANTINE_QUEUE_LENGTH=0 \
CONFIG_GUARD_SLABS_INTERVAL=8
install out/libhardened_malloc.so "/usr/local/lib/libhardened_malloc.so"
wget https://0xacab.org/optout/plagueos/-/raw/master/baseConfigs/etc/ld.so.preload -P /etc/
Secure fstab configuration
cat >> /etc/fstab<< EOF
### Only allow users to see their processes. Disallow other processes.
proc /proc proc nosuid,nodev,noexec,hidepid=2,gid=proc,rw 0 0
### Mount /tmp in tmpfs. All /tmp files will be held in RAM.
tmpfs /tmp tmpfs noexec,nosuid,nodev,size=512M,mode=1777,rw 0 0
### Mount /dev/shm in tmpfs. All /dev/shm files will be held in RAM.
tmpfs /dev/shm tmpfs nodev,nosuid,noexec,size=512M,mode=1777,ro
EOF
MAC Randomization (less important for virtual environments)
apk add macchanger
cat >> /etc/rc.local<< EOF
DEVICES=( $(ls /sys/class/net/) )
for i in ${DEVICES[@]}
do
if [[ ! "$i" == "lo" ]]; then
/usr/bin/macchanger -A "$i"
fi
done
EOF
Browsing
Hardened variants of Chromium are advised, however compiled packages for the MUSL codebase are hard to come by.
To avoid pivoting towards a Chromium derivative project, the easiest path forward is to run Chromium with flags to disable various features, execute with non-persistence in incognito, and disable the JIT engine.
Install Chromium with the following command:
apk add chromium
Place the following content in /usr/bin/hchrome:
/usr/bin/chromium-browser %U --disable-reading-from-canvas --disable-3d-apis --disable-component-update --disable-background-networking --user-agent="" --no-default-browser-check --incognito --disable-breakpad --no-crash-upload --no-report-upload --disable-crash-reporter --disable-speech-synthesis-api --disable-speech-api --disable-cloud-policy-on-signin --disable-print-preview --disable-drive --disable-full-history-sync --disable-sync --js-flags="--jitless"
Note: Outside of disabling the JIT engine, the majority of the appended flags have an orientation towards privacy over security.
chmod +x /usr/bin/hchrome
Now execute hchrome
from terminal any time chromium with the privacy-centric flags are desired
Hardened Kernel Compilation
- Install dependencies:
apk add alpine-sdk linux-headers
Additionally, you may need to install other packages based on your specific configuration and the features you want to compile into the kernel.
- Use
curl
to pull downlinux-hardened
kernel
# KVER='6.6.32-hardened1' # <- example variable for kernel version
/usr/bin/curl --verbose --tlsv1.3 --proto =https -L -O --url "https://github.com/anthraxx/linux-hardened/archive/refs/tags/$KVER.tar.gz"
- Extract the source code:
tar xvf "$KVER".tar.gz
- Change to the extracted directory:
cd linux-hardened-"$KVER"
- Stage the
plague-kernel
config
wget https://0xacab.org/optout/plague-kernel/-/raw/main/hardened_host.config -O .config
- Configure, compile, and install compiled modules:
make oldconfig && make -j$(nproc)
- Stage the compiled image under the
/boot
partition:
cp arch/x86/boot/bzImage /boot/vmlinuz-linux-hardened-"$KVER"
- Finally, the bootloader, whether that be syslinux or grub, needs to be updated to recognize the newly staged kernel.
mkinitfs -o /boot/initramfs-"$KVER" "$KVER"
Routing
This will vary largely depending upon the threat model and how critical non-attribution is to the operation.
The main options are leveraging TOR, mixnets, or VPNs.
- TOR is acceptable for the threat model of non-attribution; it favors anonymity. Analysts will run into inaccessible sites and an excess of captchas (captcha hell) which will slow down the research operation. For operations where the analyst proceeds with the use of TOR, they should leverage a guest Whonix gateway and place their Alpine instance in an isolated network segment. I’ll avoid mimicking Whonix’s documentation, but for those interested, see the VirtualBox, KVM, and Physical Isolation instructions. While tailored towards Arch Linux, the same steps in this wiki apply to configuring Alpine with the isolated network and routing all traffic via the Whonix Gateway.
- VPNs are advisable in a situation where a base level of privacy is needed. One should be aware that these do not achieve anonymity, nor are they suitable where the adversary has access to netflow data. Like all software-based traffic routing, it isn’t impervious to leakage, therefore I advise routing via a set VPN configuration on physical hardware when assurance is needed.
- Mixnets are still in their adolescence and are largely unadvised.
- CTI analysts may leverage an overlay network such as I2P for pulling data that is not found elsewhere, however one could not advise leveraging this overlay network as a primary method for research.
Hardening Checklist
- Hardened kernel
- Hardened memory allocator
- Hardened boot parameters
- Modprobe blacklist
- Sysctl kernel restrictions
- Awall Packet Filter (whitelist)
- Added entropy with use of
haveged
andjitterentropy
- Full Wayland Environment
- Permission hardening & set UMASK 0077 to system-wide default
- Use of
doas
, removal ofsudo
(default with Alpine) - Hardened SSH configuration, filtered traffic, or removed software
- Secure fstab configuration (leveraging
nodev
,nosuid
, andnoexec
) - Hardened Chromium variant with JIT disabled
- Disable swap partition
- Increased password hashing rounds
Non-Attribution Checklist
- Generic hostname [host]
- Generic username [user]
- Generic machine ID
- Randomized MAC address for Network Interface Controllers (NIC)
- Hide process identifiers
- Remove system identifiers in banners [i.e. /etc/motd]
- [] Whonix-Gateway client only (for use-cases with TOR)