Secure Coding: Probleme durch CWE 416 – Use After Free – in Java vermeiden

Die Common Weakness Enumeration CWE-416 beschreibt Sicherheitslücken beim Speichermanagement, die auch Java betreffen – Abhilfe schafft die Cleaner-API.

vorlesen Druckansicht 33 Kommentare lesen
Mann zeigt auf aufgetürmte Code-Ressourcen

(Bild: erstellt mit Chat-GPT / DALL-E)

Lesezeit: 11 Min.
Von
  • Sven Ruppert
Inhaltsverzeichnis

Die in Common Weakness Enumeration CWE-416 beschriebene Schwachstelle Use After Free (UAF) tritt auf, wenn ein Programm einen Zeiger weiterhin verwendet, nachdem dieser freigegeben wurde. Das kann Sicherheitslücken öffnen und zu undefiniertem Verhalten führen, einschließlich Abstürzen bis hin zur Datenbeschädigung. Das Problem entsteht dadurch, dass der vom Zeiger referenzierte Speicher gegebenenfalls für andere Zwecke neu zugewiesen wird. Das wiederum können Angreifer ausnutzen.

Secure Coding – Sven Ruppert
Sven Ruppert

Seit 1996 programmiert Sven Java in Industrieprojekten und seit über 15 Jahren weltweit in Branchen wie Automobil, Raumfahrt, Versicherungen, Banken, UN und Weltbank. Seit über 10 Jahren ist er von Amerika bis nach Neuseeland als Speaker auf Konferenzen und Community Events, arbeitete als Developer Advocate für JFrog und Vaadin und schreibt regelmäßig Beiträge für IT-Zeitschriften und Technologieportale. Neben seinem Hauptthema Core Java beschäftigt er sich mit TDD und Secure Coding Practices.

Falsche Speicherverwaltung: Vergessen, Zeiger zu annullieren, nachdem Speicher freigegeben wurde.

Baumelnde Zeiger: Verweise auf freigegebenen Speicher beibehalten und später darauf zugreifen.

Doppelte Freigabe: Speicher wird mehr als einmal freigegeben, was zu mehrfachen Vorgängen am gleichen Speicherort führen kann.

Mögliche Folgen durch CWE-416

Sicherheitslücken: Angreifer können UAF ausnutzen, um beliebigen Code auszuführen, Berechtigungen zu erweitern oder einen Denial-of-Service (DoS) auszulösen.

Datenkorruption: UAF kann zu unerwarteten Datenänderungen führen, die gegebenenfalls Programminstabilität oder fehlerhaftes Verhalten nach sich ziehen.

Programm stürzt ab: Der Zugriff auf freigegebenen Speicher kann zu Segmentierungs- oder anderen Laufzeitfehlern führen.

Videos by heise

Durch die automatische Garbage Collection schützt Java von sich aus vor vielen Arten von Speicherverwaltungsproblemen, einschließlich Use-After-Free-Schwachstellen. Allerdings können dennoch in Java UAF-ähnliche Probleme auftreten, wenn andere Ressourcen als der Speicher (etwa Datei-Handles oder Netzwerkverbindungen) falsch verwaltet werden. Nachfolgend finden Sie einige Szenarien in Java, die UAF ähneln, sowie Strategien zu deren Entschärfung.

Szenario 1: Missmanagement externer Ressourcen

Obwohl der Speicher in Java vom Garbage Collector (GC) verwaltet wird, können andere Ressourcen wie Datei-Handles oder Netzwerk-Sockets falsch verwaltet werden, was zu Problemen bei der Verwendung nach dem Schließen führt.

Codebeispiel:

import java.io.*;

public class UseAfterFreeExample {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("example.txt");
            // Use the file input stream...
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        // Attempt to use the file input stream after it has been closed
        try {
            int data = fis.read();  // This will throw an IOException
        } catch (IOException e) {
            System.out.println("Attempted to read from a closed stream.");
        }
    }
}

In diesem Beispiel führt der Versuch, aus der Ressource zu lesen, nach dem Schließen des FileInputStream zu einer IOException.

Automatisches Ressourcenmanagement mit Try-With-Resources:

Verwenden Sie die in Java 7 eingeführte Try-With-Resources-Anweisung, die sicherstellt, dass jede Ressource am Ende der Anweisung geschlossen wird.

import java.io.*;

   public class UseAfterFreeFixed {
       public static void main(String[] args) {
           try (FileInputStream fis = new FileInputStream("example.txt")) {
               // Use the file input stream...
           } catch (IOException e) {
               e.printStackTrace();
           }

           // fis is automatically closed here
           // Attempting to use fis here will cause a compilation error
       }
   }

Explizite Nichtig-Erklärung:

Setzen Sie Referenzen nach dem Schließen auf Null, um eine versehentliche Wiederverwendung zu vermeiden.

   import java.io.*;

   public class UseAfterFreeWithNull {
       public static void main(String[] args) {
           FileInputStream fis = null;
           try {
               fis = new FileInputStream("example.txt");
               // Use the file input stream...
           } catch (IOException e) {
               e.printStackTrace();
           } finally {
               if (fis != null) {
                   try {
                       fis.close();
                   } catch (IOException e) {
                       e.printStackTrace();
                   } finally {
                       fis = null;  // Prevent reuse
                   }
               }
           }

           // fis is now null, so the following attempt 
           // to use it will fail at compile-time
           if (fis != null) {
               try {
                   int data = fis.read();  // This will not compile since fis is null
               } catch (IOException e) {
                   System.out.println("Attempted to read from a closed stream.");
               }
           }
       }
   }

Szenario 2: Unangemessene Verwendung schwacher Referenzen

Auch wenn es sich hierbei nicht unmittelbar um ein UAF-Problem handelt, kann der Missbrauch schwacher Referenzen dennoch negative Folgen haben, wenn auf Objekte zugegriffen wird, nachdem sie vom Garbage Collector zurückgefordert wurden.

import java.lang.ref.WeakReference;

public class WeakReferenceExample {
    public static void main(String[] args) {
        Object obj = new Object();
        WeakReference<Object> weakRef = new WeakReference<>(obj);

        obj = null;  // Eligible for garbage collection

        System.gc();  // Suggest garbage collection

        // Attempt to use the object after it may have been collected
        if (weakRef.get() != null) {
            System.out.println("Object is still alive.");
        } else {
            System.out.println("Object has been garbage collected.");
        }
    }
}

In diesem Beispiel kann der Zugriff auf die schwache Referenz nach dem Annullieren der starken Referenz und dem Vorschlagen der Speicherbereinigung zu Null führen, was darauf hinweist, dass das Objekt vom GC bereits eingesammelt wurde. Vermeiden Sie die Verwendung schwacher Referenzen für kritische Objekte, die noch benötigt werden.

Überprüfen Sie immer, ob eine schwache Referenz gelöscht wurde, bevor Sie sie verwenden:

   if (weakRef.get() != null) {
       // Safe to use the object
       Object obj = weakRef.get();
       // Use obj
   } else {
       // Handle the case where the object has been collected
       System.out.println("Object has been garbage collected.");
   }

Während die automatische Speicherverwaltung von Java das Risiko herkömmlicher UAF-Schwachstellen verringert, müssen Entwicklerinnen und Entwickler dennoch bei der Ressourcenverwaltung und schwachen Referenzen vorsichtig sein, um vergleichbare Probleme zu vermeiden. Moderne Java-Funktionen wie Try-with-Ressourcen und die Berücksichtigung von Objektreferenzen können dabei helfen, robusten und fehlerfreien Code aufrechtzuerhalten.

Hier sind einige im Zusammenhang mit CWE-416-Schwachstellen in Java zu beachtende CVEs:

CVE-2022-45146

Diese Sicherheitslücke findet sich in der FIPS-Java-API von Bouncy Castle BC-FJA vor Version 1.0.2.4. Änderungen im JVM-Garbage Collector in Java 13 und späteren Versionen können dieses Problem auslösen und dazu führen, dass vom Modul verwendete temporäre Schlüssel auf Null gesetzt werden, während sie noch verwendet werden. Dies führt zu Fehlern oder potenziellem Informationsverlust. Benutzer der FIPS-Konformität sind davon nicht betroffen, da dies für Java 7, 8 und 11 gilt.

CVE-2023-38703

Diese CVE betrifft die PJSIP-Multimedia-Kommunikationsbibliothek, die in mehreren Sprachen, einschließlich Java, geschrieben ist. Das Problem tritt auf, wenn die Synchronisierung zwischen SRTP-Medien auf höherer Ebene und Transport auf niedrigerer Ebene, wie UDP, nicht aufrechterhalten wird – was zu einer UAF-Schwachstelle führt. Dies kann zu einer unerwarteten Beendigung der Anwendung oder einer Speicherbeschädigung führen.

Die beschriebenen Beispiele verdeutlichen die Bedeutung einer ordnungsgemäßen Speicher- und Ressourcenverwaltung in Java-Anwendungen, auch wenn die Sprache den Speicher durch Garbage Collection verwaltet. Weitere Informationen zu diesen Schwachstellen finden Sie in der National Vulnerability Database (NVD) oder in spezifischen Hinweisen auf Plattformen wie GitHub und mvnrepository.