Verwendung eines extern kontrollierten Format-Strings

Beschreibung

Die Verwendung eines extern kontrollierten Format-Strings (Format-String-Schwachstelle) tritt auf, wenn benutzerbereitgestellte Eingaben direkt als Format-String-Argument in Funktionen wie printf(), sprintf(), fprintf() und ähnlichen Formatierungsfunktionen verwendet werden. Format-Specifier wie %s, %x und %n in printf-Familie-Funktionen können ausgenutzt werden, um aus beliebigen Speicherstellen zu lesen (%s, %x), Programme zum Absturz zu bringen oder in beliebige Speicherstellen zu schreiben (%n). Der %n-Specifier ist besonders gefährlich, da er die Anzahl der bisher gedruckten Bytes an eine auf dem Stack bereitgestellte Adresse schreibt und somit beliebige Schreib-Primitive ermöglicht.

Risiko

Format-String-Schwachstellen sind extrem gefährlich und bieten Angreifern mächtige Exploitation-Primitive, vergleichbar mit Buffer Overflows. Die wiederholte Verwendung von %x ermöglicht das Lesen von Stack-Inhalten und legt potenziell Rücksprungadressen (Umgehung von ASLR), Canary-Werte und sensible Daten offen. Der %n-Specifier ermöglicht beliebige Schreibzugriffe auf den Speicher, was Angreifern erlaubt, GOT-Einträge, Rücksprungadressen oder Funktionspointer für Code-Ausführung zu überschreiben. Ganze Bücher wurden über Format-String-Exploitation-Techniken geschrieben. Die Schwachstelle wurde in die Top 10 der am häufigsten gemeldeten Schwachstellentypen eingestuft und betrifft Hunderte von Anwendungen in CVE-Datenbanken.

Lösung

Übergeben Sie niemals benutzerkontrollierte Eingaben direkt als Format-String. Verwenden Sie immer einen statischen Format-String mit der Benutzereingabe als Argument: printf("%s", user_input) anstelle von printf(user_input). Aktivieren Sie Compiler-Warnungen für Format-String-Probleme (-Wformat-security, -Wformat, -Wformat-nonliteral in GCC/Clang). Verwenden Sie Format-String-Prüfungsattribute für benutzerdefinierte Formatierungsfunktionen. Erwägen Sie die Verwendung sicherer Alternativen, die %n nicht unterstützen. Wenden Sie statische Analysetools an, die Format-String-Schwachstellen erkennen. In C++ bevorzugen Sie typsichere Formatierung wie std::format oder die fmt-Bibliothek.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Informationsoffenlegung

Format-Specifier %s und %x ermöglichen das Lesen beliebiger Speicherstellen auf dem Stack und potenziell darüber hinaus, was sensible Daten offenlegt.
ZugriffskontrolleBereich: Code-Ausführung

Der %n-Specifier bietet ein beliebiges Schreib-Primitiv, das das Überschreiben von Rücksprungadressen, GOT-Einträgen oder Funktionspointern ermöglicht.
VerfügbarkeitBereich: Denial-of-Service

Ungültige Format-Specifier oder das Dereferenzieren ungültiger Adressen verursacht Abstürze.

Beispielcode + Lösungscode

Anfälliger Code

#include <stdio.h>

// ANFÄLLIG: Benutzereingabe als Format-String
void log_message(char *user_input) {
    printf(user_input);  // Benutzer kann %x, %s, %n einschleusen
}

// ANFÄLLIG: Format-String in syslog
void log_error(char *error_msg) {
    syslog(LOG_ERR, error_msg);  // Gleiche Schwachstelle
}

// ANFÄLLIG: Indirekter Format-String
void display_username(char *username) {
    char buffer[256];

    // Benutzergesteuerter Format-String
    sprintf(buffer, username);  // Anfällig!

    display(buffer);
}

/*
 * Angriffsbeispiele:
 * Eingabe: "%x %x %x %x"     -> Leakt Stack-Werte
 * Eingabe: "%s"              -> Liest String an Stack-Adresse (kann abstürzen)
 * Eingabe: "%n"              -> Schreibt an Adresse auf Stack
 * Eingabe: "AAAA%08x.%08x.%08x.%08x.%n" -> Write-what-where-Angriff
 */

Korrigierter Code

#include <stdio.h>
#include <syslog.h>

// SICHER: Benutzereingabe als Argument, nicht Format-String
void log_message_safe(const char *user_input) {
    printf("%s", user_input);  // Benutzereingabe kann keine Specifier einschleusen
}

// SICHER: Format-String für syslog
void log_error_safe(const char *error_msg) {
    syslog(LOG_ERR, "%s", error_msg);  // Fester Format-String
}

// SICHER: Ordnungsgemäße sprintf-Verwendung
void display_username_safe(const char *username) {
    char buffer[256];

    // Format-Specifier für Benutzerdaten verwenden
    snprintf(buffer, sizeof(buffer), "Benutzer: %s", username);

    display(buffer);
}

// SICHER: C++ typsichere Formatierung (C++20)
#ifdef __cplusplus
#include <format>
#include <string>

void log_cpp(const std::string& user_input) {
    // Typsichere Formatierung - keine Format-String-Injection möglich
    auto msg = std::format("Nachricht: {}", user_input);
    std::cout << msg << std::endl;
}
#endif

// Kompilieren mit Format-Warnungen: gcc -Wformat -Wformat-security -Werror

Ausgenutzt in der Praxis

ProFTPD Format-String (ProFTPD, 1999-2000)

Die Entdeckung von Format-String-Schwachstellen als Angriffsvektor kam aus einem Sicherheits-Audit von ProFTPD im September 1999. Die ersten großen öffentlichen Exploits, die Remote-Root-Zugriff ermöglichten, wurden im Juni 2000 auf Bugtraq veröffentlicht und markierten den Beginn der weitverbreiteten Format-String-Exploitation.

PHP Format-String-Schwachstellen (PHP, mehrfach)

CVE-2011-1153 und verwandte Schwachstellen behob 27 Format-String-Probleme in PHP auf einmal und demonstrierte, wie verbreitet diese Schwachstellenklasse in der Softwareentwicklung wurde.

Wu-FTPd Format-String (Wu-FTPd, 2000)

Eine der am weitesten verbreiteten frühen Format-String-Schwachstellen, die einen populären FTP-Server-Daemon betraf, der auf vielen Unix-Systemen verwendet wurde, und Remote-Root-Kompromittierung ermöglichte.


Tools zum Testen/Ausnutzen


CVE-Beispiele

  • CVE-2011-1153 — PHP mehrfache Format-String-Schwachstellen.

  • CVE-2012-0809 — sudo Format-String-Schwachstelle ermöglicht Privilegieneskalation.

  • CVE-2022-42043 — PDF-XChange Format-String-Schwachstelle.


Referenzen

  1. MITRE. "CWE-134: Use of Externally-Controlled Format String." https://cwe.mitre.org/data/definitions/134.html

  2. CERT. "FIO30-C. Exclude user input from format strings." https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C

  3. Wikipedia. "Uncontrolled format string." https://en.wikipedia.org/wiki/Uncontrolled_format_string