= Sichere Software-Entwicklung im systemnahen Bereich :author: Jan Klemkow, Benjamin Franzke :lang: de == Kurzfassung Dieses Dokument beschreibt Sicherheitsrisiken von Systemprogrammen mit dem Fokus auf UNIX-Systeme. Systemnahe meinst in diesem Dokument alle Programme welche direkt auf die Systemschnittstellen (System-Calls) zugreifen. == Einleitung Sicherheit fuer die Software-Entwicklung im systemnahen Bereich spielt eine wesentliche Rolle. Da es hier oft nicht nur um die Anwendung selbst sonder auch um die Sicherheit des Gesamtsystems geht. Systemdienste werden in vielen Faellen mit hoeheren Rechten ausgefuehrt. Gelingt es einem Angreifer einen solchen Dienst zu uebernehmen, erlangt er selbst hoehere Rechte auf dem Zielsystem. Bei Endanwendungen ist dieses in der Regel nicht der Fall, sodass der Angreifer bei einem erfolgreichen nur Teile des Zielsystems unter seine Kontrolle bringen kann. Historisch betrachtet hat sich die Lage in diesem Bereich in den vergangenen zehn Jahren verbessert. Die grossen Angriffe, welche sich zu Beginn der 2000er Jahre noch auf Systemdienste richteten, sind heute eher selten geworden. Momentan stehen die Endanwendungen wie Browser oder Dokumentenbetrachter im Fokus der Angriffe. // //Im systemnahen Bereich gibt es viele Programme welche fuer die Sicherheit des //Gesamtsystems eine enorme Rolle spielt. // Angefangen beim Kernel des Betriebssystems == Angriffsvektoren In diesem Kapitel werden verschiedene Angrifftvektoren und Schwachstellen von Systemprogrammen erlaeutert. Zudem werden werden verschiedene Verteidigungsmassnahmen erlaeutert, welche das Risiko der Angriffsmoeglichkeiten senken. === Standard-C-Bibliothek Viele Systemprogramme sind in der Programmiersprache C geschrieben. Deren Standardbiblothek unterlag im laufe der Zeit einer ganzen Reihe von Veraenderungen, welche unteranderem die Sicherheit von Programmen erhoehten. Eines der groessten Probleme in C-Programmen stellen die Zeichekettenfunktionen da. Da diese seit langer Zeit immer wieder fuer Sicherheitsprobleme sorgen. Bei dieses Funktionen ist Problem, dass diese die Laengen von Speicherbereichen nicht beachten. Dadurch kommt es schnell zu Pufferueberlaeufen und Zugriffen auf nicht allokierte Speicherbereiche. Dadurch koennen Angreiffer eigenen Programmcode einschleusen und ausfuehlen lassen, sowie Programme zum Absturz bringen. Als Fallbeispiel wird die Funktion strcpy erklaert. Diese Funktion wird dafuer benutzt um eine Zeichenkette von einem Speicherplatz zu einem anderen zu kopieren. Als Parameter bekommt die Funktion die Startadresse der Quelle und die Startadresse des Ziels im Speicher. Der Funtkion ist dabei die Groesse der jeweiligen Speicherplaetze nicht bekannt. Es wird nun eine Speicherzelle nach der Anderen kopiert, solange bis in der Quelle ein NUL-Byte auftaucht. Dabei kann die Funtkion weder sicherstellen, dass sie nicht ueber den Quellpuffer hinaus Bytes kopiert, sowie dass sie nicht ueber die genzen des Zielpuffers hinaus schreibt. Durch dieses Verhalten, kommt es immer wieder zu Pufferueberlaeufen, welche zu unvorhersagbaren Folgen fuer den weiteren Programmablauf fuehren. Um dieses Problem zu loesen wurden neue Zeichenkettenfuntkionen in die Standard-C-Bibliothek auf genommen, welche als Argument maximal Laengen uebergeben bekommen. [source,c] ------------------- strcpy(char *dst, const char *src); strncpy(char *dst, const char *src, size_t len); strlcpy(char *dst, const char *src, size_t dstsize); ------------------- Hierbei gibt es zwei Ansaetze mit Laengen umzugehen. TODO: strncpy() strlcpy() * gets() * strlen() * strcpy() * str..() === Netzwerk-Programmierung Ein grosser Angriffsvektor auf Computersysteme sind die Laufenden Dienste, welche ueber Netzwerk und Internet erreichbar sind. Ein Fehler in ihrer Programmierung koennte von ausserhalb ausgenutzt werden, da Internetdiensten eine staendige weltweite Erreichbarkeit haben. Somit kann ein Internetdienst potentiell zur jeder Zeit von jedem Ort aus angegriffen werden. Zum Schutz solcher Dienste gibt es verschiedene Mechanismen, welche im Folgenden erlaeutert werden. * privilege separation * chroot/jail * Zugangsbeschraenkung (Firewall Layer 3/4) * Applikation Level Gateway (doppelte parsen von protokollen) ==== Privilege Separation Bei der Privilege-Seperation wird ein Programm in verschiedene Prozesse mit unterschiedlichen Berechtigungena auf geteilt. Ziel ist es, so wenig Programmcode wie moeglich mit root-Rechten laufen zu lassen. Als Beispiel soll der Window-Compositer Wayland genannt... TODO: wayland privilege separater erklaeren Im einfachsten Fall startet ein Deamon mit Root rechten und holt sich alle Resourcen, wie etwas sockets mit Well-Known-Ports oder eine Filedescriptor auf eine Datei, gibt dann die rechte ab und laeuft im user-mode weiter. ==== Service-Seperation Da auf einem Computer zumeist mehr als nur ein Dienst laeuft, welcher mit einem Netzwerk oder dem Internet verbunden ist, ist auch die Angriffsmoeglichkeit auf dieses System sehr hoch. Vor Allem beim Servern, welche viele Dienste wie HTTP, FTP, SMTP, POP3 und viele mehr anbieten ist die Gefahr einer Sicherheitsluecke sehr hoch. Sollte einer dieser Dienste ueber eine Sicherheitsluecke von einem Angreifer uebernommen werden, so koennte diese ebenfalls die anderen Dienste und deren Daten uebernehmen. Um dieses zu vermeiden, werden diese Dienste in kuenstlichen Umgebungen getrennt von einander getrennt. Eine Variante ist dafuer der System-Call +chroot(2)+. Dieser System-Call wechselt fuer einen Prozess und dessen Kind-Prozesse das Root-Verzeichnis. Somit kann ein Prozess auf Dateien ausserhalb des neuen Root-Verzeichnisses mehr zugreifen. Bereits geoeffnete Dateien koennen aber weiterhin verwendet werden, auch wenn diese Ausserhalb des neuen Root-Verzeichnisses liegen. Ein Beispiel fuer die Anwendung dieser Technik ist der Apache-Http-Server, welcher meist in das Verzeichnis +/var/www+ als Root-Verzeichnis wechselt. ==== Zugangsbeschraenkung Sollte ein Dienst nur fuer === Kernel Beim erstellen einer sicherheitskritischen Anwendung ist nicht nur die Architektur und Programmierung der Anwendung selbst wichtig. Die Umgebung in der die Anwendung laeuft ist ebenfalls ein wichtiger Faktor, fuer dich Sicherheit eines Systems. So kann die Wahl eines anderen Betriebssystems schon dafuer ausschlaggebend dafuer sein, ob eine Programmierfehler zu einem Sicherheitsrisiko wird oder nicht. Im Folgenden werden einige Aspekte des Kernel-Designs auf ihre Sicherheit fuer das Gesamtsystem betrachtet. * interrupt blockierung * Treiber Userland(minix) / OpenSource (nvidia) === Microkernel Die meisten Betriebssysteme verwenden ein monolitisches Kernel-Design. In diesem werden viele Aufgaben des Betriebssystems im Kernel erledigt. Das bedeutet, dass der Programmcode mit sehr hohen Berechtigungen ausgefuehrt wird. Ein Grossteil dieser Aufgaben koennen auch von User-Prozessen uebernommen werden. Dieses hat den Vorteil, dass Schutzfunktionen des Prozessors genutzt werden koennen. Bei Betriebssystemen mit einem Mikro-Kernel-Design ist dieses anders. Dort werden viele Aufgaben an User-Prozesse ausgelagert, z. B. die Geraetetreiber. ==== NVIDIA Treiber Problem Im +CVE-2012-0946+ wird eine Sicherheitsluecke im NVIDIA Graphikkarten-Treiber fuer Linux, Solaris und FreeBSD beschrieben, welcher das erlangen von hoeheren Rechten ermoeglicht. Das ausnutzen dieses Programmierfehlers ist nur moeglich, weil der Treiber im Kernel-Mode ausgefuehrt wird. //Bei dem Betriebssystem Minix etwa, werden Geraetetreiber als User-Prozess //gestartet. //Dadruch unterliegen sie den gleichen Sicherheitsvorkehrungen wie alle anderen //Programme. //So werden z. B. Speicherschutzverletzungen vom Prozessor erkannt. //Ein Fehlerhaft programmierter Treiber kann so nicht mehr das gesammte System //gefaehrden. // vim: set syntax=asciidoc spell spelllang=de,en: