Unsachgemäße Kontrolle dynamisch verwalteter Code-Ressourcen

Beschreibung

Unsachgemäße Kontrolle dynamisch verwalteter Code-Ressourcen tritt auf, wenn Software das Lesen von oder Schreiben in dynamisch verwaltete Code-Ressourcen wie Variablen, Objekte, Klassen, Attribute, Funktionen oder ausführbare Anweisungen nicht ordnungsgemäß einschränkt. Viele Programmiersprachen bieten mächtige Laufzeitfähigkeiten für dynamische Codeausführung, Reflektion und Objektmanipulation. Wenn diese Features mit nicht vertrauenswürdigen Eingaben ohne ordnungsgemäße Validierung verwendet werden, können Angreifer Code-Ressourcen manipulieren, um beliebige Befehle auszuführen, Anwendungsverhalten zu ändern oder Sicherheitskontrollen zu umgehen.

Risiko

Diese Schwachstellenklasse umfasst mehrere gefährliche Angriffsmuster. Code-Injection ermöglicht Angreifern das Einfügen und Ausführen beliebigen Codes. Unsichere Reflektion ermöglicht die Instanziierung beliebiger Klassen und den Aufruf von Methoden. Deserialisierungsangriffe rekonstruieren bösartige Objekte aus nicht vertrauenswürdigen Daten. Dynamische Evaluierungsfunktionen (eval, exec) können vom Angreifer bereitgestellte Code-Strings ausführen. Die Auswirkungen reichen von Informationsoffenlegung bis zur vollständigen Systemkompromittierung, abhängig von den Privilegien der betroffenen Anwendung und der Art der dynamisch verwalteten Ressourcen, die ausgenutzt werden.

Lösung

Vermeiden Sie die Verwendung dynamischer Codeausführungs-Features mit nicht vertrauenswürdigen Eingaben, wann immer möglich. Wenn dynamische Features verwendet werden müssen, implementieren Sie strenge Eingabevalidierung mit Allowlists für erlaubte Werte. Deaktivieren Sie unnötige dynamische Features in der Laufzeitumgebung. Verwenden Sie parametrisierte Schnittstellen anstelle von string-basierter Code-Konstruktion. Für Reflektion validieren Sie Klassennamen gegen eine explizite Allowlist. Für Deserialisierung verwenden Sie Typ-Whitelisting oder vermeiden Sie die Deserialisierung nicht vertrauenswürdiger Daten vollständig. Implementieren Sie Sandboxing für Codeausführungs-Features. Verwenden Sie statische Analysetools, um gefährliche dynamische Code-Muster zu erkennen.

Häufige Auswirkungen

AuswirkungDetails
IntegritätBereich: Integrität

Unbefugten Code oder Befehle ausführen - Angreifer können beliebigen Code durch dynamische Code-Features injizieren und ausführen.
IntegritätBereich: Integrität

Ausführungslogik ändern - Dynamische Manipulation von Code-Ressourcen kann das Anwendungsverhalten ändern.
VertraulichkeitBereich: Vertraulichkeit

Anwendungsdaten lesen - Angreifer können über dynamisch instanziierte Objekte oder Reflektion auf sensible Daten zugreifen.

Beispielcode + Lösungscode

Verwundbarer Code

# Verwundbar: Dynamische Code-Ausführung mit eval
def vulnerable_calculator(expression):
    # Verwundbar: eval() führt beliebigen Python-Code aus
    result = eval(expression)
    return result

# Normale Verwendung: vulnerable_calculator("2 + 2")
# Angriff: vulnerable_calculator("__import__('os').system('rm -rf /')")
// Verwundbar: Unsichere Reflektion
public class VulnerableDispatcher {

    public void dispatchCommand(HttpServletRequest request) throws Exception {
        String commandClass = request.getParameter("command");
        String action = request.getParameter("action");

        // Verwundbar: Beliebige Klassen-Instanziierung
        Class<?> clazz = Class.forName(commandClass);
        Object instance = clazz.getDeclaredConstructor().newInstance();

        // Verwundbar: Beliebiger Methoden-Aufruf
        Method method = clazz.getMethod(action);
        method.invoke(instance);

        // Angreifer kann jede Klasse instanziieren und jede Methode aufrufen:
        // ?command=java.lang.Runtime&action=exec&...
    }
}
// Verwundbar: Unsichere Deserialisierung
import java.io.*;

public class VulnerableDeserializer {

    public Object deserialize(byte[] data) throws Exception {
        // Verwundbar: Deserialisierung nicht vertrauenswürdiger Daten
        ByteArrayInputStream bis = new ByteArrayInputStream(data);
        ObjectInputStream ois = new ObjectInputStream(bis);

        // Angreifer kann bösartige serialisierte Objekte erstellen
        // die während der Deserialisierung Code ausführen
        return ois.readObject();
    }
}

Sicherer Code

# Behoben: Sichere Auswertung von Ausdrücken
import ast
import operator

# Erlaubte Operatoren für sichere mathematische Auswertung
SAFE_OPERATORS = {
    ast.Add: operator.add,
    ast.Sub: operator.sub,
    ast.Mult: operator.mul,
    ast.Div: operator.truediv,
    ast.Pow: operator.pow,
    ast.USub: operator.neg,
}

def safe_eval(expr):
    """Sicher einen mathematischen Ausdruck auswerten"""
    try:
        tree = ast.parse(expr, mode='eval')
        return _eval_node(tree.body)
    except (ValueError, TypeError, SyntaxError) as e:
        raise ValueError(f"Ungültiger Ausdruck: {e}")

def _eval_node(node):
    if isinstance(node, ast.Constant):  # Zahlen
        return node.value
    elif isinstance(node, ast.BinOp):  # Binäre Operationen
        if type(node.op) not in SAFE_OPERATORS:
            raise ValueError(f"Nicht unterstützter Operator: {type(node.op)}")
        left = _eval_node(node.left)
        right = _eval_node(node.right)
        return SAFE_OPERATORS[type(node.op)](left, right)
    elif isinstance(node, ast.UnaryOp):  # Unäre Operationen
        if type(node.op) not in SAFE_OPERATORS:
            raise ValueError(f"Nicht unterstützter Operator: {type(node.op)}")
        operand = _eval_node(node.operand)
        return SAFE_OPERATORS[type(node.op)](operand)
    else:
        raise ValueError(f"Nicht unterstützter Knotentyp: {type(node)}")

# Sicher: safe_eval("2 + 2") gibt 4 zurück
# Sicher: safe_eval("__import__('os')") wirft ValueError
// Behoben: Command-Pattern mit Whitelist
public class FixedDispatcher {

    // Whitelist erlaubter Befehle
    private static final Map<String, Class<? extends Command>> ALLOWED_COMMANDS;

    static {
        ALLOWED_COMMANDS = new HashMap<>();
        ALLOWED_COMMANDS.put("view", ViewCommand.class);
        ALLOWED_COMMANDS.put("edit", EditCommand.class);
        ALLOWED_COMMANDS.put("delete", DeleteCommand.class);
    }

    public void dispatchCommand(HttpServletRequest request) throws Exception {
        String commandName = request.getParameter("command");

        // Behoben: Nur gewhitelistete Befehle erlauben
        Class<? extends Command> commandClass = ALLOWED_COMMANDS.get(commandName);

        if (commandClass == null) {
            throw new SecurityException("Unbekannter Befehl: " + commandName);
        }

        // Sichere Instanziierung bekannter Klasse
        Command command = commandClass.getDeclaredConstructor().newInstance();
        command.execute(request);
    }
}

interface Command {
    void execute(HttpServletRequest request);
}
// Behoben: Sichere Deserialisierung mit Typ-Filterung
import java.io.*;

public class FixedDeserializer {

    // Whitelist erlaubter Klassen
    private static final Set<String> ALLOWED_CLASSES = Set.of(
        "com.myapp.SafeClass1",
        "com.myapp.SafeClass2",
        "java.lang.String",
        "java.lang.Integer"
    );

    public Object deserialize(byte[] data) throws Exception {
        ByteArrayInputStream bis = new ByteArrayInputStream(data);

        // Behoben: Filternden ObjectInputStream verwenden
        ObjectInputStream ois = new ObjectInputStream(bis) {
            @Override
            protected Class<?> resolveClass(ObjectStreamClass desc)
                    throws IOException, ClassNotFoundException {

                String className = desc.getName();

                // Nur gewhitelistete Klassen erlauben
                if (!ALLOWED_CLASSES.contains(className)) {
                    throw new InvalidClassException(
                        "Unbefugte Klasse: " + className);
                }

                return super.resolveClass(desc);
            }
        };

        return ois.readObject();
    }
}

CVE-Beispiele

  • CVE-2022-2054: Anwendung führte bösartige Strings aus, die an eval()-Funktion übergeben wurden.
  • CVE-2018-1000613: Unsichere Reflektion während Deserialisierung ermöglichte Remote-Code-Ausführung.
  • CVE-2015-8103: Java-Deserialisierungs-Schwachstelle ermöglichte beliebige Codeausführung.

Verwandte CWEs

  • CWE-664: Unsachgemäße Kontrolle einer Ressource durch ihre Lebensdauer (Eltern)
  • CWE-94: Unsachgemäße Kontrolle der Code-Generierung ('Code Injection') (Kind)
  • CWE-470: Verwendung extern kontrollierter Eingaben zur Auswahl von Klassen oder Code ('Unsichere Reflektion') (Kind)
  • CWE-502: Deserialisierung nicht vertrauenswürdiger Daten (Kind)
  • CWE-914: Unsachgemäße Kontrolle dynamisch identifizierter Variablen (Kind)
  • CWE-915: Unsachgemäß kontrollierte Änderung dynamisch bestimmter Objektattribute (Kind)

Referenzen

  1. MITRE Corporation. "CWE-913: Improper Control of Dynamically-Managed Code Resources." https://cwe.mitre.org/data/definitions/913.html
  2. OWASP. "Code Injection." https://owasp.org/www-community/attacks/Code_Injection
  3. OWASP. "Deserialization Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html