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!