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
| Auswirkung | Details |
|---|---|
| Integrität | Umfang: 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
- MITRE Corporation. "CWE-432: Dangerous Signal Handler not Disabled During Sensitive Operations." https://cwe.mitre.org/data/definitions/432.html
- 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