BlackPill is a stealthy Linux rootkit made in Rust.
The rootkit is composed of multiple modules (talking about Rust modules, not kernel modules):
- defense evasion: hide files, processes, network connections, etc.
- hooking: hook syscalls and IDT
- hypervisor: create a virtual machine to execute malicious code
- persistence: make the rootkit persistent after reboot and resilient to supression
- utils: various utilities
The architecture looks as the following:
And here is how malicious code is executed from C2 to VM guest:
C2 sends crafted assembled x86_64 mnemonics to the rootkit, which then sends it to the VM guest to execute it. The VM guest is isolated from the host and can be used to execute malicious code.
Kernel do not see incoming malicous packets as they are filtered by the eBPF XDP program and sent to the LKM module, and outgoing packets are modified by the eBPF TC program.
Important
This project is still a work-in-progress.
Not all features are working!
Feel free to submit issues or pull requests.
Hooking is a fundamental capability of the rootkit, implemented using kprobes
in the Linux kernel. This technique intercepts and redirects the execution of system functions to monitor or modify their behavior. In the context of this rootkit, kprobes
provides a powerful mechanism to interact with kernel functions without altering the source code directly.
To ensure stealth, the rootkit employs two primary anti-detection mechanisms:
-
Removing the Module from the Kernel Module List
When a kernel module is loaded, it is added to the kernel's module list, visible via tools likelsmod
or/proc/modules
. To prevent detection:- The rootkit manually removes itself from this list.
- Despite being removed from the list, the module remains operational, enabling continued execution of its functionality.
-
Hooking the
filldir64
Function to Hide a Specific Directory
To conceal files used by the rootkit, a hook is implemented on thefilldir64
function. This function is invoked when a process reads directory contents (e.g., viagetdents
orreaddir
system calls).- Hooking Process:
- The rootkit intercepts the
filldir64
function usingkprobes
. - During execution, the handler inspects directory entries returned to the user.
- If an entry matches the
/BLACKPILL-BLACKPILL
directory (used to store critical rootkit files), it is filtered out and not returned to the user. - All other directory entries are returned normally, ensuring transparency for user-space tools.
- The rootkit intercepts the
- Hooking Process:
-
Using eBPF XDP and TC Programs to Modify Ingress and Egress network traffic To normalize our malicious network communications, we use eBPF XDP (eXpress Data Path) and TC (Traffic Control) programs. As such, we can:
- Intercept specific incoming (ingress) packets with the XDP program at the lowest network level by matching the crafted TCP payload signature from our C2, which we then redirect to a custom BPF map for VM/LKM processing.
- Intercept specific outgoing (egress) packets with the TC program by matching TCP packets generated by VM/LKM, which we then modify by overwriting their payload with our C2 response data. The original packets are automatically retransmitted by TCP, maintaining the appearance of legitimate traffic.
-
Initial System Configuration
- Enable hardware virtualization extensions (Intel VT-x or AMD-V) in the BIOS/UEFI.
- Configure the control registers (CR0, CR4, and IA32_EFER) to switch into VMX (Virtual Machine Extensions) or SVM (Secure Virtual Machine) mode.
-
Entering VMX or SVM Mode
- Initialize the virtualization-specific data structures (VMCS for Intel or VMCB for AMD).
- Program the processor features, such as VM exits, to handle interactions between the guest and the host.
-
Managing Transitions Between Host and Guest
- Configure the entry and exit points for virtual machines (VM entry/exit).
- Implement logic to intercept sensitive system calls made by the guest and analyze their effects.
-
Guest System Creation
- Allocate memory for the guest and initialize its resources (registers, stack, etc.).
-
Communication
- Use communication channels between the rootkit and the hypervisor to transmit commands or data.
Follow these steps to implement a basic hypervisor, ensuring that hardware configurations and system resources are properly managed for a secure and functional environment.
Persistence is a critical capability of any rootkit, enabling it to maintain control over the target system even after a reboot.
In its current implementation, the persistence mechanism demonstrates its functionality by creating a test file in the file system using the /bin/touch
command. This placeholder action showcases the rootkit's ability to execute privileged operations and can be extended to implement more advanced persistence strategies.
Multiple steps needs to be done before compiling our rootkit. The development environment is composed of :
- a simple Alpine Linux image providing essential tools
- a custom compiled kernel with Rust activated
- a simple QEMU virtual machine accelerated by KVM
Start by cloning the repository and its shallow submodules :
git clone [email protected]:DualHorizon/blackpill.git --recursive --depth 1
On an arch-based distribution :
sudo pacman -S qemu-base qemu-desktop docker grub
On an arch-based Linux distribution, install Rust and other dependencies :
sudo pacman -S rust rust-src rust-bindgen
sudo pacman -S clang lld llvm
Then we'll need Rust sources and bindgen :
rustup component add rust-src clippy rustfmt
cargo install --locked bindgen-cli
Make sure you can start compiling your kernel with Rust by running in folder linux/
:
$ cd blackpill
$ pushd linux
$ make LLVM=1 rustavailable
Rust is available!
$ popd
Launch the first time setup task which configures and compiles the kernel :
make first-time-setup
Important
If you are asked of customizing options, press Enter each time.
You can compile the Rust kernel module (out-of-tree) with :
make
Launch the VM with :
make vm
Inside the VM, you are auto logged in root
. You can enable the module:
$ modprobe blackpill
# you can check kernel logs with
$ dmesg
Once the vm started you can use the command above to escalate your privileges:
mkdir ImFeelingRootNow_<PID>
Replace <PID>
with the process id of the process you want to escalate the privilege to.
Setup the python client:
cd blackpill-c2
poetry install
poetry shell
python client.py
After launching the client with your args ([ip] [port]) you should get:
$ python client.py 0.0.0.0 1339
Connected to rootkit!
Then you can use the help command to display available commands:
blackpill: help
Available Commands
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Command ┃ Description ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ read_virt_memory <address> │ Read 4 bytes (32 bits) memory at 'address' │
│ write_virt_memory <address> <value> │ Write 4 bytes (32 bits) memory at 'address' │
│ launch_userland_binary <path> │ Launch a userland binary at 'path' │
│ change_msr <msr> <value> │ Change the value of a Model Specific Register (MSR) │
│ read_phys_memory <address> <value> │ Read 4 bytes (32 bits) of physical memory at 'address' │
│ write_phys_memory <address> <value> │ Write 4 bytes (32 bits) of physical memory at 'address' │
│ stop_execution │ Stop the execution of the guest VM │
│ change_vmcs_field <field> <value> │ Change a VMCS field to 'value' │
│ help │ Show this help message │
└─────────────────────────────────────┴─────────────────────────────────────────────────────────┘
Environment setup: