DDexec / EverythingExec

Reading time: 5 minutes

tip

Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Supporta HackTricks

Contesto

In Linux, per eseguire un programma, deve esistere come file, deve essere accessibile in qualche modo attraverso la gerarchia del file system (questo è proprio come funziona execve()). Questo file può risiedere su disco o in ram (tmpfs, memfd) ma hai bisogno di un percorso. Questo ha reso molto facile controllare cosa viene eseguito su un sistema Linux, rende facile rilevare minacce e strumenti dell'attaccante o prevenire che provino a eseguire qualsiasi cosa di loro (e. g. non consentire agli utenti non privilegiati di posizionare file eseguibili ovunque).

Ma questa tecnica è qui per cambiare tutto questo. Se non puoi avviare il processo che desideri... allora dirotta uno già esistente.

Questa tecnica ti consente di bypassare tecniche di protezione comuni come read-only, noexec, whitelisting dei nomi dei file, whitelisting degli hash...

Dipendenze

Lo script finale dipende dai seguenti strumenti per funzionare, devono essere accessibili nel sistema che stai attaccando (per impostazione predefinita li troverai ovunque):

dd
bash | zsh | ash (busybox)
head
tail
cut
grep
od
readlink
wc
tr
base64

La tecnica

Se sei in grado di modificare arbitrariamente la memoria di un processo, allora puoi prenderne il controllo. Questo può essere utilizzato per dirottare un processo già esistente e sostituirlo con un altro programma. Possiamo ottenere questo sia utilizzando la syscall ptrace() (che richiede di avere la possibilità di eseguire syscall o di avere gdb disponibile sul sistema) o, più interessantemente, scrivendo in /proc/$pid/mem.

Il file /proc/$pid/mem è una mappatura uno a uno dell'intero spazio degli indirizzi di un processo (e. g. da 0x0000000000000000 a 0x7ffffffffffff000 in x86-64). Questo significa che leggere o scrivere in questo file a un offset x è lo stesso che leggere o modificare i contenuti all'indirizzo virtuale x.

Ora, abbiamo quattro problemi di base da affrontare:

  • In generale, solo root e il proprietario del programma del file possono modificarlo.
  • ASLR.
  • Se proviamo a leggere o scrivere in un indirizzo non mappato nello spazio degli indirizzi del programma, otterremo un errore I/O.

Questi problemi hanno soluzioni che, sebbene non siano perfette, sono buone:

  • La maggior parte degli interpreti di shell consente la creazione di descrittori di file che saranno poi ereditati dai processi figli. Possiamo creare un fd che punta al file mem della shell con permessi di scrittura... quindi i processi figli che utilizzano quel fd saranno in grado di modificare la memoria della shell.
  • ASLR non è nemmeno un problema, possiamo controllare il file maps della shell o qualsiasi altro dal procfs per ottenere informazioni sullo spazio degli indirizzi del processo.
  • Quindi dobbiamo lseek() sul file. Dalla shell questo non può essere fatto a meno di utilizzare il famigerato dd.

In maggior dettaglio

I passaggi sono relativamente facili e non richiedono alcun tipo di competenza per comprenderli:

  • Analizza il binario che vogliamo eseguire e il loader per scoprire quali mappature necessitano. Poi crea un "shell"code che eseguirà, in linea di massima, gli stessi passaggi che il kernel esegue ad ogni chiamata a execve():
  • Crea le suddette mappature.
  • Leggi i binari in esse.
  • Imposta i permessi.
  • Infine, inizializza lo stack con gli argomenti per il programma e posiziona il vettore ausiliario (necessario per il loader).
  • Salta nel loader e lascia che faccia il resto (carica le librerie necessarie per il programma).
  • Ottieni dal file syscall l'indirizzo a cui il processo tornerà dopo la syscall che sta eseguendo.
  • Sovrascrivi quel luogo, che sarà eseguibile, con il nostro shellcode (attraverso mem possiamo modificare pagine non scrivibili).
  • Passa il programma che vogliamo eseguire allo stdin del processo (sarà read() da detto "shell"code).
  • A questo punto spetta al loader caricare le librerie necessarie per il nostro programma e saltare in esso.

Controlla lo strumento in https://github.com/arget13/DDexec

EverythingExec

Ci sono diverse alternative a dd, una delle quali, tail, è attualmente il programma predefinito utilizzato per lseek() attraverso il file mem (che era l'unico scopo per utilizzare dd). Queste alternative sono:

bash
tail
hexdump
cmp
xxd

Impostando la variabile SEEKER puoi cambiare il seeker utilizzato, e. g.:

bash
SEEKER=cmp bash ddexec.sh ls -l <<< $(base64 -w0 /bin/ls)

Se trovi un altro seeker valido non implementato nello script, puoi comunque usarlo impostando la variabile SEEKER_ARGS:

bash
SEEKER=xxd SEEKER_ARGS='-s $offset' zsh ddexec.sh ls -l <<< $(base64 -w0 /bin/ls)

Blocca questo, EDRs.

Riferimenti

tip

Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Supporta HackTricks