Aufruf einer nicht-ubiquitären API

Beschreibung

Aufruf einer nicht-ubiquitären API ist eine Schwachstelle, bei der ein Produkt eine API-Funktion verwendet, die nicht auf allen Versionen der Zielplattform oder des Betriebssystems existiert. Dies umfasst Funktionen, die in neueren Versionen hinzugefügt, in bestimmten Versionen veraltet oder entfernt wurden, oder nur auf bestimmten Plattformvarianten verfügbar sind. Wenn Code sich auf solche APIs ohne ordnungsgemäße Versionsprüfung oder Fallback-Mechanismen verlässt, kann die Anwendung auf Systemen, wo die API nicht verfügbar ist, nicht starten oder sich falsch verhalten. Dies ist besonders problematisch für sicherheitsbezogene Funktionen, die auf älteren Systemen fehlen können.

Risiko

Die Verwendung nicht-ubiquitärer APIs erzeugt erhebliche Bereitstellungs- und Sicherheitsrisiken. Anwendungen können abstürzen oder nicht starten auf Plattformen, denen die erforderlichen Funktionen fehlen. Sicherheitsfeatures, die von neueren APIs abhängen, können auf älteren Systemen stillschweigend fehlschlagen und Benutzer ungeschützt lassen. Funktionen, die aus Sicherheitsgründen veraltet sind, können noch verwendet werden und Systeme bekannten Schwachstellen aussetzen. Das Verhalten der Anwendung wird über verschiedene Plattformversionen inkonsistent, was Tests und Support erschwert. Angreifer können gezielt ältere Systeme angreifen, wo Sicherheits-APIs fehlen. Laufzeitfehler durch fehlende Funktionen bieten schlechte Benutzererfahrung und potenzielle Denial-of-Service.

Lösung

Identifizieren Sie die minimale Plattformversion, die Ihre Anwendung unterstützen muss, und dokumentieren Sie diese klar. Verwenden Sie Compile-Zeit-Prüfungen, um API-Verfügbarkeit zu verifizieren, wenn möglich. Implementieren Sie Laufzeit-Versionsprüfungen vor dem Aufruf von APIs, die möglicherweise nicht existieren. Stellen Sie Fallback-Implementierungen für kritische Funktionalität bereit, wenn primäre APIs nicht verfügbar sind. Testen Sie sowohl auf der ältesten als auch auf der neuesten unterstützten Plattformversion. Verwenden Sie Plattformabstraktionsschichten, die Versionsunterschiede intern behandeln. Vermeiden Sie veraltete Funktionen und planen Sie Migrationspfade, wenn APIs zur Entfernung vorgesehen sind.

Häufige Auswirkungen

AuswirkungDetails
SonstigeBereich: Sonstige

Qualitätsverschlechterung - Anwendung schlägt fehl oder verhält sich inkonsistent auf Plattformen, denen die erforderlichen API-Funktionen fehlen.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Absturz, Beendigung oder Neustart - Aufruf nicht existierender Funktionen verursacht Laufzeitfehler und Anwendungsabstürze.

Beispielcode

Verwundbarer Code

// Verwundbar: Verwendung von Windows Vista+ API ohne Versionsprüfung
#include <windows.h>
#include <bcrypt.h>

// Verwundbar: BCrypt-Funktionen nur ab Vista verfügbar
int vulnerable_generate_random(unsigned char* buffer, size_t length) {
    NTSTATUS status;

    // BCryptGenRandom existiert nicht auf Windows XP
    status = BCryptGenRandom(NULL, buffer, length,
                             BCRYPT_USE_SYSTEM_PREFERRED_RNG);

    return NT_SUCCESS(status) ? 0 : -1;
    // Stürzt auf Windows XP ab!
}

// Verwundbar: Verwendung von POSIX.1-2008 Funktion ohne Prüfung
#define _GNU_SOURCE
#include <string.h>

// Verwundbar: strnlen nicht auf allen Systemen verfügbar
size_t vulnerable_safe_strlen(const char* str, size_t maxlen) {
    // strnlen ist POSIX.1-2008, nicht auf älteren Systemen verfügbar
    return strnlen(str, maxlen);
    // Kann Link-Fehler oder Laufzeitfehler auf alten Plattformen verursachen
}

// Verwundbar: Verwendung Linux-spezifischer Syscall
#include <sys/random.h>

int vulnerable_get_random(void* buf, size_t buflen) {
    // getrandom() hinzugefügt in Linux 3.17, glibc 2.25
    return getrandom(buf, buflen, 0);
    // Schlägt auf älteren Kernels/glibc fehl
}
// Verwundbar: Verwendung Java-versionsspezifischer API
public class VulnerableJavaApi {

    // Verwundbar: String.isBlank() nur in Java 11+
    public boolean vulnerable_isEmpty(String s) {
        return s.isBlank();  // NoSuchMethodError auf Java 8!
    }

    // Verwundbar: Files.readString() nur in Java 11+
    public String vulnerable_readFile(Path path) throws IOException {
        return Files.readString(path);  // Existiert nicht in Java 8
    }

    // Verwundbar: Verwendung von Preview-Features
    public void vulnerable_patternMatch(Object obj) {
        // Pattern Matching nur in Java 14+ (preview) / 16+ (final)
        if (obj instanceof String s) {
            System.out.println(s.length());
        }
    }
}

// Verwundbar: Verwendung Android-API ohne Versionsprüfung
public class VulnerableAndroidApi {

    // Verwundbar: API-Level nicht geprüft
    public void vulnerable_createNotificationChannel(Context context) {
        // NotificationChannel hinzugefügt in API 26 (Android 8.0)
        NotificationChannel channel = new NotificationChannel(
            "my_channel", "My Channel", NotificationManager.IMPORTANCE_DEFAULT);

        NotificationManager nm = context.getSystemService(NotificationManager.class);
        nm.createNotificationChannel(channel);
        // Stürzt auf Android 7.1 und niedriger ab!
    }
}
# Verwundbar: Verwendung Python-versionsspezifischer Features
import sys

# Verwundbar: f-strings nur in Python 3.6+
def vulnerable_format(name):
    return f"Hello, {name}!"  # SyntaxError auf Python 3.5

# Verwundbar: := Operator nur in Python 3.8+
def vulnerable_walrus():
    if (n := len(some_list)) > 10:  # SyntaxError auf Python 3.7
        print(f"Liste zu lang: {n}")

# Verwundbar: Verwendung von Typing-Features aus neueren Versionen
from typing import TypedDict  # Nur Python 3.8+

class Person(TypedDict):  # Schlägt auf 3.7 fehl
    name: str
    age: int

Lösungscode

// Sicher: Laufzeit-Versionsprüfung für Windows-APIs
#include <windows.h>
#include <wincrypt.h>

// Funktionszeiger für dynamisches Laden
typedef NTSTATUS (WINAPI *PFN_BCryptGenRandom)(
    BCRYPT_ALG_HANDLE, PUCHAR, ULONG, ULONG);

int secure_generate_random(unsigned char* buffer, size_t length) {
    // Zuerst moderne API versuchen
    HMODULE bcrypt = LoadLibraryA("bcrypt.dll");
    if (bcrypt) {
        PFN_BCryptGenRandom pfnBCryptGenRandom =
            (PFN_BCryptGenRandom)GetProcAddress(bcrypt, "BCryptGenRandom");

        if (pfnBCryptGenRandom) {
            NTSTATUS status = pfnBCryptGenRandom(
                NULL, buffer, (ULONG)length,
                BCRYPT_USE_SYSTEM_PREFERRED_RNG);
            FreeLibrary(bcrypt);
            return NT_SUCCESS(status) ? 0 : -1;
        }
        FreeLibrary(bcrypt);
    }

    // Fallback zu CryptoAPI (Windows XP kompatibel)
    HCRYPTPROV hProv;
    if (!CryptAcquireContext(&hProv, NULL, NULL,
                             PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
        return -1;
    }

    int result = CryptGenRandom(hProv, (DWORD)length, buffer) ? 0 : -1;
    CryptReleaseContext(hProv, 0);
    return result;
}

// Sicher: Compile-Zeit- und Laufzeitprüfungen für POSIX-Funktionen
#include <string.h>

size_t secure_safe_strlen(const char* str, size_t maxlen) {
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L
    // strnlen verfügbar
    return strnlen(str, maxlen);
#else
    // Fallback-Implementierung
    size_t len = 0;
    while (len < maxlen && str[len] != '\0') {
        len++;
    }
    return len;
#endif
}

// Sicher: Linux-Versionsprüfung mit Fallback
#include <unistd.h>
#include <sys/syscall.h>
#include <fcntl.h>

int secure_get_random(void* buf, size_t buflen) {
#if defined(__linux__)
    // Zur Laufzeit auf getrandom prüfen
    #ifdef SYS_getrandom
    long ret = syscall(SYS_getrandom, buf, buflen, 0);
    if (ret >= 0) {
        return 0;
    }
    if (errno != ENOSYS) {
        return -1;  // Echter Fehler, nicht fehlender Syscall
    }
    #endif

    // Fallback zu /dev/urandom
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd < 0) return -1;

    ssize_t result = read(fd, buf, buflen);
    close(fd);
    return (result == (ssize_t)buflen) ? 0 : -1;
#else
    // Nicht-Linux: Plattformspezifische Methode verwenden
    return platform_get_random(buf, buflen);
#endif
}
// Sicher: Java-versionsabhängiger Code
public class SecureJavaApi {

    // Sicher: Versionsprüfung mit Fallback
    public boolean secure_isEmpty(String s) {
        if (s == null) return true;

        // Java-Version zur Laufzeit prüfen
        int version = getJavaVersion();
        if (version >= 11) {
            // Reflection verwenden um isBlank() aufzurufen wenn verfügbar
            try {
                return (Boolean) String.class
                    .getMethod("isBlank")
                    .invoke(s);
            } catch (Exception e) {
                // Zum Fallback durchfallen
            }
        }

        // Fallback für Java 8
        return s.trim().isEmpty();
    }

    private int getJavaVersion() {
        String version = System.getProperty("java.version");
        if (version.startsWith("1.")) {
            return Integer.parseInt(version.substring(2, 3));
        }
        int dot = version.indexOf('.');
        if (dot != -1) {
            return Integer.parseInt(version.substring(0, dot));
        }
        return Integer.parseInt(version);
    }

    // Sicher: Kompatible API verwenden
    public String secure_readFile(Path path) throws IOException {
        // Funktioniert auf Java 8+
        byte[] bytes = Files.readAllBytes(path);
        return new String(bytes, StandardCharsets.UTF_8);
    }
}

// Sicher: Android-API mit Versionsprüfung
public class SecureAndroidApi {

    public void secure_createNotificationChannel(Context context) {
        // Sicher: API-Level vor Verwendung neuer APIs prüfen
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                "my_channel", "My Channel",
                NotificationManager.IMPORTANCE_DEFAULT);

            NotificationManager nm = context.getSystemService(
                NotificationManager.class);
            nm.createNotificationChannel(channel);
        }
        // Für ältere Versionen werden Channels nicht benötigt
    }

    // AndroidX für Rückwärtskompatibilität verwenden
    public void secure_showNotification(Context context) {
        NotificationCompat.Builder builder =
            new NotificationCompat.Builder(context, "my_channel")
                .setSmallIcon(R.drawable.notification_icon)
                .setContentTitle("Titel")
                .setContentText("Nachricht")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT);

        // NotificationCompat behandelt Versionsunterschiede
        NotificationManagerCompat nm = NotificationManagerCompat.from(context);
        nm.notify(1, builder.build());
    }
}
# Sicher: Python-Versionsprüfungen
import sys

# Sicher: Versionsprüfung für f-strings
def secure_format(name):
    if sys.version_info >= (3, 6):
        return f"Hello, {name}!"
    else:
        return "Hello, {}!".format(name)

# Sicher: Bedingte Imports mit Fallbacks
try:
    from typing import TypedDict
except ImportError:
    # Python < 3.8 Fallback
    from typing import Dict
    TypedDict = Dict  # Vereinfachter Fallback

# Sicher: Feature-Erkennung
def secure_get_list_length():
    some_list = [1, 2, 3, 4, 5]

    # Walrus-Operator nur in 3.8+
    if sys.version_info >= (3, 8):
        # Kann Walrus hier nicht verwenden, da Syntaxfehler auf älteren Versionen
        # exec() oder separates Modul für versionsspezifischen Code verwenden
        pass

    # Kompatibler Ansatz
    n = len(some_list)
    if n > 10:
        print("Liste zu lang: {}".format(n))

# Sicher: Verwendung von Kompatibilitätsbibliotheken
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from typing import TypedDict

# Laufzeitkompatibilität
try:
    from typing import TypedDict
except ImportError:
    class TypedDict(dict):
        """Fallback TypedDict für Python < 3.8"""
        def __init_subclass__(cls, **kwargs):
            pass

CVE-Beispiele

Keine spezifischen CVEs werden dieser CWE direkt zugeordnet, obwohl viele Schwachstellen aus fehlenden Sicherheitsfunktionen auf älteren Plattformen entstehen.


Referenzen

  1. MITRE Corporation. "CWE-589: Call to Non-ubiquitous API." https://cwe.mitre.org/data/definitions/589.html
  2. Microsoft. "Version Helper Functions."
  3. Android Developers. "Build.VERSION_CODES."