Es gibt viele Sicherheitsrisiken, die durch den engen Kontakt mit der Hardware entstehen, im Gegensatz zur Verwendung gut getesteter und bewährter APIs aus höheren Programmiersprachen. Es ist viel einfacher, in C einen Pufferüberlauf zu verursachen als in einer Sprache wie Java.
Welche Risiken oder Schwachstellen (z. B. Pufferüberläufe) sollten jedem C-Programmierer bekannt sein (für C-Programmierer relevante IE-Schwachstellen)? Zu welchen Problemen könnten diese führen? Wie vermeide ich sie und was sind häufige Fehler, die dazu führen, dass diese in Programmen auftreten?
Antworten:
Pufferüberläufe sind groß. In C ist standardmäßig nichts auf den Bereich geprüft, daher ist es sehr einfach, einen Puffer zu überschreiben. Es gibt eine Standardbibliotheksfunktion
gets()
, die nicht daran gehindert werden kann, den Puffer zu überlaufen, und die fast nie verwendet werden sollte.Es gibt einige Techniken auf Implementierungsebene, die die Ausnutzung behindern, z. B. das Verwürfeln von Heap-Blöcken. Dadurch werden jedoch Pufferüberläufe in lokalen Puffern nicht gestoppt, was häufig interessante Dinge wie das Ändern der Adresse bewirken kann, an die eine Funktion zurückkehrt.
In C gibt es keine gute allgemeine Lösung. Viele Bibliotheksfunktionen verfügen über Versionen, die die Anzahl der Schreibvorgänge begrenzen. obwohl das zu berechnen ungeschickt sein kann. Es gibt Software, die Heap-Pufferüberläufe im Test erkennen kann, solange der entsprechende Test ausgeführt wird, und Stack-Überlauf wird beim Testen häufig als Absturz angezeigt. Ansonsten geht es um sorgfältige Codierung und Codeüberprüfung.
Ein verwandtes Problem ist das Problem, in einen Puffer zu schreiben, der um ein Zeichen zu klein ist, und zu vergessen, dass eine C-Zeichenfolge mit einer Länge von n Zeichen aufgrund des
'\0'
Terminators n + 1 Zeichen im Speicher benötigt . Wenn es dem Angreifer gelingt, eine Zeichenfolge ohne Terminator zu speichern, wird jede C-Funktion, die eine Zeichenfolge erwartet, so lange verarbeitet, bis sie ein Null-Byte erreicht. Dies kann dazu führen, dass mehr Informationen kopiert oder ausgegeben werden als gewünscht (oder der geschützte Speicher für einen DOS-Angriff erreicht wird) ). Die Lösung ist wiederum Sensibilisierung, Pflege und Codeüberprüfung.Es gibt ein weiteres Risiko für die
printf()
Familie. Wenn Sie jemals schreibenchar * str; ... printf(str);
, bereiten Sie sich auf Probleme vor, wennstr
beim Drucken ein '%' enthalten ist. Die%n
Formatanweisung ermöglichtprintf()
das Schreiben in den Speicher. Die Lösung istprintf("%s", str);
oderputs(str);
. (Verwenden Siesnprintf()
stattdessen auch den C99sprintf()
.)Die Verwendung von Ganzzahlen ohne Vorzeichen, insbesondere als Schleifenindizes, kann Probleme verursachen. Wenn Sie einem vorzeichenlosen Wert einen kleinen negativen Wert zuweisen, erhalten Sie einen großen positiven Wert. Das kann Dinge wie die Verarbeitung von nur N Instanzen von etwas oder in eingeschränkten Funktionen wie untergraben
strncpy()
. Untersuchen Sie alle vorzeichenlosen Ganzzahlen. Möglicherweise möchten Sie dies vermeidenunsigned short
, da ein großer Wert in einem dieser Werte in einen großen positiven Wert in einem umgewandelt wirdint
.Vergessen Sie nicht, dass eine Zeichenkonstante in C tatsächlich eine ist
int
. Das Schreiben von so etwaschar c; while((c = getchar()) != EOF) ...
kann leicht fehlschlagen, da esEOF
in a nicht darstellbar istchar
.Es gibt viel mehr charakteristische C-Fehler, die mir einfallen, aber diese können Sicherheitsprobleme verursachen.
quelle
printf("%s", str)
eine nackte Saite zu verwenden, wennputs(str)
derselbe Job ausgeführt wird.puts
ein Zeilenumbruchzeichen hinzu, währendprintf
dies nicht der Fall ist.fputs(str, stdout)
, was nicht.Einige der C-spezifischen Risiken umfassen: Pufferüberläufe , Formatierungszeichenfolgenangriffe und Ganzzahlüberläufe .
quelle
Hier ist ein leicht zu übersehendes Risiko, das Probleme verursachen kann, deren Behebung Stunden dauern wird.
Betrachten Sie den folgenden Code, der problemlos kompiliert werden kann.
Wenn Sie überprüfen, ob es vorhanden
lpstr_current_state
istCONST_EMERGENCY_STATE_HOLY_CRAP
, weisen Sie es tatsächlich zu. Es ist besser, die konstante Variable immer links zu setzen. Wenn Sie die Konstante links setzen, schlägt der Compiler fehl, da Sie einer Variablen keinen Wert zuweisen können.Dann können Sie sich leicht sagen: "Heiliger Mist, das hätte schlimm sein können", während Sie den zu lesenden Code korrigieren ...
quelle
=
und verwendet==
.Es gibt nur ein Sicherheitsrisiko: Die Tatsache, dass es Personen außerhalb gibt, die ihr Bestes tun, um Schwachstellen in Ihrer Software zu erkennen und sie zu ihrem eigenen Vorteil auszunutzen. Alles andere folgt von dort.
Wenn Sie also denken, "niemand, der bei klarem Verstand ist ...", müssen Sie sofort denken, "außer jemand, der sich in die Computer anderer Leute hacken möchte, würde genau das tun".
Die größte Konsequenz ist, dass Sie immer dann, wenn Sie auf Ereignisse außerhalb reagieren (z. B. indem Sie von außen gelieferte Daten verarbeiten), davon ausgehen müssen, dass diese Daten unter der Kontrolle Ihres schlimmsten Feindes standen.
quelle