Gefährlicher Signal-Handler nicht während sensibler Operationen deaktiviert

Beschreibung

Gefährlicher Signal-Handler nicht während sensibler Operationen deaktiviert ist eine Schwachstelle, bei der ein Produkt einen Signal-Handler verwendet, der Zustand mit anderen Signal-Handlern oder dem Hauptprogramm teilt, aber das Produkt diese Signal-Handler nicht ordnungsgemäß maskiert oder daran hindert, aufgerufen zu werden, während der ursprüngliche Handler noch läuft. Wenn ein Signal-Handler ausgeführt wird, kann ein anderer Handler ihn beim Empfang eines anderen Signals unterbrechen. Wenn beide Handler auf gemeinsamen Zustand wie globale Variablen zugreifen, kann ein Angreifer diesen Zustand korrumpieren, indem er ein zweites Signal auslöst, bevor der erste Handler abgeschlossen ist.

Risiko

Unmaskierte Signal-Handler während sensibler Operationen erzeugen Race Conditions, die Datenkorrumpierung und Sicherheitsumgehungen ermöglichen. Angreifer können spezifische Signale auslösen, um Handler in kritischen Momenten zu unterbrechen und gemeinsamen Zustand zu korrumpieren oder undefiniertes Verhalten zu verursachen. In sicherheitskritischem Code können unterbrochene Authentifizierungs- oder Autorisierungs-Handler Systeme in permissiven Zuständen hinterlassen. Speicherkorrumpierung durch Signal-Handler-Races kann zu beliebiger Codeausführung führen. Die asynchrone Natur von Signalen macht diese Schwachstellen schwer durch Tests zu erkennen, aber zuverlässig in Produktion ausnutzbar.

Lösung

Schalten Sie gefährliche Handler ab, wenn sensible Operationen durchgeführt werden, indem Sie Signal-Maskierungsfunktionen verwenden (sigprocmask, pthread_sigmask). Verwenden Sie sigaction() mit sa_mask, um Signale während der Handler-Ausführung automatisch zu blockieren. Vermeiden Sie den Zugriff auf gemeinsamen Zustand in Signal-Handlern—verwenden Sie sig_atomic_t für einfache Flags, falls notwendig. Erwägen Sie die Verwendung von signalfd() oder Self-Pipe-Tricks, um Signale synchron zu verarbeiten. Entwerfen Sie Signal-Handler so minimal wie möglich und verschieben Sie Arbeit in das Hauptprogramm. Verwenden Sie volatile sig_atomic_t für Flags, die von Handlern gesetzt und vom Hauptprogramm gelesen werden.

Häufige Auswirkungen

AuswirkungDetails
IntegritätUmfang: Integrität

Anwendungsdaten modifizieren - Korrumpierung des gemeinsamen Zustands zwischen Handlern ermöglicht Datenmodifikation. Race Conditions in Signal-Handlern können kritische Datenstrukturen korrumpieren.

Beispielcode

Anfälliger Code

// Anfällig: Signal-Handler teilen Zustand ohne Maskierung
#include <signal.h>
#include <stdio.h>

volatile int counter = 0;
volatile int in_handler = 0;

// Anfällig: Handler können sich gegenseitig unterbrechen
void handler_sigusr1(int sig) {
    in_handler = 1;

    // Anfällig: Ein anderes Signal kann hier unterbrechen
    counter++;
    printf("SIGUSR1: counter = %d\n", counter);

    // Anfällig: Gemeinsamer Zustand kann korrumpiert werden
    sleep(1);  // Simuliert Arbeit - SEHR gefährlich in Signal-Handler

    in_handler = 0;
}

void handler_sigusr2(int sig) {
    // Anfällig: Kann ausgeführt werden, während SIGUSR1-Handler läuft
    in_handler = 1;

    counter--;  // Race Condition mit SIGUSR1-Handler
    printf("SIGUSR2: counter = %d\n", counter);

    in_handler = 0;
}

int main() {
    // Anfällig: Einfaches signal() maskiert keine anderen Signale
    signal(SIGUSR1, handler_sigusr1);
    signal(SIGUSR2, handler_sigusr2);

    while(1) {
        pause();
    }
    return 0;
}

// Angreifer kann schnelle SIGUSR1 und SIGUSR2 senden, um counter zu korrumpieren
// Anfällig: Sensible Operation durch Signal unterbrochen
#include <signal.h>
#include <string.h>

char password_buffer[256];
volatile int auth_in_progress = 0;

void cleanup_handler(int sig) {
    // Anfällig: Kann Passwort-Validierung unterbrechen
    // Versucht "aufzuräumen", korrumpiert aber Zustand
    memset(password_buffer, 0, sizeof(password_buffer));
    auth_in_progress = 0;
}

int validate_password(const char *provided, const char *expected) {
    auth_in_progress = 1;

    // Anfällig: Signal kann zwischen diesen Operationen unterbrechen
    strncpy(password_buffer, provided, sizeof(password_buffer) - 1);

    // Wenn SIGTERM hier ankommt, wird Buffer geleert, aber auth_in_progress
    // spiegelt möglicherweise nicht den tatsächlichen Zustand wider

    int result = strcmp(password_buffer, expected) == 0;

    auth_in_progress = 0;
    return result;
}

int main() {
    signal(SIGTERM, cleanup_handler);  // Anfällig: Keine Maskierung

    // ...
}

Korrigierter Code

// Korrigiert: Signale während Handler-Ausführung ordnungsgemäß maskieren
#include <signal.h>
#include <stdio.h>

volatile sig_atomic_t counter = 0;  // Korrigiert: sig_atomic_t verwenden

void handler_sigusr1(int sig) {
    // Korrigiert: Andere Signale via sa_mask maskiert (unten gesetzt)
    counter++;
}

void handler_sigusr2(int sig) {
    counter--;
}

int main() {
    struct sigaction sa1, sa2;

    // Korrigiert: SIGUSR1-Handler mit Signal-Maskierung konfigurieren
    sa1.sa_handler = handler_sigusr1;
    sigemptyset(&sa1.sa_mask);
    sigaddset(&sa1.sa_mask, SIGUSR2);  // Korrigiert: SIGUSR2 während SIGUSR1 blockieren
    sa1.sa_flags = 0;
    sigaction(SIGUSR1, &sa1, NULL);

    // Korrigiert: SIGUSR2-Handler mit Signal-Maskierung konfigurieren
    sa2.sa_handler = handler_sigusr2;
    sigemptyset(&sa2.sa_mask);
    sigaddset(&sa2.sa_mask, SIGUSR1);  // Korrigiert: SIGUSR1 während SIGUSR2 blockieren
    sa2.sa_flags = 0;
    sigaction(SIGUSR2, &sa2, NULL);

    while(1) {
        pause();
        printf("counter = %d\n", counter);
    }
    return 0;
}
// Korrigiert: Signale während sensibler Operationen maskieren
#include <signal.h>
#include <string.h>
#include <pthread.h>

// Korrigiert: Thread-lokalen Speicher für sensible Daten verwenden
static __thread char password_buffer[256];

volatile sig_atomic_t cleanup_requested = 0;

void cleanup_handler(int sig) {
    // Korrigiert: Nur Flag setzen, kein tatsächliches Aufräumen
    cleanup_requested = 1;
}

int validate_password(const char *provided, const char *expected) {
    sigset_t block_set, old_set;
    int result;

    // Korrigiert: Signale während sensibler Operation blockieren
    sigemptyset(&block_set);
    sigaddset(&block_set, SIGTERM);
    sigaddset(&block_set, SIGINT);
    sigaddset(&block_set, SIGHUP);

    // Korrigiert: Signale vor sensibler Operation maskieren
    pthread_sigmask(SIG_BLOCK, &block_set, &old_set);

    // Jetzt sicher, Passwort-Validierung durchzuführen
    strncpy(password_buffer, provided, sizeof(password_buffer) - 1);
    password_buffer[sizeof(password_buffer) - 1] = '\0';

    result = strcmp(password_buffer, expected) == 0;

    // Korrigiert: Passwort sicher löschen
    explicit_bzero(password_buffer, sizeof(password_buffer));

    // Korrigiert: Ursprüngliche Signal-Maske wiederherstellen
    pthread_sigmask(SIG_SETMASK, &old_set, NULL);

    // Korrigiert: Prüfen, ob Aufräumen während Operation angefordert wurde
    if (cleanup_requested) {
        // Verzögertes Aufräumen behandeln
        perform_cleanup();
    }

    return result;
}

int main() {
    struct sigaction sa;

    sa.sa_handler = cleanup_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGTERM, &sa, NULL);

    // ...
}
// Korrigiert: Self-Pipe-Trick für synchrone Signal-Behandlung verwenden
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>

static int signal_pipe[2];

void signal_handler(int sig) {
    // Korrigiert: Minimaler Handler - nur in Pipe schreiben
    int saved_errno = errno;
    write(signal_pipe[1], &sig, sizeof(sig));
    errno = saved_errno;
}

void setup_signal_pipe() {
    pipe(signal_pipe);

    // Schreibende nicht-blockierend machen
    fcntl(signal_pipe[1], F_SETFL, O_NONBLOCK);

    struct sigaction sa;
    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;

    sigaction(SIGUSR1, &sa, NULL);
    sigaction(SIGUSR2, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);
}

void process_signals() {
    // Korrigiert: Signale synchron in Hauptschleife behandeln
    struct pollfd pfd = {signal_pipe[0], POLLIN, 0};

    while (poll(&pfd, 1, 0) > 0) {
        int sig;
        if (read(signal_pipe[0], &sig, sizeof(sig)) == sizeof(sig)) {
            // Korrigiert: Signal synchron verarbeiten
            switch (sig) {
                case SIGUSR1:
                    handle_usr1_safely();
                    break;
                case SIGUSR2:
                    handle_usr2_safely();
                    break;
                case SIGTERM:
                    handle_term_safely();
                    break;
            }
        }
    }
}

CVE-Beispiele

Für diese CWE sind keine spezifischen CVEs in der MITRE-Datenbank aufgeführt. Das Schwachstellenmuster ist dokumentiert in:

  • CERT C Secure Coding Standard: SIG00-C — Mask signals handled by noninterruptible signal handlers

Referenzen

  1. MITRE Corporation. "CWE-432: Dangerous Signal Handler not Disabled During Sensitive Operations." https://cwe.mitre.org/data/definitions/432.html
  2. CERT C Coding Standard. "SIG00-C. Mask signals handled by noninterruptible signal handlers." https://wiki.sei.cmu.edu/confluence/display/c/SIG00-C.+Mask+signals+handled+by+noninterruptible+signal+handlers