
Exploit del kernel per CVE-2016-6187 (Escalation locale dei privilegi)
Exploit del kernel per CVE-2016-6187 (Escalation dei privilegi locali) (versioni interessate: < 4.6.5)
L'exploit è stato sviluppato contro un kernel 4.6 compilato personalizzato + buildroot (kconfig in arrivo)
e sconfigge KASLR, SMEP, SMAP (KPTI non disponibile per 4.6)
La vulnerabilità sfruttata è un heap based single nullbyte overflow in uno degli hook LSM di AppArmor (setprocattr). Vedi https://nvd.nist.gov/vuln/detail/CVE-2016-6187
Le misurazioni iniziali indicano una probabilità del 94.44% di generare una shell di root. In 1 caso su 18, l'exploit causerà un oops del kernel. È necessario ulteriore lavoro per stabilizzare completamente il kernel dopo aver ottenuto l'accesso di root.
Abusiamo del nullbyte overflow per corrompere il LSB di un puntatore della freelist in kmalloc-128, facendo sì che
punti a un oggetto già allocato. Successivamente, liberiamo quell'oggetto allocato, causando uno scenario di double free.
Ora possiamo allocare due strutture sovrapposte dalla cache kmalloc-128. La prima struttura è un oggetto
con contenuto leggibile dallo spazio utente. Per questo, ho scelto ip6_sf_socklist. La seconda struttura,
che verrà allocata sopra ip6_sf_socklist, dovrebbe contenere puntatori del kernel - rfkill_data è una buona scelta.
Contiene un puntatore allo heap (lista vuota rfkill_data.events) e un puntatore a dati globali verso rfkill_fds (rfkill_data.list.prev).
Una volta che rfkill_data è allocato sopra ip6_sf_socklist, possiamo recuperare i puntatori del kernel chiamando
getsockopt(sock, IPPROTO_IPV6, MCAST_MSFILTER, gsf, &optlen). La base del testo del kernel può quindi essere derivata da rfkill_fds.
Quindi causiamo un secondo scenario di double free, ma questa volta abusiamo del processo di collegamento della coda dei messaggi per sovrascrivere il puntatore della freelist
e ingannare l'allocatore affinché restituisca un chunk da kmalloc-96 quando in realtà richiediamo un chunk da kmalloc-128.
Possiamo quindi sfruttare la discrepanza di dimensione per fare overflow nel puntatore della freelist di un chunk kmalloc-96, fornendoci la primitiva per
allocare indirizzi arbitrari. Nel nostro caso, puntiamo alla struttura ptmx_fops e sovrascriviamo il callback ptmx_fops.unlocked_ioctl
con un puntatore al nostro gadget stack pivot.
Chiamare ioctl su /dev/ptmx attiva il gadget pivot e spostiamo lo stack su uno stack fittizio in kmalloc-128 dove abbiamo
già preparato la nostra catena ROP di primo stadio.
Poiché abbiamo spazio limitato sullo stack fittizio di kmalloc-128 (non sappiamo se il chunk successivo sarà nello stesso slab), il compito
della catena ROP di primo stadio è copiare la catena ROP di secondo stadio dallo spazio utente nel buffer del log dmesg del kernel (__log_buf)
e spostare lo stack su __log_buf.
La catena ROP di secondo stadio cerca quindi di riparare il kernel (ripristinando ptmx_fops, ...) prima di chiamare commit_creds(prepare_kernel_cred(NULL))
per aumentare i privilegi.
E infine esce dal kernelspace tramite iretq e torniamo, con privilegi elevati, alla nostra subroutine execve("/bin/sh", 0, 0).
Gli utenti non privilegiati devono essere in grado almeno di aprire /dev/rfkill. Alcune distribuzioni potrebbero
non permetterlo. Ho verificato Ubuntu e Fedora, entrambe consentono agli utenti non privilegiati di aprire /dev/rfkill
(sebbene Fedora utilizzi SELinux invece di AppArmor)
La sezione seguente spiega come riprodurre l'escalation dei privilegi locali in qemu-x86_64.
Per prima cosa, dovrai ottenere un initrd e un kernel bzImage. Ci sono due modi:
Il modo più semplice e veloce. Puoi trovare tutti i componenti richiesti in prebuilt/.
Oppure, alternativamente, puoi anche compilare i componenti da solo. Basta recuperare il
kernel 4.6 e usare
il file x86_64_vuln_kern_defconfig fornito in config/ per compilare il bzImage.
Per l'initrd potresti, ad esempio, usare buildroot. Assicurati
solo di impostare i permessi corretti per /dev/rfkill nel tuo init
chmod 664 /dev/rfkill
Questo è lo stesso di Ubuntu-16.04 e molte altre distribuzioni. Inoltre non dimenticare di compilare
l'exploit e metterlo nel tuo initrd
make
Una volta ottenuti i componenti, avvia la macchina vulnerabile
./boot.sh <path/to/bzImage> <path/to/rootfs.cpio>
ed esegui l'exploit
./exploit
Potresti dover eseguire l'exploit più volte, a seconda se abbiamo azzerato un LSB che era già zero. Nota anche che c'è ancora una leggera probabilità (forse 10%-20%?) di crash dopo aver ottenuto l'accesso di root - ci sto lavorando.
Divertiti.
ptmx_fops per aumentare la stabilitàkmalloc-128 per aumentare la stabilità