Hinzufügen von Datenstruktur-Sentinel
Beschreibung
Hinzufügen von Datenstruktur-Sentinel ist eine Schwachstelle, bei der das versehentliche Hinzufügen eines Datenstruktur-Sentinels ernsthafte Programmierlogik-Probleme verursachen kann. Datenstruktur-Sentinels wie Null-Zeichen in Strings oder spezielle Marker in verketteten Listen kontrollieren die Integrität der Datenstruktur und markieren Grenzen. Wenn Benutzereingaben Sentinel-Werte in Datenstrukturen einführen können, können Angreifer Daten abschneiden, Validierung umgehen oder anderes unerwartetes Verhalten verursachen, indem sie diese Steuerzeichen einfügen.
Risiko
Unbeabsichtigtes Sentinel-Hinzufügen ermöglicht Angreifern, das Verhalten von Datenstrukturen zu manipulieren. Das Einfügen von Null-Bytes in Strings verursacht vorzeitige Terminierung, schneidet Daten ab oder umgeht Sicherheitsprüfungen, die Inhalt nach dem Null untersuchen. In Dateipfaden können Null-Bytes Pfade abschneiden, um auf unbeabsichtigte Dateien zuzugreifen. In Datenbanken kann Sentinel-Injection Datenstrukturen korrumpieren. Die hohe Wahrscheinlichkeit der Ausnutzung kombiniert mit der Fähigkeit, Sicherheitskontrollen zu umgehen, macht dies zu einer signifikanten Schwachstelle. Angreifer können Sentinel-Injection verwenden, um Eingabefilter zu umgehen oder Anwendungslogik zu manipulieren.
Lösung
Kapseln Sie Benutzer von Sentinel-Interaktion durch ordnungsgemäße Abstraktionsschichten. Validieren Sie alle Eingaben, um Sentinel-Werte auszuschließen, bevor Daten in Strukturen einbezogen werden. Implementieren Sie ordnungsgemäße Fehlerprüfung, um unbeabsichtigte Sentinel-Einfügung zu verhindern. Verwenden Sie Abstraktionsbibliotheken, die riskante APIs maskieren und Sentinels intern behandeln. Für Strings, bereinigen oder lehnen Sie Null-Bytes in Benutzereingaben ab. Erwägen Sie die Verwendung von langen-präfixierten Datenstrukturen anstelle von sentinel-terminierten. Wenden Sie OS-Level präventive Funktionalität an, wo verfügbar.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Umfang: Integrität Anwendungsdaten modifizieren - Sentinel-Hinzufügung führt generell dazu, dass Datenstrukturen nicht ordnungsgemäß funktionieren, indem Daten am eingefügten Sentinel-Punkt abgeschnitten werden. |
| Zugriffskontrolle | Umfang: Zugriffskontrolle Schutzmechanismus umgehen - Sicherheitsprüfungen, die Daten nach einem eingefügten Sentinel untersuchen, können umgangen werden, da sie den verbleibenden Inhalt nicht sehen. |
Beispielcode
Anfälliger Code
// Anfällig: Benutzereingabe mit Null-Byte
#include <stdio.h>
#include <string.h>
void vulnerable_string_input() {
char buffer[256];
int c;
int i = 0;
// Anfällig: fgetc kann Null-Bytes lesen
while ((c = fgetc(stdin)) != EOF && c != '\n' && i < 255) {
buffer[i++] = c; // Null-Byte (0x00) kann hier eingefügt werden
}
buffer[i] = '\0';
// Wenn Benutzer "admin\x00ignore_this" eingibt
// buffer enthält: "admin\0ignore_this\0"
// strlen(buffer) gibt 5 zurück
// strcmp(buffer, "admin") gibt 0 zurück - Übereinstimmung!
// Aber beabsichtigte Eingabe war "admin\x00ignore_this"
printf("Eingabe: %s\n", buffer); // Gibt nur "admin" aus
}
// Anfällig: Pfadtraversierung via Null-Byte-Injection
void vulnerable_file_access(const char *user_filename) {
char filepath[512];
snprintf(filepath, sizeof(filepath), "/var/data/%s.txt", user_filename);
// Angreifer liefert: "../../etc/passwd\x00"
// filepath wird zu: "/var/data/../../etc/passwd\x00.txt"
// fopen sieht: "/var/data/../../etc/passwd"
// Die ".txt"-Erweiterung wird durch Null-Byte ignoriert
FILE *f = fopen(filepath, "r");
if (f) {
// Liest /etc/passwd anstelle der beabsichtigten Datei
char content[1024];
fread(content, 1, sizeof(content), f);
fclose(f);
}
}
<?php
// Anfällig: PHP Null-Byte-Injection
function vulnerable_include($page) {
// Beabsichtigt: nur .php Dateien aus pages-Verzeichnis einbinden
$file = "pages/" . $page . ".php";
// Angreifer liefert: "../../../../etc/passwd\x00"
// $file wird zu: "pages/../../../../etc/passwd\x00.php"
// PHP (ältere Versionen) sieht: "pages/../../../../etc/passwd"
if (file_exists($file)) {
include($file); // Bindet /etc/passwd ein
}
}
// Anfällig: Erweiterungs-Validierungs-Umgehung
function vulnerable_upload($filename, $content) {
$allowed_extensions = array('jpg', 'png', 'gif');
// Erweiterung extrahieren
$ext = pathinfo($filename, PATHINFO_EXTENSION);
// Angreifer liefert: "malware.php\x00.jpg"
// pathinfo sieht Erweiterung als "jpg" (nach Null)
// Aber Dateisystem kann als "malware.php" speichern
if (in_array(strtolower($ext), $allowed_extensions)) {
file_put_contents("uploads/" . $filename, $content);
// Bösartige PHP-Datei hochgeladen
}
}
?>
# Anfällig: Sentinel-Hinzufügung bei Datenverarbeitung
class VulnerableDataProcessor:
RECORD_SEPARATOR = '\x1E' # ASCII Record Separator
def add_record(self, data):
# Anfällig: Benutzer kann Record-Separator einfügen
# Dies teilt ihre Daten in mehrere Datensätze
self.buffer += data + self.RECORD_SEPARATOR
# Angreifer liefert: "value1\x1Emalicious_record"
# Erzeugt zwei Datensätze anstelle von einem
def validate_input(self, user_input):
# Anfällig: Validierung geschieht nach Null-Bytes
# Angreifer: "safe_input\x00<script>alert(1)</script>"
if "script" not in user_input.lower():
return True # Besteht weil Vergleich C-Strings verwendet
# In einigen Kontexten wird Teil nach Null noch verarbeitet
return False
def create_command(self, user_arg):
# Anfällig: Null-Byte schneidet Befehl ab
command = f"process --safe-mode --input={user_arg}"
# Angreifer: "file.txt\x00--unsafe-mode"
# Kann bei Null abschneiden, oder durch Shell durchlassen
# abhängig davon wie Befehl ausgeführt wird
return command
// Anfällig: Java mit nativen Aufrufen kann betroffen sein
public class VulnerableNativeHandler {
// Native Methode die C-Strings verwendet
private native void processFile(String filename);
public void handleUserFile(String userFilename) {
// Anfällig: Java String kann Null-Bytes enthalten
// Nativer Code der strlen/strcmp verwendet wird bei Null abschneiden
// Benutzer liefert: "safe.txt\0../../etc/passwd"
// Java sieht vollständigen String
// Nativer C-Code sieht: "safe.txt"
if (userFilename.endsWith(".txt")) {
processFile(userFilename);
// Nativer Code kann falsche Datei verarbeiten
}
}
// Anfällig: Datenbank-Sentinel-Injection
public void storeData(String key, String value) {
// Wenn Datenbank spezielle Byte-Sequenzen als Trennzeichen verwendet
// kann Angreifer sie einfügen um Speicher zu korrumpieren
String record = key + "\t" + value; // Tab als Separator
// Angreifer-value: "data\tanother_key\tattacker_value"
// Erzeugt mehrere Datensätze anstelle von einem
database.write(record);
}
}
Korrigierter Code
// Korrigiert: Null-Bytes in Benutzereingabe ablehnen
#include <stdio.h>
#include <string.h>
int secure_string_input(char *buffer, size_t buffer_size) {
int c;
size_t i = 0;
while ((c = fgetc(stdin)) != EOF && c != '\n' && i < buffer_size - 1) {
// Korrigiert: Null-Bytes ablehnen
if (c == '\0') {
fprintf(stderr, "Fehler: Null-Byte in Eingabe nicht erlaubt\n");
buffer[0] = '\0';
return -1;
}
buffer[i++] = c;
}
buffer[i] = '\0';
return 0;
}
// Korrigiert: Pfadkomponenten ohne Null-Bytes validieren
int secure_file_access(const char *user_filename) {
// Korrigiert: Auf Null-Bytes in Eingabe prüfen
if (memchr(user_filename, '\0', strlen(user_filename) + 1) !=
user_filename + strlen(user_filename)) {
// Diese Prüfung erkennt ob eingebettete Nulls vorhanden sind
// Tatsächlich stoppt strlen beim ersten Null, also anderen Ansatz verwenden
}
// Korrigiert: Besserer Ansatz - Zeichen für Zeichen validieren
for (size_t i = 0; user_filename[i] != '\0'; i++) {
// Korrigiert: Null-Bytes und Pfadtraversierung ablehnen
if (user_filename[i] == '\0' ||
user_filename[i] == '/' ||
user_filename[i] == '\\') {
return -1;
}
}
// Korrigiert: Sichere Pfadkonstruktion verwenden
char filepath[512];
int written = snprintf(filepath, sizeof(filepath),
"/var/data/%s.txt", user_filename);
if (written < 0 || written >= sizeof(filepath)) {
return -1; // Pfad zu lang oder Fehler
}
// Korrigiert: Verifizieren dass keine Pfadtraversierung aufgetreten ist
char resolved[PATH_MAX];
if (realpath(filepath, resolved) == NULL) {
return -1;
}
if (strncmp(resolved, "/var/data/", 10) != 0) {
return -1; // Beabsichtigtes Verzeichnis verlassen
}
FILE *f = fopen(resolved, "r");
// ...
return 0;
}
<?php
// Korrigiert: Null-Byte-Bereinigung
function secure_include($page) {
// Korrigiert: Null-Bytes entfernen
$page = str_replace(chr(0), '', $page);
// Korrigiert: Erlaubte Seiten als Whitelist
$allowed_pages = array('home', 'about', 'contact', 'products');
if (!in_array($page, $allowed_pages)) {
die('Ungültige Seite');
}
$file = "pages/" . $page . ".php";
include($file);
}
// Korrigiert: Sicherer Datei-Upload
function secure_upload($filename, $content) {
// Korrigiert: Null-Bytes aus Dateinamen entfernen
$filename = str_replace(chr(0), '', $filename);
// Korrigiert: Sicheren Dateinamen generieren
$safe_name = preg_replace('/[^a-zA-Z0-9_.-]/', '', $filename);
// Korrigiert: Erweiterung anhand des tatsächlichen Dateiinhalts verifizieren
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->buffer($content);
$allowed_mimes = array(
'image/jpeg' => 'jpg',
'image/png' => 'png',
'image/gif' => 'gif'
);
if (!isset($allowed_mimes[$mime])) {
die('Ungültiger Dateityp');
}
// Korrigiert: Erkannte Erweiterung verwenden
$safe_filename = pathinfo($safe_name, PATHINFO_FILENAME) . '.' . $allowed_mimes[$mime];
file_put_contents("uploads/" . $safe_filename, $content);
}
?>
# Korrigiert: Sentinel-Bereinigung bei Datenverarbeitung
class SecureDataProcessor:
RECORD_SEPARATOR = '\x1E'
FORBIDDEN_BYTES = ['\x00', '\x1E', '\x1F'] # Null und Record-Separatoren
def sanitize_input(self, data):
"""Sentinel-Zeichen aus Eingabe entfernen oder escapen."""
sanitized = data
for byte in self.FORBIDDEN_BYTES:
sanitized = sanitized.replace(byte, '')
return sanitized
def add_record(self, data):
# Korrigiert: Vor Hinzufügen bereinigen
clean_data = self.sanitize_input(data)
self.buffer += clean_data + self.RECORD_SEPARATOR
def validate_input(self, user_input):
# Korrigiert: Zuerst auf Null-Bytes prüfen
if '\x00' in user_input:
raise ValueError("Null-Bytes in Eingabe nicht erlaubt")
# Korrigiert: Jetzt sicher Inhalt zu prüfen
if "script" in user_input.lower():
return False
return True
def create_command(self, user_arg):
# Korrigiert: Null-Bytes ablehnen
if '\x00' in user_arg:
raise ValueError("Null-Bytes nicht erlaubt")
# Korrigiert: Parametrisierte Ausführung verwenden
import subprocess
return subprocess.run(
['process', '--safe-mode', f'--input={user_arg}'],
capture_output=True
)
// Korrigiert: Null-Byte-Behandlung in Java
public class SecureNativeHandler {
private native void processFile(String filename);
public void handleUserFile(String userFilename) throws SecurityException {
// Korrigiert: Auf Null-Bytes prüfen
if (userFilename.indexOf('\0') != -1) {
throw new SecurityException("Null-Bytes in Dateinamen nicht erlaubt");
}
// Korrigiert: Dateinamen-Zeichen validieren
if (!userFilename.matches("^[a-zA-Z0-9_.-]+\\.txt$")) {
throw new SecurityException("Ungültiges Dateinamen-Format");
}
// Korrigiert: Pfad auflösen und verifizieren dass er im erlaubten Verzeichnis ist
Path basePath = Paths.get("/var/data").toRealPath();
Path filePath = basePath.resolve(userFilename).normalize();
if (!filePath.startsWith(basePath)) {
throw new SecurityException("Pfadtraversierung erkannt");
}
processFile(filePath.toString());
}
// Korrigiert: Ordnungsgemäße Kodierung für Datenbankspeicherung
public void storeData(String key, String value) throws SecurityException {
// Korrigiert: Validieren dass keine Steuerzeichen
if (containsControlChars(key) || containsControlChars(value)) {
throw new SecurityException("Steuerzeichen nicht erlaubt");
}
// Korrigiert: Ordnungsgemäßes Escaping oder parametrisierte Speicherung verwenden
PreparedStatement stmt = connection.prepareStatement(
"INSERT INTO data (key, value) VALUES (?, ?)"
);
stmt.setString(1, key);
stmt.setString(2, value);
stmt.executeUpdate();
}
private boolean containsControlChars(String s) {
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c < 32 && c != '\t' && c != '\n' && c != '\r') {
return true;
}
}
return false;
}
}
CVE-Beispiele
Keine spezifischen CVEs sind in der MITRE-Datenbank für dieses CWE aufgeführt. Das Muster ist jedoch stark dokumentiert in:
- Null-Byte-Injection-Angriffen (PHP File-Inclusion, Pfadtraversierung)
- CERT C: STR03-C. Do not inadvertently truncate a string
- CERT C: STR06-C. Do not assume that strtok() leaves the parse string unchanged
Referenzen
- MITRE Corporation. "CWE-464: Addition of Data Structure Sentinel." https://cwe.mitre.org/data/definitions/464.html
- CERT C Secure Coding Standard. "STR03-C. Do not inadvertently truncate a string."
- OWASP. "Embedding Null Code." https://owasp.org/www-community/attacks/Embedding_Null_Code