WEBVTT
00:00:00.000 --> 00:00:16.070
35C3 Vorspannmusik
00:00:16.070 --> 00:00:25.390
Herald: Gut dann wollen jetzt beginnen.
Wer von euch hat schon mal eine E-Mail
00:00:25.390 --> 00:00:33.960
gekriegt mit einem Anhang? Wer von euch
hat eine E-Mail bekommen mit einem Anhang
00:00:33.960 --> 00:00:40.410
von einer euch unbekannten Person? Das
sind erstaunlich wenige, so 20 Prozent.
00:00:40.410 --> 00:00:47.671
Wer von euch hat den schon mal aufgemacht?
3, 4. Wer von euch kennt jemanden, der schon
00:00:47.671 --> 00:00:55.620
mal einen Anhang aufgemacht hat? Ja
deutlich mehr. Wer von euch musste die
00:00:55.620 --> 00:01:03.420
Probleme beseitigen, die dadurch
entstanden sind? Ja auch so 10 20 Prozent
00:01:03.420 --> 00:01:12.170
würde ich sagen. Ja es ist natürlich eine
ganz gute Idee, sich zu überlegen, ob man
00:01:12.170 --> 00:01:16.220
denn jetzt eine Rechnung oder ein Angebot
auf machen möchte, was man von einer
00:01:16.220 --> 00:01:22.580
völlig unbekannten Person aus dem Internet
bekommen hat. Und was da passiert, wenn
00:01:22.580 --> 00:01:26.659
man es tatsächlich täte, das wird uns
jetzt haggl erläutern. Applaus.
00:01:26.659 --> 00:01:30.990
Applaus
00:01:30.990 --> 00:01:46.450
Haggl: Was? Das sollte so nicht aussehen,
Moment, warum sagt mir das keiner?
00:01:46.450 --> 00:02:15.700
So, hallo, dann können wir jetzt ja anfangen.
Ja also danke für die schöne Einleitung.
00:02:15.700 --> 00:02:20.690
Schön, dass ihr alle da seid, dass ihr so
zahlreich erschienen seid. Wie in jedem
00:02:20.690 --> 00:02:27.160
Jahr eigentlich, gab es auch in 2018 - ich
schiebe mal eben die Maus da weg - 2018
00:02:27.160 --> 00:02:31.300
wieder ein paar schöne Beispiele, oder
schön ist in Anführungszeichen zu setzen,
00:02:31.300 --> 00:02:33.560
Beispiele von Sicherheitslücken, die
ziemlich gravierend sind und die
00:02:33.560 --> 00:02:40.000
geschlossen werden, glücklicherweise,
gingen direkt am Anfang des Jahres los. Im
00:02:40.000 --> 00:02:43.670
Januar wurden im Firefox und mithin auch
im tor-Browser, der ja drauf basiert, ein
00:02:43.670 --> 00:02:49.910
paar Sicherheitslücken geschlossen,
für die man nur eine Webseite besuchen
00:02:49.910 --> 00:02:53.910
musste und dann sind da intern irgendwelche
Buffer Overflows passiert und im
00:02:53.910 --> 00:02:58.450
schlimmsten Fall konnten Angreifer damit
halt beliebigen Code ausführen. Ein
00:02:58.450 --> 00:03:01.860
weiteres Ding aus diesem Jahr, da habe ich
leider keine schöne Pressemitteilung zu
00:03:01.860 --> 00:03:07.150
gefunden, nur so einen CVE-Artikel. Der
Adobe Reader, mal wieder, übrigens guckt
00:03:07.150 --> 00:03:12.989
mal wie viele Sicherheitslücken der hat,
das ist enorm. Da war auch was drin mit
00:03:12.989 --> 00:03:17.560
einem Buffer Overflow und ebenfalls
arbitrary code execution. Heißt, ein
00:03:17.560 --> 00:03:20.930
Angreifer kann beliebigen Code auf dem
Rechner ausführen. Drittes Beispiel, gar
00:03:20.930 --> 00:03:26.099
nicht so lange her, ist eigentlich im
letzten Jahr gewesen, und zwar die beiden
00:03:26.099 --> 00:03:31.190
Würmer Petya und WannaCry. Das waren so
sympathische Ransomwares, die die
00:03:31.190 --> 00:03:35.959
Festplatte verschlüsseln und den Besitzer
dann auffordern, Bitcoins zu überweisen, an
00:03:35.959 --> 00:03:40.200
eine bestimmte Adresse, damit der Rechner
wieder frei geschaltet werden kann. Auch
00:03:40.200 --> 00:03:45.780
die waren beide, also Teil der Angriffe,
die die genutzt haben, funktionierten über
00:03:45.780 --> 00:03:50.060
Buffer Overflows. Man sieht also, es ist
ein Thema mit dem man sehr viel machen
00:03:50.060 --> 00:03:55.110
kann und deswegen reden wir da jetzt ein
bisschen darüber. Das ist der grobe
00:03:55.110 --> 00:03:57.760
Fahrplan, es gibt jetzt so eine kleine
Vorstellungsrunde, ich sage etwas zu mir,
00:03:57.760 --> 00:04:02.349
frage was über euch, sag was zum Vortrag.
Danach machen wir ein paar Grundlagen, die
00:04:02.349 --> 00:04:05.830
zum Teil sehr technisch und zäh sind, ich
bitte das zu entschuldigen, aber ganz ohne
00:04:05.830 --> 00:04:12.090
geht es leider nicht. Und dann erkläre
ich, wie halt der Exploit funktioniert, so
00:04:12.090 --> 00:04:16.289
ein Stack Buffer Overflow Exploit und zum
Schluss gibt es, wenn die Demo-Götter uns
00:04:16.289 --> 00:04:20.320
weiter gewogen sind, eine Demonstration,
wo ich meinen eigenen Rechner halt mit
00:04:20.320 --> 00:04:28.630
einem verwundbaren Programm übernehme.
Also über mich: Ich bin im Chaos und
00:04:28.630 --> 00:04:34.050
Hackspace Siegen aktiv und treibe da so
ein bisschen mein Unwesen, habe so halb
00:04:34.050 --> 00:04:38.120
den Hut für "Chaos macht Schule" und die
Cryptoparties auf. Wenn es sich nicht
00:04:38.120 --> 00:04:42.639
vermeiden lässt, mache ich noch
Infrastruktur und ähnliche Geschichten. In
00:04:42.639 --> 00:04:46.530
meinem sonstigen Leben bin ich
Informatiker, ich programmiere kleine
00:04:46.530 --> 00:04:52.430
Mikrocontroller für Soundsysteme, ich
mache Judo und bin Musiker. Meine
00:04:52.430 --> 00:04:57.190
Lieblingsspeise ist Dal. An dieser stelle
herzlichen Dank an das Küchenteam, die uns
00:04:57.190 --> 00:05:01.240
während dem Aufbau sehr gut verpflegt
haben, unter anderem mit Dal. So, bisschen
00:05:01.240 --> 00:05:07.130
was über euch. Ein kurzes Funktionscheck:
Wer hört mir zu und ist in der Lage und
00:05:07.130 --> 00:05:13.490
gewillt, seinen Arm zu heben bei Fragen,
die zutreffen? Das sind ungefähr alle,
00:05:13.490 --> 00:05:17.180
cool. Dann: Wer weiß, wie so ein Rechner
aufgebaut ist, Stichwort
00:05:17.180 --> 00:05:21.880
Rechnerarchitektur, Rechenwerk,
Steuerwerk, so was? Das sind auch
00:05:21.880 --> 00:05:27.130
erstaunlich viele, okay. Wer hat schon mal
programmiert? Im Idealfall mit C, das sind
00:05:27.130 --> 00:05:34.030
ungefähr alle. Wer hat schon mal gehört,
was ein Stack ist? Was ein Buffer Overflow
00:05:34.030 --> 00:05:41.610
ist? Wer hat sich bei allem gemeldet? Ihr
werdet euch langweilen! lacht Der
00:05:41.610 --> 00:05:46.600
Vortrag ist nämlich, ich habe versucht,
ihn möglichst einfach zu halten, weil
00:05:46.600 --> 00:05:49.470
inspiziert inspiriert, entschuldigung,
inspiriert ist er von einem
00:05:49.470 --> 00:05:52.560
Arbeitskollegen, den ich eigentlich für
sehr kompetent halte, und der sich auch
00:05:52.560 --> 00:05:56.759
auskennt mit Mikrocontrollern, mit
Assemblycode und der mich irgendwann mal
00:05:56.759 --> 00:06:01.411
fragte, oder mehr im Nebensatz beiläufig
erwähnte, dass er nicht verstünde, wie
00:06:01.411 --> 00:06:07.610
denn eigentlich sein kann, dass ein PDF
einen Rechner übernehmen kann. Challenge
00:06:07.610 --> 00:06:11.190
accepted habe ich mir gedacht, das muss
man doch irgendwie erklären können. Und
00:06:11.190 --> 00:06:15.419
dann habe ich den Vortrag gemacht, habe
hier in den Talk eingereicht in Pretalx,
00:06:15.419 --> 00:06:18.539
der wurde dann halt paar Wochen später
auch angenommen, dann habe ich mir den
00:06:18.539 --> 00:06:21.330
Vortrag, den ich zwei Wochen vorher
zusammengeschraubt hatte, noch einmal
00:06:21.330 --> 00:06:24.683
angeschaut und geprüft, ob er den
Anforderungen entspricht, die ich mir
00:06:24.683 --> 00:06:28.550
selber gesetzt habe, festgestellt oh mein
Gott, er ist viel zu technisch. Deswegen
00:06:28.550 --> 00:06:31.980
habe ich den weiter abgespeckt und
versucht, alle nicht nötigen Details, alle
00:06:31.980 --> 00:06:34.919
nicht nötigen Fachwörter, alles raus zu
streichen, was man nicht unbedingt
00:06:34.919 --> 00:06:40.350
braucht, um das Prinzip eines Stack Buffer
Overflows oder ein Exploit dessen zu
00:06:40.350 --> 00:06:46.139
verstehen. Der Vortrag ist vor 20 Minuten
fertig geworden, ich habe ihn noch nie
00:06:46.139 --> 00:06:50.759
Probe gehalten, ich habe gestern bei der
Demo zweimal meinen Rechner abgeschossen,
00:06:50.759 --> 00:06:55.710
insofern bin ich selber sehr gespannt, was
jetzt passiert. Also fangen wir an.
00:06:55.710 --> 00:06:59.790
Grundlagen: Wie funktioniert ein Rechner?
Wenn man es ganz ganz ganz ganz grob
00:06:59.790 --> 00:07:05.169
vereinfacht ausdrückt, gibt es irgendwo
einen Prozessor, da kommen auf der einen
00:07:05.169 --> 00:07:11.680
Seite Daten rein, nämlich Programmdaten,
und auf der anderen Seite kommen Daten
00:07:11.680 --> 00:07:15.860
rein und raus, nämlich die Nutzdaten, die
ich verarbeiten möchte. Das kann, weiß
00:07:15.860 --> 00:07:20.550
ich, ein Office-Dokument sein, ein PDF
oder halt auch Daten aus dem Internet wie
00:07:20.550 --> 00:07:27.889
HTML-Seiten, bei Web-Browsern sind das halt
die Daten die rein und raus gehen. Daten
00:07:27.889 --> 00:07:35.030
werden intern im Rechner in dem Speicher
abgelegt und zwar in Paketen von acht 1en
00:07:35.030 --> 00:07:39.639
und 0en. 1en und 0en sind Bits, das wissen
ja wahrscheinlich alle. So ein Paket von
00:07:39.639 --> 00:07:45.420
acht davon nennt man ein Byte. Jedes Byte
hat eine Adresse im Speicher. Und der Witz
00:07:45.420 --> 00:07:49.389
ist, dass das hier so, dass was da
gezeichnet ist, nicht ganz korrekt ist,
00:07:49.389 --> 00:07:53.660
denn Programme und Daten liegen im
gleichen Speicher. Das heißt, ich kann es
00:07:53.660 --> 00:07:57.900
theoretisch schaffen, den Prozessor dazu
zu bringen, Programme auszuführen aus dem
00:07:57.900 --> 00:08:02.240
Speicherbereich, wo eigentlich Daten
liegen sollten. Das ist schon eigentlich
00:08:02.240 --> 00:08:10.139
wie letztendlich so ein Buffer Overflow
Exploit funktioniert. Wenn ich den
00:08:10.139 --> 00:08:13.580
Prozessor da in der Mitte doch noch mal
ein bisschen aufbohre, dann sehe ich, dass
00:08:13.580 --> 00:08:18.849
er da drin so einen Registersatz hat. Man
nennt das so, Register. Das ist so ein
00:08:18.849 --> 00:08:23.940
bisschen vergleichbar mit dem, man könnte
sagen, das Kurzzeitgedächtnis von
00:08:23.940 --> 00:08:28.580
Menschen, man sagt Menschen ja nach, sie
könnten sich so größenordnungsmäßig 7
00:08:28.580 --> 00:08:33.829
Dinge gleichzeitig merken, kurzzeitig. So
ähnlich ist das bei einem Prozessor auch,
00:08:33.829 --> 00:08:38.829
der hat einen sehr begrenzten Satz von
Registern, also Speicherzellen, die sehr
00:08:38.829 --> 00:08:43.159
schnell sind, aber halt auch sehr teuer
sind. Deswegen baut man da nicht so viele
00:08:43.159 --> 00:08:46.399
von, das sind die Register. Und dann merkt
er sich halt zum Beispiel, wo er im
00:08:46.399 --> 00:08:54.069
Programm gerade steht. Also wo im
Programmspeicher er gerade steht oder wo
00:08:54.069 --> 00:08:59.420
der Stack gerade steht. Der Stack ist ein
besonderer Speicherbereich im
00:08:59.420 --> 00:09:04.869
Datenspeicher, also eigentlich kein
Programmspeicher, der verwendet wird um
00:09:04.869 --> 00:09:10.009
Dinge längerfristig abzulegen, also man
könnte sagen Langzeitgedächtnis. Naja es
00:09:10.009 --> 00:09:14.410
stimmt nicht ganz, aber grob. Und der
Stack ist wirklich, der Name ist Programm,
00:09:14.410 --> 00:09:17.550
ist wie ein Stapel, ich lege unten was
drauf, dann lege ich was oben drauf, lege
00:09:17.550 --> 00:09:20.879
etwas weiteres oben drauf, und ich lege
auch Dinge oder hole Dinge genau in der
00:09:20.879 --> 00:09:26.829
umgekehrten Reihenfolge wieder runter.
Also wie ein Stapel Papier eigentlich. Der
00:09:26.829 --> 00:09:31.850
wird gebraucht, um bestimmte Features von
Programmiersprachen zu implementieren, das
00:09:31.850 --> 00:09:35.749
werden wir gleich noch sehen. Wenn jetzt
also so ein Programm abläuft, also links
00:09:35.749 --> 00:09:39.970
sieht man einen Ausschnitt aus dem
Programmspeicher. Also ich habe da jetzt
00:09:39.970 --> 00:09:43.759
byteweise mal irgendwelche random
Zahlen hingemalt. Naja es sind keine
00:09:43.759 --> 00:09:47.670
random Zahlen, es ist tatsächlich ein Teil
eines Shell-Codes. Also von dem Ding, was
00:09:47.670 --> 00:09:52.259
ich gleich benutze, um meinen Rechner
aufzumachen. Die sind jetzt von oben nach
00:09:52.259 --> 00:09:56.009
unten byteweise aufgelistet und man würde
jetzt sehen, also Speicheradressen gehen
00:09:56.009 --> 00:09:59.750
jetzt hier von oben nach unten. Das heißt,
oben sind die niedrigen Speicheradressen,
00:09:59.750 --> 00:10:03.300
unten sind die hohen Speicheradressen. Und
die werden einfach der Reihe nach
00:10:03.300 --> 00:10:07.329
durchgezählt, jedes Byte hat halt so eine
Adresse. Und so weiß der Prozessor genau,
00:10:07.329 --> 00:10:11.379
wenn er gesagt bekommt, macht mal hier an
der und der Stelle im Programmcode weiter,
00:10:11.379 --> 00:10:15.940
dann springt er halt dahin und liest
weiter. Und zwar sieht das so aus, da
00:10:15.940 --> 00:10:22.259
werden halt Dinge, also dass der Opcode
oder der Befehl, der gerade ausgeführt
00:10:22.259 --> 00:10:25.389
werden soll, wird eingelesen und wird
ausgeführt und der könnte dann zum
00:10:25.389 --> 00:10:30.910
Beispiel so etwas machen wie ein Datum aus
dem Registersatz in den Stack legen oder
00:10:30.910 --> 00:10:34.720
auf den Stack oben drauf legen. Im
nächsten Schritt wird dann etwas anderes
00:10:34.720 --> 00:10:38.639
oben draufgelegt und dann wird da wieder
was runtergeholt und wieder zurück in das
00:10:38.639 --> 00:10:41.899
Register geschrieben, möglicherweise noch
irgendwomit verrechnet und so, hangelt er
00:10:41.899 --> 00:10:48.230
sich halt durch den Code durch. Von oben
nach unten. Man sieht auch, es muss nicht
00:10:48.230 --> 00:10:54.369
unbedingt immer alles, jedes Ding muss
nicht auch ein Befehl sein, da sind auch
00:10:54.369 --> 00:10:59.499
Daten dazwischen. In diesem Fall halt das,
was auf den Stack gelegt wurde. Aber im
00:10:59.499 --> 00:11:02.110
Prinzip funktioniert das so. Ein Rechner
geht einfach der Reihe nach die Befehle
00:11:02.110 --> 00:11:04.470
durch. Es gibt dann halt auch
Sprungbefehle, die sagen können: springe
00:11:04.470 --> 00:11:08.790
mal irgendwie zurück oder springe mal vor,
aber im Prinzip ist das alles was ein
00:11:08.790 --> 00:11:16.660
Rechner macht. Okay, der Shell Code, den
ich gleich benutzen werde, sieht so aus.
00:11:16.660 --> 00:11:21.660
Das ist offensichtlich ein Programm, was
den laufenden Prozess beendet und eine
00:11:21.660 --> 00:11:26.929
Shell mit dem Namen "sh" startet. Nun ist
das natürlich nicht so offensichtlich, so
00:11:26.929 --> 00:11:29.509
will ja keiner Code schreiben. Deswegen
haben sich Leute einfallen lassen: Hey,
00:11:29.509 --> 00:11:33.930
wir müssen irgendwie es schaffen, was
lesbareres zu bekommen und außerdem würden
00:11:33.930 --> 00:11:36.499
wir ganz gerne, wenn wir ein Programm
geschrieben haben, das nicht für jeden
00:11:36.499 --> 00:11:40.249
Prozessor dieser Welt neu schreiben
müssen. Weil das da ist Code, der nur auf
00:11:40.249 --> 00:11:45.860
diesem Prozessor, in diesem Fall ein x86
Prozessor ,läuft. Wenn ich versuche, den
00:11:45.860 --> 00:11:49.399
auf einem ARM-Prozesor laufen zu lassen,
dann sagt er: Kenne ich nicht den Befehl,
00:11:49.399 --> 00:11:56.309
mache ich nicht. Beides erreicht man mit
sogenannten höheren Programmiersprachen.
00:11:56.309 --> 00:12:03.350
Eine davon ist C. Das gleiche Programm
sieht in C so aus. Und hier kann man schon
00:12:03.350 --> 00:12:06.450
einigermaßen erkennen, okay hier ist
irgendwie ein String drin, offensichtlich
00:12:06.450 --> 00:12:12.879
also eine Zeichenkette die auf /bin/sh
zeigt. Das ist für Leute, die Unix kennen,
00:12:12.879 --> 00:12:17.739
die Standard Shell. Also ein
Kommandozeileninterpreter. Und darunter ist
00:12:17.739 --> 00:12:22.660
eine Zeile, naja die ist wirklich sehr
kryptisch, die muss man schon kennen. Es
00:12:22.660 --> 00:12:26.489
ruft halt das Programm auf. Aber wie
gesagt, Menschen, die sich damit ein
00:12:26.489 --> 00:12:29.759
bisschen auskennen, die können so etwas
direkt auf Anhieb lesen, das da oben
00:12:29.759 --> 00:12:35.389
natürlich nicht. Dieser Code wird jetzt in
einen sogenannten Compiler reingeschmissen
00:12:35.389 --> 00:12:43.069
und der Compiler macht dann aus dem
Unteren ungefähr das Obere wieder. Zudem
00:12:43.069 --> 00:12:47.129
gibt es dann, wenn man schon so eine
schöne Abstraktion hat, noch
00:12:47.129 --> 00:12:51.230
Programmbibliotheken, dass man nicht jeden
Scheiß neu machen muss. So was wie: öffne
00:12:51.230 --> 00:12:55.120
mir eine Datei und lies den Inhalt. Das
will ich ja nicht jedes mal auf der Ebene
00:12:55.120 --> 00:12:59.989
neu programmieren. Deswegen lege ich mir
dafür schöne Bibliotheken an, mit
00:12:59.989 --> 00:13:04.920
Funktionen. Funktionen sind Teile also
wieder verwertbare Programmteile, die
00:13:04.920 --> 00:13:08.829
einen definierten Satz von
Eingabeparametern haben. Also ich kann
00:13:08.829 --> 00:13:13.579
einer Datei-öffnen-Funktionen
beispielsweise mitgeben, wo die Datei sich
00:13:13.579 --> 00:13:16.929
befindet im Dateisystem und ob ich sie
lesend schreiben (öffnen) möchte oder schreibend
00:13:16.929 --> 00:13:23.480
oder beides. Dann haben die einen
Funktionsrumpf. Das ist der Teil der
00:13:23.480 --> 00:13:27.259
Funktionen, der was macht. Der also das
tut, was die Funktion tun soll mit den
00:13:27.259 --> 00:13:31.259
Eingangsparametern und dann gibt es noch
einen Return-Wert, dass ist also ein
00:13:31.259 --> 00:13:34.129
Rückgabewert, den die Funktion dann zurück
gibt, wenn sie fertig ist mit was immer
00:13:34.129 --> 00:13:38.860
sie getan hat. Und auf diese Weise baut
man sich halt Bibliotheken und verwendet
00:13:38.860 --> 00:13:41.929
die halt immer wieder. Dieses execve ganz
unten ist beispielsweise eine
00:13:41.929 --> 00:13:47.799
Bibliotheksfunktion. Und das war im
Prinzip schon alles, was wir für den
00:13:47.799 --> 00:13:59.829
Exploit wissen müssen. Wenn nämlich jetzt
so eine Funktionen aufgerufen wird. Nehmen
00:13:59.829 --> 00:14:05.639
wir mal an, wir hätten eine Funktion, die
drei Parameter hat, einer davon ist, ach
00:14:05.639 --> 00:14:09.509
Quatsch. Zwei Parameter hat,
entschuldigung, und drei lokale Variablen.
00:14:09.509 --> 00:14:13.379
Achso, Variable habe ich gar nicht
erklärt. Variablen ist auch ein Konzept
00:14:13.379 --> 00:14:17.970
von Programmiersprachen. Da kann ich halt
Speicheradressen sprechende Namen geben.
00:14:17.970 --> 00:14:24.329
Dass ich halt als Programmierer sehen kann,
wo ich was abgelegt habe. Und Funktionen
00:14:24.329 --> 00:14:29.160
können lokale Variablen haben. Davon
beliebig viele und die werden eben über
00:14:29.160 --> 00:14:32.350
einen Stack abgebildet, genauso wie die
Funktionsparameter über einen Stack
00:14:32.350 --> 00:14:36.399
abgebildet werden und der Rückgabewert
weiß ich jetzt gerade nicht, da will ich
00:14:36.399 --> 00:14:40.699
nichts falsches sagen. Könnte sein, dass
der auch über einen Stack läuft, bin ich
00:14:40.699 --> 00:14:45.160
mir aber gerade nicht sicher. Nein, ich
glaube, läuft er nicht, whatever. Wenn ich
00:14:45.160 --> 00:14:48.509
jetzt mir eine Funktion vorstelle, die
zwei Parameter hat und drei lokale
00:14:48.509 --> 00:14:54.860
Variablen. Davon soll eine ein Buffer
sein, dann passiert, wenn die Funktion
00:14:54.860 --> 00:14:59.769
aufgerufen wird, folgendes: Zuerst werden
die Funktionsargumente oder Parameter auf
00:14:59.769 --> 00:15:06.009
den Stack gelegt, und zwar in umgekehrter
Reihenfolge. Fragt nicht, ist so. Danach
00:15:06.009 --> 00:15:10.589
wird die momentane Adresse oder die
nächste Adresse auf den Stack gelegt,
00:15:10.589 --> 00:15:15.220
damit das Programm weiß, wenn es aus der
Funktion zurückkommt, wo es weitermachen
00:15:15.220 --> 00:15:21.419
muss, weil es folgt ja ein Sprung. Dann
kommt noch was, das uns nicht interessiert
00:15:21.419 --> 00:15:26.689
und dann kommen die lokalen Variablen der
Funktion selber. In diesem Fall eine
00:15:26.689 --> 00:15:30.119
Variable, dann kommt ein Buffer und dann
kommt noch eine Variable. An dieser Stelle
00:15:30.119 --> 00:15:32.333
ist sehr schön zu erklären, was ein
Buffer ist. Ein Buffer ist
00:15:32.333 --> 00:15:39.489
eigentlich nichts anderes als eine
Variable, die aber mehr Speicher hat.
00:15:39.489 --> 00:15:44.029
Also das ist jetzt nicht eine Zahl,
sondern es sind beispielsweise 512 Zahlen
00:15:44.029 --> 00:15:49.720
der gleichen Größe. Das ist ein Buffer.
Das hier wäre zum Beispiel jetzt ein
00:15:49.720 --> 00:15:56.350
Buffer der Größe 5. 5 Bytes groß. Die
können beliebig groß sein, wobei beliebig
00:15:56.350 --> 00:16:00.519
ist nicht ganz richtig. Hängt von der
Speichergröße ab, wie ich gestern gelernt
00:16:00.519 --> 00:16:07.569
habe, bei der Demo. lacht Ja, aber im
Prinzip sind die nicht begrenzt und das
00:16:07.569 --> 00:16:09.930
Schlimme ist, man muss sich wenn man so
low level programmiert, selber darum
00:16:09.930 --> 00:16:15.679
kümmern, dass man die Grenzen einhält und
nicht zu viel da rein schreibt. Und dann
00:16:15.679 --> 00:16:23.089
sind wir schon beim Programm. Das Rechte
ist das C Program, was ich exploite. Das
00:16:23.089 --> 00:16:28.160
ist relativ überschaubar, oben diese Zeile
"int main", das kann man ignorieren, dass
00:16:28.160 --> 00:16:32.879
braucht man halt, um C zu sagen: hier
beginnt das Programm, hier geht es los und
00:16:32.879 --> 00:16:37.529
es ist aber wichtig zu wissen, dass main
bereits eine Funktion ist. Also vorher
00:16:37.529 --> 00:16:44.419
passieren noch andere Dinge, die wir nicht
genauer betrachten, die mir aber sehr
00:16:44.419 --> 00:16:49.119
viele Steine in den Weg gelegt haben
gestern. lacht Die rufen am Ende dann
00:16:49.119 --> 00:16:55.209
halt diese Funktion main auf. Es ist also
ein ganz normaler Funktionsaufruf. Die
00:16:55.209 --> 00:16:58.239
Funktion main hat jetzt eine lokale
Variable, nämlich den Buffer der Größe
00:16:58.239 --> 00:17:02.279
256. Das ist nicht mehr ganz aktuell, in
der Demo, die ich gleich zeige, ist er ein
00:17:02.279 --> 00:17:07.620
bisschen größer, spielt aber keine Rolle.
Danach gibt es eine Bibliotheksfunktion
00:17:07.620 --> 00:17:15.420
string copy, strcpy. Die kopiert, was
immer in argv[1] liegt. Da muss man jetzt
00:17:15.420 --> 00:17:20.079
dazu sagen, das ist ein
Kommandozeilenparameter. Wenn ich ein
00:17:20.079 --> 00:17:23.250
Programm aufrufe auf der Kommandozeile,
kann ich dem noch Argumente mitgeben und
00:17:23.250 --> 00:17:27.289
das erste Argument, was ich dem mitgebe in
diesem Fall, würde halt in den Buffer
00:17:27.289 --> 00:17:33.840
kopiert. Danach wird eine nette Nachricht
ausgegeben "Hallo, was immer in dem Buffer
00:17:33.840 --> 00:17:38.520
steht" und dann folgt das return. Sprich,
die Funktionen kehrt zurück und dieser
00:17:38.520 --> 00:17:43.130
ganze Stack wird abgeräumt und der
Prozessor springt dahin, an die Stelle,
00:17:43.130 --> 00:17:48.110
deren Adresse jetzt in return links rot
markiert steht. Also weil vorher hat sich
00:17:48.110 --> 00:17:51.620
ja das Programm gemerkt, wo es nachher hin
zurück muss, indem es die Adresse extra da
00:17:51.620 --> 00:17:56.470
hingelegt hat. Und wenn ich jetzt zu viele
Daten in diesen Buffer rein schreibe, der
00:17:56.470 --> 00:18:01.039
ist links verkürzt dargestellt, also
jetzt die letzten 5 Bytes von diesem 256
00:18:01.039 --> 00:18:05.519
Bytes Buffer, wenn ich jetzt zu viele
Daten da reinschreibe, dann kommt
00:18:05.519 --> 00:18:10.080
irgendwann der Punkt, wo ich halt die
letzten paar Bytes überschreibe. Dann
00:18:10.080 --> 00:18:13.000
überschreibe ich das Ding, was uns nicht
interessiert und irgendwann überschreibe
00:18:13.000 --> 00:18:19.149
ich die Return-Adresse. Was jetzt
passiert, erst mal noch nichts. Dann kommt
00:18:19.149 --> 00:18:26.350
das return 0 und was das return macht: Es
lädt was immer an dieser stelle steht
00:18:26.350 --> 00:18:29.669
zurück in den instruction pointer, also an
die Stelle, wo sich der Prozessor intern
00:18:29.669 --> 00:18:34.880
merkt, wo das Programm gerade steht und
macht an der Stelle weiter. Wenn ich es
00:18:34.880 --> 00:18:41.820
jetzt schaffe, in diesen Buffer meinen
Shell Code reinzuladen, also das kleine
00:18:41.820 --> 00:18:45.810
Programmstück, was ihr eben gesehen habt,
was das laufende Programm beendet und ein
00:18:45.810 --> 00:18:49.230
neues Programm, nämlich einen
Kommandozeileninterpreter startet, wenn
00:18:49.230 --> 00:18:53.060
ich das jetzt schaffe das da oben rein zu
schreiben und zudem noch es schaffe, an
00:18:53.060 --> 00:19:00.429
die Return-Adresse den Anfang von diesem
Code reinzuschreiben, dann passiert genau,
00:19:00.429 --> 00:19:04.280
was nicht passieren darf: Nämlich das
Programm macht, was ich ihm vorher gesagt
00:19:04.280 --> 00:19:10.620
habe und was ich vorher in diesem Buffer
reingeschrieben habe. Und jetzt kommt der
00:19:10.620 --> 00:19:16.130
Punkt, an dem es interessant wird. Jetzt
muss ich erst mal meine Maus wiederfinden,
00:19:16.130 --> 00:19:41.529
dass habe ich mir alles anders
vorgestellt. Sieht man das? Das ist ein
00:19:41.529 --> 00:19:57.440
bisschen klein. Könnt ihr das
lesen, nein oder? Da hinten, könnt ihr
00:19:57.440 --> 00:20:05.570
das da hinten lesen? Nein. Bis gerade eben
konnte ich auch noch die Größe von meinem
00:20:05.570 --> 00:20:15.070
Terminal verstellen. Das scheint jetzt
nicht mehr zu gehen. Aha, geht nur auf dem
00:20:15.070 --> 00:20:26.139
linken Bildschirm, aus Gründen! Könnt ihr
das jetzt einigermaßen lesen? Gut. Also
00:20:26.139 --> 00:20:36.639
ich habe hier dieses Programm, ich habe
das mal vorbereitet. Es macht, was es
00:20:36.639 --> 00:20:40.500
soll, es liest halt den ersten Parameter
ein, also "vuln" ist das Programm, das
00:20:40.500 --> 00:20:44.129
verwundbar ist. Ich habe ihm den Parameter
"haggl" mit gegeben, also sagt das "Hallo,
00:20:44.129 --> 00:20:49.740
haggl", alles cool. Wenn ich jetzt aber
Dinge da rein schreibe, die ein bisschen
00:20:49.740 --> 00:21:09.529
länger sind, beispielsweise so etwas. Ich
hoffe, das ist die richtige Syntax um in
00:21:09.529 --> 00:21:14.669
Python Dinge auf der Kommandozeile direkt
auszuführen. Ja. Dann kriege ich einen
00:21:14.669 --> 00:21:18.179
segmentation fault. Ein segmentation fault
ist der nette Hinweis vom
00:21:18.179 --> 00:21:22.360
Betriebssystemen: Kollege, du hast gerade
Speicher lesen und/oder schreiben wollen,
00:21:22.360 --> 00:21:29.750
auf den du keinen Zugriff hast. Wenn Leute
die Exploits schreiben, so etwas kriegen,
00:21:29.750 --> 00:21:33.360
also das ist das was normalerweise das
kennt wahrscheinlich jeder, was passiert
00:21:33.360 --> 00:21:36.190
wenn ein Programm einfach so ohne
irgendetwas zu sagen abstürzt, dann ist
00:21:36.190 --> 00:21:40.809
das oft ein segmentation fault. Das ist
für die meisten Menschen sehr nervig, weil
00:21:40.809 --> 00:21:43.460
man dann das Programm neu starten muss,
gegebenenfalls Daten verloren gegangen
00:21:43.460 --> 00:21:47.600
sind. für Leute, die Exploits schreiben,
ist das der heilige Gral, weil
00:21:47.600 --> 00:21:52.770
segmentation faults sind oft ein Indiz
dafür, dass es da gerade einen Buffer
00:21:52.770 --> 00:21:56.200
Overflow gegeben hat. Und ein Buffer
Overflow, damit kann man eine Menge
00:21:56.200 --> 00:22:10.760
anfangen. Man kann beispielsweise einen
Shell Code reinladen. xxd -p shellcode,
00:22:10.760 --> 00:22:15.539
das ist das Ding, was wir eben in den
Slides gesehen haben. Das ist also dieses
00:22:15.539 --> 00:22:21.840
Programm in Maschinencode, was das
laufende Programm beendet und eine Shell
00:22:21.840 --> 00:22:29.780
startet. Und wenn ich die jetzt da rein
injecte, also anstatt jetzt diesem
00:22:29.780 --> 00:22:39.150
Pythonding oder dem einfachen String
haggl, dieses Teil jetzt da rein pipe,
00:22:39.150 --> 00:22:44.380
dann bekomme ich eine Shell. Und zwar eine
Shell, die nicht die andere Shell ist,
00:22:44.380 --> 00:22:50.120
sondern eine neue Shell ist. An der Stelle
habe ich es geschafft. Jetzt bin ich halt
00:22:50.120 --> 00:22:55.730
drin und kann hier auf dem System
beliebige Dinge machen. Das ist erstmal so
00:22:55.730 --> 00:23:01.320
noch nicht so schlimm und das ist ja auch
ein sehr akademisches Beispiel, weil ich
00:23:01.320 --> 00:23:03.860
es alles selber geschrieben habe. Ich muss
auf dem eigenen Rechner sein, ich bin eh
00:23:03.860 --> 00:23:08.330
schon der Benutzer, aber interessant wird
es natürlich, wenn so eine Verwundbarkeit
00:23:08.330 --> 00:23:11.920
irgendwo an der Stelle sitzt, die auf das
Netzwerk horcht. Wenn man von außen
00:23:11.920 --> 00:23:16.529
irgendwie Pakete einschleusen kann, die so
etwas hervorrufen. Und ich dann irgendwie
00:23:16.529 --> 00:23:20.340
eine Shell oder ähnliches bekomme. Auch
interessant wird es, wenn das ein Dienst
00:23:20.340 --> 00:23:24.070
ist, der aus Gründen mit root-Rechten
laufen muss. Dann habe ich nämlich auf
00:23:24.070 --> 00:23:28.690
einmal eine root-Shell. Und so weiter und
so fort. Also man kann da eine Menge
00:23:28.690 --> 00:23:36.110
lustige Sachen mit machen. Ich habe noch
etwas anderes vorbereitet. Und zwar, wenn
00:23:36.110 --> 00:23:40.050
ich jetzt einen Debugger über das Ding
laufen lassen. Ein Debugger ist ein
00:23:40.050 --> 00:23:45.340
Programm, mit dem ich zur Laufzeit rein
gucken kann, in den Speicher, und schauen
00:23:45.340 --> 00:23:49.019
kann, was denn der Prozess so gerade da
macht, und was wo an welcher Stelle im
00:23:49.019 --> 00:23:53.410
Speicher geschrieben wird. Hier sehe ich
jetzt noch mal den Quellcode von dem
00:23:53.410 --> 00:23:56.310
Programm. Das ist das gleiche wie eben,
bis auf das der Buffer ein bisschen größer
00:23:56.310 --> 00:24:03.510
ist. Das spielt aber keine große Rolle und
ich habe zwei breakpoints gesetzt.
00:24:03.510 --> 00:24:07.049
Breakpoint heißt, das Programm oder der
Debugger vielmehr, hält das Programm an
00:24:07.049 --> 00:24:12.550
der Stelle an, damit man halt schauen
kann, was an bestimmten Speicheradressen
00:24:12.550 --> 00:24:18.440
steht. Und einer ist hier auf dieser Zeile
stringcopy, Zeile 6. Also der macht, hält
00:24:18.440 --> 00:24:23.809
an bevor das reinkopiert wurde, und der
nächste breakpoint hält kurz vor dem
00:24:23.809 --> 00:24:27.240
return an. Und da müsste ich halt dann
schon sehen, was im Buffer passiert ist.
00:24:27.240 --> 00:24:31.759
Wenn ich das Programm jetzt laufen lasse,
mit den richtigen Argumenten, das ist
00:24:31.759 --> 00:24:36.360
alles schon hoffentlich konfiguriert, ja,
dann sehe ich hier, das es ist ein
00:24:36.360 --> 00:24:40.090
Ausschnitt aus dem Buffer, und zwar die
letzten, irgendwo in den letzten paar
00:24:40.090 --> 00:24:45.249
hundert Bytes, da steht ziemlich
zufälliger Quatsch drin. Keine Ahnung, was
00:24:45.249 --> 00:24:50.259
das ist das. Das hat irgend ein Prozess
vorher, oder vielleicht möglicherweise
00:24:50.259 --> 00:24:56.169
auch der init Prozess, also das was vor
meinem eigentlichen main Programm läuft,
00:24:56.169 --> 00:25:02.929
da hingelegt, keine Ahnung, weiß ich
nicht. Und die return Adresse steht im
00:25:02.929 --> 00:25:07.090
Moment noch auf dieser lustigen,
kryptischen Zahl. Das wird irgendwas sein,
00:25:07.090 --> 00:25:11.700
was, nachdem main beendet wurde, also die
Funktion main beendet wurde,
00:25:11.700 --> 00:25:18.361
wahrscheinlich noch irgendwie Speicher
aufräumt oder so etwas. So, jetzt sind wir
00:25:18.361 --> 00:25:22.500
also an der Stelle stringcopy, also es ist
noch nichts passiert. Wenn ich jetzt zum
00:25:22.500 --> 00:25:31.460
nächsten breakpoint weiterlaufe, dann seht
ihr? Hier oben sind ganz viele 0x90
00:25:31.460 --> 00:25:35.071
Instruktionen. Das sind die Anweisungen
für den Prozessor: mach mal nichts, warte
00:25:35.071 --> 00:25:39.929
mal einen Takt lang. Und davon habe ich
ganz viele da rein geschrieben und
00:25:39.929 --> 00:25:45.409
irgendwann kommt dann hier dieser Shell
Code. Das war das Ding, was eben das
00:25:45.409 --> 00:25:50.710
Programm beendet und die Shell startet.
Und ich kann auch schon sehen, die return
00:25:50.710 --> 00:25:53.870
Adresse ist jetzt überschrieben worden mit
einer Adresse, die ich selber gewählt
00:25:53.870 --> 00:25:59.211
habe. Und diese Adresse, die zielt
irgendwo oben vor den Shell Code in diese
00:25:59.211 --> 00:26:04.770
ganzen no-operation Instruktionen, das
nennt man eine NOP-slide. Das macht man,
00:26:04.770 --> 00:26:07.460
um ein bisschen zu puffern und ein
bisschen Platz zu haben und ein bisschen
00:26:07.460 --> 00:26:10.779
Freiheit zu haben, weil wenn so ein
Prozess gestartet wird, dann hängen da
00:26:10.779 --> 00:26:16.450
oben über dem Stack noch ganz viele andere
Sachen und die können sich, nicht zur
00:26:16.450 --> 00:26:19.149
Laufzeit, die können sich aber auch von
Programmausführung zu Programmausführung
00:26:19.149 --> 00:26:23.779
ändern. Wenn ich zum Beispiel eine neue
Variable definiere oder whatever.
00:26:23.779 --> 00:26:26.629
Jedenfalls habe ich die Sachen nicht immer
exakt an der gleichen Speicheradresse und
00:26:26.629 --> 00:26:29.260
deswegen macht man gerne so einen NOP-
slide. Also man nimmt seinen Shell Code,
00:26:29.260 --> 00:26:32.720
packt den irgendwo in die Mitte, packt
davor ganz viele NOPs, wo der Rechner
00:26:32.720 --> 00:26:35.389
einfach so durchtingelt und nichts tut,
und dann irgendwann an dem Shell Code
00:26:35.389 --> 00:26:39.400
ankommt und dahinter packt man ganz viele
return Adressen, die oben in diese NOP-
00:26:39.400 --> 00:26:44.850
Slide reinspringen. Das heißt, egal ob ich
den Bereich vor dem Shell Code treffe oder
00:26:44.850 --> 00:26:47.870
den Bereich hinter dem Shell Code treffe,
es passiert immer was passieren muss,
00:26:47.870 --> 00:26:52.110
nämlich er springt im ungünstigsten Fall
von hinterm Shell Code zu vor dem Shell
00:26:52.110 --> 00:26:55.960
Code und hangelt sich dann durch bis zum
Shell Code, der dann das tut, was er tut.
00:26:55.960 --> 00:27:08.669
Und das ist der Buffer Overflow Exploit.
Demo Time! Ja wobei, wie ich schon sagte,
00:27:08.669 --> 00:27:12.620
das ist ein sehr akademisches Beispiel,
weil ich das alles halt sehr, um es
00:27:12.620 --> 00:27:18.750
einfach zu halten, sehr klein gehalten
habe. In der Realität würde man sagen, es
00:27:18.750 --> 00:27:22.081
ist ja keiner so blöd, wenn da irgendwas
in den Puffer rein schreibt, nicht zu
00:27:22.081 --> 00:27:25.049
checken, wie groß denn der Puffer ist und
mal zu gucken, ob man überhaupt noch da
00:27:25.049 --> 00:27:33.639
rein schreiben darf. Das stimmt auch,
meistens. lacht Das Problem ist: selbst,
00:27:33.639 --> 00:27:38.340
wenn es immer gemacht würde, habe ich oft
das Problem, dass ich das zu der Zeit, wo
00:27:38.340 --> 00:27:40.929
ich das Programm schreibe, noch gar nicht
wissen kann, wie groß dieser Buffer sein
00:27:40.929 --> 00:27:43.950
muss, weil ich es erst zur Laufzeit
mitbekomme, wie viele Pakete da jetzt
00:27:43.950 --> 00:27:47.769
gleich ankommen, wenn es zum Beispiel ein
Netzwerkdienst ist. Das heißt, die Größe
00:27:47.769 --> 00:27:50.799
von so einem Buffer wird oft berechnet und
wenn ich jetzt bei der Berechnung der
00:27:50.799 --> 00:27:53.881
Größe irgend einen Fehler habe, der dazu
führt, dass ich eine falsche Größe raus
00:27:53.881 --> 00:27:58.139
bekomme, die dann dazu führt, dass der
Rechner, oder das Programm vielmehr,
00:27:58.139 --> 00:28:03.260
denkt, der Buffer ist riesig groß, dann
schreibt das Ding halt weiter. Das ist
00:28:03.260 --> 00:28:12.340
eine Sache, die oft ausgenutzt wird und
Daten kommen halt eben, let's face it,
00:28:12.340 --> 00:28:18.389
meistens nicht von der Kommandozeile, aber
stattdessen kommen die aus Dateien, die
00:28:18.389 --> 00:28:22.539
ich irgendwie manipulieren kann oder sogar
aus dem Netzwerk. Da habe ich natürlich
00:28:22.539 --> 00:28:28.259
dann beliebige Angriffsvektoren. Das
heißt, was in der Realität passieren
00:28:28.259 --> 00:28:32.910
würde, also jetzt beispielsweise bei
diesen drei Dingern die wir da hatten: Das
00:28:32.910 --> 00:28:40.119
Erste war ja der Firefox Browser. Da würde
man vermutlich ein Bild beispielsweise
00:28:40.119 --> 00:28:45.139
sich zusammen bauen, was irgendwie den
Bild Standard nicht ganz erfüllt oder wo
00:28:45.139 --> 00:28:52.880
irgendwo ein Byte falsch ist, so dass, wenn
der Browser das Bild lädt, sich irgendwo
00:28:52.880 --> 00:28:57.220
verhaspelt und irgendwo es zu einem Buffer
Overflow kommt, weil z. B. ein Puffer zu
00:28:57.220 --> 00:29:02.650
klein ausgelegt wird. Oder ich kann
versuchen, es über Javascript Dinge zu
00:29:02.650 --> 00:29:07.679
machen, aber das ist eigentlich noch was
anderes. Im zweiten Fall, von dem Adobe
00:29:07.679 --> 00:29:12.659
Reader, sind es auch oft tatsächlich
irgendwelche Bilder. Das Problem bei dem
00:29:12.659 --> 00:29:15.850
Adobe Reader ist halt so ein bisschen das
der so eine eierlegende Wollmilchsau ist.
00:29:15.850 --> 00:29:22.140
Ich kann mit dem Teil ja beliebige
Bildformate anzeigen lassen, Text mit
00:29:22.140 --> 00:29:25.509
eigenen Schriften, dass wäre auch ein
Angriffsvektor, wenn ich da eine eigene
00:29:25.509 --> 00:29:28.789
Schrift einbauen und die Schrift halt
irgendwie so baue, dass beim Einlesen und
00:29:28.789 --> 00:29:33.970
beim Parsen und Bauen dieser Schrift der
Reader irgendwie einen Buffer Overflow
00:29:33.970 --> 00:29:39.889
kriegt. Bis hin zu 3D Elemente, ich habe
gehört, sie haben teile von Flash in den
00:29:39.889 --> 00:29:45.350
Adobe Reader eingebaut, damit man damit
dann 3D Modelle irgendwie anzeigen und
00:29:45.350 --> 00:29:50.299
auch schön drehen kann. Was man ja alles
braucht, alles im gleichen Programm, gute
00:29:50.299 --> 00:29:55.309
Idee! Naja jedenfalls, auch da bieten sich
verschiedenste Angriffsvektoren und was
00:29:55.309 --> 00:30:00.940
man machen würde ist halt eine Datei
bauen, die halten den Fehler ausnutzt und
00:30:00.940 --> 00:30:07.510
einen Buffer Overflow erzeugt. Der Angriff
den ich hier gezeigt habe, der ist heute
00:30:07.510 --> 00:30:11.919
aus verschiedenen Gründen nicht mehr
möglich. Ich musste drei Dinge abschalten
00:30:11.919 --> 00:30:16.220
um den überhaupt laufen lassen zu können.
Das Eine ist, wie ihr euch wahrscheinlich
00:30:16.220 --> 00:30:19.340
jetzt schon gedacht habt, naja dann könnte
ich doch einfach sagen ich habe einen
00:30:19.340 --> 00:30:22.879
bestimmten Programmspeicher, ich habe
einen bestimmten Datenspeicher und was im
00:30:22.879 --> 00:30:26.480
Datenspeicher liegt, darf niemals nicht vom
Prozessor ausgeführt werden. Das gibt es.
00:30:26.480 --> 00:30:31.779
Das nennt sich "write XOR execute" und ist
seit einigen Jahren im Kernel und sogar in
00:30:31.779 --> 00:30:36.710
Hardware drin. Wenn es angeschaltet ist.
Das musste ich ausschalten, dann gibt es
00:30:36.710 --> 00:30:41.580
noch die Möglichkeit sogenannte Stack
Canaries, also Kanarienvögel, in den Stack
00:30:41.580 --> 00:30:46.919
einzubauen. Das ist so ein Magic Byte, was
da reingeschrieben wird, und ein bisschen
00:30:46.919 --> 00:30:50.669
Code, was vor dem rückkehren der Funktion
noch einmal testet, ob dieses Byte, was
00:30:50.669 --> 00:30:57.100
ich da reingeschrieben habe, also zwischen
den lokalen Variablen und der
00:30:57.100 --> 00:31:01.499
Returnadresse, moment ich mache das mal
eben grafisch, damit man das sehen kann,
00:31:01.499 --> 00:31:05.070
genau, also an diese Stelle, die jetzt da
schwarz markiert ist, zwischen dem Return
00:31:05.070 --> 00:31:10.149
und dem Buffer, würde man ein zur Laufzeit
gewähltes beliebiges Byte reinschreiben
00:31:10.149 --> 00:31:14.419
und bevor man aus der Funktion
zurückkehrt, würde man testen, ob dieses
00:31:14.419 --> 00:31:18.690
Byte, was man da vorher reingeschrieben
hat, noch das gleiche ist wie vorher. Wenn
00:31:18.690 --> 00:31:21.090
nicht, ist ein Buffer Overflow passiert
und dann lässt man das Programm besser
00:31:21.090 --> 00:31:26.239
sofort abstürzen, anstatt abzuwarten, was
jetzt kommt. Das ist ein Stack Canary.
00:31:26.239 --> 00:31:33.040
Auch das musste ich ausschalten. Und das
Dritte ist bei modernen Betriebssystemen,
00:31:33.040 --> 00:31:35.690
mittlerweile haben es glaube ich alle
drin, ist das so, dass jeder Prozess mit
00:31:35.690 --> 00:31:42.509
einem zufälligen Speicherlayout läuft. Das
heißt, ich kann nicht mehr wirklich sagen
00:31:42.509 --> 00:31:46.360
wo meine Dinge im Speicher liegen und das
macht es sehr sehr schwierig, weil ich ja
00:31:46.360 --> 00:31:51.619
irgendwo an den Shell Code, an das Ende
vom Shell Code, eine Adresse schreiben
00:31:51.619 --> 00:31:55.960
muss, die vorne in diese NOP slide
reinführt. Und wenn ich absolut keine
00:31:55.960 --> 00:31:59.009
Ahnung habe, wo diese Adresse ist, also
ich kann nicht mehr durch Vergrößern des
00:31:59.009 --> 00:32:02.179
Buffers oder durch mehr NOPs einfügen kann
ich das irgendwann nicht mehr schaffen,
00:32:02.179 --> 00:32:06.330
weil es einfach so zufällig ist, dass ich
keine Chance mehr habe herauszufinden, wo
00:32:06.330 --> 00:32:10.889
zur Laufzeit denn dieser Shell Code liegt,
und wenn ich nicht weiß wo der liegt, kann
00:32:10.889 --> 00:32:15.399
ich da nicht rein springen, kann ich also
keinen Buffer Overflow Exploit machen.
00:32:15.399 --> 00:32:19.080
Diese drei Dinge gibt es heute, die sind
in der Regel, also zwei davon macht der
00:32:19.080 --> 00:32:23.049
Compiler, das Dritte macht das
Betriebssystem, und die musste ich jetzt
00:32:23.049 --> 00:32:27.369
alle umgehen, um das überhaupt
demonstrieren zu können. Aber die zugrunde
00:32:27.369 --> 00:32:32.049
liegenden Probleme bestehen weiterhin, die
sind nämlich: Speichermanagement ist
00:32:32.049 --> 00:32:36.570
fehleranfällig, Menschen machen immer
Fehler, ist oft auch nicht so
00:32:36.570 --> 00:32:41.149
übersichtlich, Datenformate sind zu
komplex, Programme müssen zu viel
00:32:41.149 --> 00:32:45.679
gleichzeitig können und das führt alles
dazu, dass halt immer mehr Fehler
00:32:45.679 --> 00:32:48.649
passieren. Je höher die Komplexität ist,
desto höher ist natürlich die
00:32:48.649 --> 00:32:53.629
Wahrscheinlichkeit, das ich Fehler mache.
Und das ist nicht gefixt und die
00:32:53.629 --> 00:32:58.460
Programmiersprachen, die man dafür
verwendet, sind eigentlich einer Zeit
00:32:58.460 --> 00:33:03.639
entstammend, in der das Internet noch ein
freundlicher Ort war. Man kannte sich und
00:33:03.639 --> 00:33:09.100
da hat man über Security nicht so richtig
nachgedacht. Es gibt außerdem Nachfolger
00:33:09.100 --> 00:33:13.419
von dieser Praxis, also diese Praxis, die
ich jetzt hier gezeigt habe, geht halt
00:33:13.419 --> 00:33:16.679
nicht, aber es gibt Nachfolger davon, also
Modifikationen davon, die sind ein
00:33:16.679 --> 00:33:20.350
bisschen gewiefter, ein bisschen
trickreicher und die schaffen es dann,
00:33:20.350 --> 00:33:23.760
unter Umständen eben doch noch so etwas
nicht zu bekommen. Also das funktioniert
00:33:23.760 --> 00:33:30.230
heute immer noch, wie ihr halt an den
Nachrichten gesehen habt. Oft werden auch
00:33:30.230 --> 00:33:37.519
nicht alle Gegenmaßnahmen ergriffen, das
ist halt Teil der Betriebssystemhersteller
00:33:37.519 --> 00:33:43.799
bzw. Benutzer. An der Stelle danke ich und
Fragen?
00:33:43.799 --> 00:33:53.259
Applaus
00:33:53.259 --> 00:34:01.999
Herald: Herzlichen Dank haggl für diesen
wundervollen Vortrag! So, Frage-Antwort-
00:34:01.999 --> 00:34:07.580
Runde, es gibt zwei Mikrofone, die sind
dort und dort, beleuchtet, wer eine Frage
00:34:07.580 --> 00:34:15.349
hat, stellt sich bitte hinter die
Mikrofone und ich rufe dann auf. Keine
00:34:15.349 --> 00:34:24.600
Fragen? Das scheint ein ganz schön
umfangreicher Vortrag gewesen zu sein.
00:34:24.600 --> 00:34:30.210
Haggl: Keine Fragen, oder seid ihr
bedient? Keine Fragen, weil es zu viele
00:34:30.210 --> 00:34:38.109
gäbe, oder keine Fragen, weil es gar keine
mehr gibt? Achso, eine Entweder-Oder-Frage
00:34:38.109 --> 00:34:41.800
kann man nicht mit Handzeichen
beantworten, das ist schwierig. lacht
00:34:41.800 --> 00:34:46.310
Herald: Dort steht jemand, bitte.
Frage: Ich hätte mal eine Frage und zwar:
00:34:46.310 --> 00:34:51.139
Ich habe die Stelle nicht gefunden, wo die
Return-Adresse ausgerechnet wird und
00:34:51.139 --> 00:34:54.210
reingeschrieben wird, also der Return-
Wert.
00:34:54.210 --> 00:34:57.369
Haggl: Ja, die konntest du auch nicht
finden, weil ich die garnicht gezeigt
00:34:57.369 --> 00:35:16.890
habe. Ich habe dazu noch ein kleines
Pythonskript, das das alles macht. Das ist
00:35:16.890 --> 00:35:21.381
diese hier. Also der der Shellcode, den
ich vorher gezeigt habe, das ist wirklich
00:35:21.381 --> 00:35:28.569
nur der Part, der halt execve aufruft mit
/bin/sh und der wird halt hier eingelesen
00:35:28.569 --> 00:35:33.350
in diesem Pythonskript und dann baue ich
mit dem Pythonskript halt eben ganz viele
00:35:33.350 --> 00:35:37.250
NOPs davor, das ist an der Stelle, also
hier berechne ich erstmal wie lange diese
00:35:37.250 --> 00:35:41.250
NOP-slide sein soll und packe halt
den NOP-slide davor, dann kommt noch ein
00:35:41.250 --> 00:35:45.010
bisschen Padding und Alignment, damit
das alles richtig hinten rauskommt, dass
00:35:45.010 --> 00:35:50.819
die Rücksprungadresse auch an der
richtigen Stelle liegt im stack. Genau und
00:35:50.819 --> 00:35:55.511
hier oben habe ich halt diese Target-
Adresse und die kriegst du halt nur raus,
00:35:55.511 --> 00:35:58.230
wenn du das Programm einmal laufen lässt
und mit dem Debugger schaust, na wo ist
00:35:58.230 --> 00:36:02.180
denn der Buffer. Und dann kannst du halt
irgendwie eine Adresse in der Mitte von
00:36:02.180 --> 00:36:07.849
dem Buffer aussuchen, den Shellcode
dahinter packen, ein par NOPs davor, dann
00:36:07.849 --> 00:36:13.660
tut es das schon. So weit die Theorie, die
Praxis, habe ich schmerzhaft gelernt, ist
00:36:13.660 --> 00:36:18.730
ein bisschen komplizierter. Genau, also
mehr macht dieses Skript auch nicht, es
00:36:18.730 --> 00:36:20.710
nimmt den Shellcode, packt NOPs davor,
return-Adresse dahinter, fertig.
00:36:20.710 --> 00:36:26.460
Frage: Ist die Adresse eine relative oder
eine absolute Adresse?
00:36:26.460 --> 00:36:33.410
Haggl: Das ist eine absolute Adresse. Und
das ist halt das Ding, an der Stelle
00:36:33.410 --> 00:36:37.130
greift halt dieses address space layout
randomization, also dieser
00:36:37.130 --> 00:36:40.440
Abwehrmechanismus vom Betriebssystem, der
dafür sorgt, dass jedes mal, wenn ich das
00:36:40.440 --> 00:36:45.470
Programm aufrufe, diese Adresse nicht mehr
stimmt. Das kann ich euch demonstrieren.
00:36:45.470 --> 00:37:01.960
Wenn ich jetzt dieses Ding, was nämlich
der make Befehl macht, weil, wenn ich
00:37:01.960 --> 00:37:06.280
dieses make exploit mache, was ich euch
eben gezeigt habe, da wird vorher noch mit
00:37:06.280 --> 00:37:11.520
diesem Befehl hier für den nächsten
Prozess, dieses address space layout
00:37:11.520 --> 00:37:16.730
randomization ausgeschaltet. Standardmäßig
ist das an im Linux Kernel und wenn ich
00:37:16.730 --> 00:37:22.800
das nicht mache, sondern einfach nur das
Programm aufrufe und da meine payload
00:37:22.800 --> 00:37:31.620
reinschiebe, dann gibt es zwar auch einen
segmentation fault, natürlich, aber es
00:37:31.620 --> 00:37:38.230
gibt halt, ich kriege halt keine shell.
Und das ist genau aus dem Grund, weil der
00:37:38.230 --> 00:37:41.829
buffer nicht an der Stelle liegt, wo ich
ihn vermutet habe und deswegen meine
00:37:41.829 --> 00:37:46.309
Rücksprungadresse irgendwo im Speicher
zeigt, da springt er dann hin und, ups,
00:37:46.309 --> 00:37:54.020
gibts einen segmentation fault. Die beiden
anderen Dinge könnte ich auch noch eben
00:37:54.020 --> 00:37:58.550
zeigen, ich weiß nicht, wir haben noch
Zeit oder? Die beiden an Dinge könnte ich
00:37:58.550 --> 00:38:02.820
eben auch noch zeigen, also was man machen
muss, um die beiden anderen
00:38:02.820 --> 00:38:06.940
Sicherheitsmechanismen abzuschalten. Da
muss man das Programm nämlich mit diesen
00:38:06.940 --> 00:38:17.760
beiden "-z execstack", das ist dieses was
den Softwarecheck ausschaltet, ob ich
00:38:17.760 --> 00:38:23.109
gerade im stack versuche Programme, also
generell im Datenspeicher, nein im stack,
00:38:23.109 --> 00:38:28.410
versuche, Programme auszuführen. Das muss
man ausschalten. Und dann gibt es noch
00:38:28.410 --> 00:38:32.119
diesen stack protector, dass ist ein stack
canarie, dass wird auch standardmäßig
00:38:32.119 --> 00:38:37.590
eingebaut. Muss man also auch explizit
ausschalten, um das möglich zu machen. Das
00:38:37.590 --> 00:38:41.339
heißt , Programme, die mit modernen
compilern und modernen compiler flags
00:38:41.339 --> 00:38:45.730
kompiliert wurden, sollten trade mark
eigentlich nicht so leicht verwundbar
00:38:45.730 --> 00:38:50.450
sein. Und wenn dann noch address space
layout randomization dazu kommt, dann ist
00:38:50.450 --> 00:38:54.799
das schon relativ gut, da muss man schon
echt eine Menge Zeug machen, um dann noch
00:38:54.799 --> 00:39:01.710
irgendwie reinzukommen. Aber es geht halt
trotzdem.
00:39:01.710 --> 00:39:05.750
Herald: Gut ich sehe da drüben an dem
Mikrofon noch jemanden, der wartet, bitte.
00:39:05.750 --> 00:39:11.960
Frage: Kann man nicht einfach relative
Adressen verwenden, um ASLR auszuhebeln
00:39:11.960 --> 00:39:17.491
und wenn nein, warum nicht?
Haggl: Was meinst du mit relativ, relativ
00:39:17.491 --> 00:39:23.410
wozu?
Frage: Naja, also ich meine, ah nein, ich
00:39:23.410 --> 00:39:30.430
habe es gerafft, okay.
Haggl: Aber im Prinzip bist du auf dem
00:39:30.430 --> 00:39:35.839
richtigen Weg, also was man halt machen
kann, ist, man kann herausfinden, wo die
00:39:35.839 --> 00:39:40.940
libc zum Beispiel anfängt. Und wenn ich
weiß, wo die libc anfängt, dann weiß ich
00:39:40.940 --> 00:39:45.440
auch, wenn ich zudem noch die Version von
der libc kenne, die da reingelinkt wurde,
00:39:45.440 --> 00:39:50.420
weiß ich dann auch, wo bestimmte Dinge aus
der libc im Speicher liegen. Und sobald
00:39:50.420 --> 00:39:55.369
ich das habe, kann ich halt dann anfangen,
mir wieder Dinge zusammenzubauen. Das geht
00:39:55.369 --> 00:39:57.290
aber dann eher in Richtung return oriented
programming.
00:39:57.290 --> 00:40:06.680
Herald: Gut, sieht aus, als sei die Frage
beantwortet, gibt es noch weitere Fragen?
00:40:06.680 --> 00:40:10.530
Das scheint nicht der Fall zu sein, aus
dem Internet scheint auch keine Frage zu
00:40:10.530 --> 00:40:21.480
kommen. Dann würde ich hiermit den Vortrag
schließen, herzlichen Dank haggl!
00:40:21.480 --> 00:40:27.380
Applaus
00:40:27.380 --> 00:40:33.275
35c3 Abspannmusik
00:40:33.275 --> 00:40:47.000
Untertitel erstellt von c3subtitles.de
im Jahr 2020. Mach mit und hilf uns!