Fragen? Antworten! Siehe auch: Alternativlos
Oder nehmen wir an, ihr kennt euch mit Computer-Architektur aus. Ihr wisst, dass es einen Kernel gibt, und der kann Userspace-Programme laufen lassen, und stellt denen virtuellen Speicher zur Verfügung und Syscalls und so weiter. Natürlich wisst ihr, dass der Kernel grundsätzlich in den Speicher der Prozesse reingucken kann.
Wenn ihr also, sagen wir mal, curl benutzt, und der spricht SSL mit einem Gegenüber, dann kann der Kernel grundsätzlich in den Speicher gucken und die unverschlüsselten Daten sehen. Da wird jetzt niemand wirklich überrascht sein, dass das so ist, hoffe ich.
Aber die Details waren bisher nicht so attraktiv. Das war mit Aufwand verbunden. OK, du kannst gucken, welche SSL-Library der benutzt, und da stattdessen eine Version mit Backdoor einblenden als Kernel. Oder du könntest die Offsets von SSL_read und SSL_write raussuchen und dann da Breakpoints setzen, wie bei den Debugging-APIs, und dann halt reingucken. Du hättest potentiell auch mit Races zu tun. Du müsstest wissen, wie man aus einer Shared Library die Offsets von Funktionen rausholt.
Gut, aber dann denkst du da ein bisschen drüber nach, und plötzlich sieht das gar nicht mehr so schwierig aus. curl lädt openssl als dynamische Library.
$ ldd =curlDa sieht man schon, dass die Adressen irgendwie krumm aussehen. Das ist ASLR. Wenn du nochmal ldd machst, kriegst du andere Adressen. OK aber warte. Die Daten hat der Kernel ja, und exponiert sie sogar an den Userspace:
linux-vdso.so.1 (0x00007ffff7dd2000)
libcurl.so.4 => /usr/lib64/libcurl.so.4 (0x00007f1df4c66000)
libssl.so.3 => /usr/lib64/libssl.so.3 (0x00007f1df4b6f000)
[...]
$ cat /proc/self/mapsUnd innerhalb des Adressbereichs, an den so eine Library gemappt wird, bleibt das Offset von SSL_write ja konstant. Wie finden wir das? Nun, da gibt es Tooling for:
[...]
7f846a8d2000-7f846aa27000 r-xp 00028000 00:14 5818768 /lib64/libc.so.6
$ nm -D /usr/lib64/libssl.so.3 | grep SSL_writeGut, also grundsätzlich könnte man den Kernel so umbauen, dass er beim Mappen von libssl.so.3 immer bei Offset 35660 einen Breakpoint setzt, und dann könnte man da die Daten abgreifen. Das ist aber eine Menge Gefummel und Kernel-Space-Programmierung ist sehr ungemütlich. Der kleinste Ausrutscher kann gleich die ganze Maschine crashen.
0000000000035660 T SSL_write@@OPENSSL_3.0.0
Warum erzähle ich das alles? Stellt sich raus: Muss man gar nicht. Ist alles schon im Kernel drin. Nennt sich uprobes und ist per Default angeschaltet. Da kann man über ein Config-File im /sys/-Tree dem Kernel sagen, man möchte gerne hier einen Breakpoint haben. Dann kann man per eBPF ein über Kernelversionen portables Kernelmodul hacken, das sich an die uprobe ranhängt und die Daten kopiert. Dann gibt es ein standardisiertes Interface dafür, wie man von dem eBPF-Modul die Daten wieder in den Userspace kopiert, wenn man das möchte.
Was hat man dann? Ein Tool, das in alle SSL-Verbindungen via OpenSSL auf der Maschine reingucken kann. Der Aufwand ist so gering, dass man das im Handumdrehen mit nur ein paar Zeilen Code auch auf gnutls erweitern kann, und auf NSS (die Mozilla-Library). Auch den TLS-Code von Go oder von Java kann man abfangen (Java ist etwas fummelig, da würde man vermutlich einen gemütlicheren Weg nehmen).
Gut, für das Eintragen der uprobe und für das Laden von dem eBPF muss man root sein. Es ist also kein Elevation of Privilege im herkömmlichen Sinne.
Aber es immanentisiert das Problem, das bis dahin bloß theoretisch war. Aus meiner Sicht heißt das, dass man ab jetzt keinem Kernel trauen kann, den man da nicht selbst hingetan hat. Mit anderen Worten: In jemand anderes Container laufen geht nicht.
Das war schon immer klar, aber jetzt ist es immanent. Was sage ich jetzt. Die Tools gibt es sogar schon seit mehreren Jahren. Ein SSL-Abgreif-Tool war das 2. Projekt, das auf diesen Frameworks gebaut wurde. Es sind sogar mehrere Tutorials und Beispielanwendungen online, wie man das macht.
Das nimmt mich gerade mehr mit als es sollte. Wir haben die Vertrauensfrage in der IT auf jeder Ebene verkackt. Der Hardware kann man schon länger nicht mehr trauen. Anderer Leute Software kann man schon länger nicht mehr trauen. Dass man Hypervisoren und Containern nicht trauen kann, ist auch schon immer klar, daher machen die ja dieses Affentheater mit "memory encryption", damit ihr euch in die Tasche lügen könnt, das sei schon nicht so schlimm. Doch. Doch, ist es.
Das könnte mich alles im Moment noch kalt lassen. Ich hacke alle meine Software selber (bis auf den Kernel), OpenSSL ist bei mir statisch reingelinkt (d.h. der Kernel sieht nicht, dass ich da eine Library lade, von der er die Offsets kennt). Man kann mit uprobes und ebpf immer noch meine Anwendungen ausspähen, aber dafür muss man die erstmal reverse engineeren und pro Anwendung die OpenSSL-Offsets raussuchen und eintragen. Das nimmt einem im Moment das Framework noch nicht ab. Aber ihr merkt hoffentlich selber, wie gering die Knautschzone hier ist. Meine Software läuft auf einer physischen Maschine, keinem vserver. Sagt der ISP. Ihr könnt euch mal mal selber fragen, wie sicher ihr euch seid, das selber nachprüfen zu können, wenn euch euer Dienstleister das verspricht.
Ich spoiler mal: Könnt ihr nicht.
Ich glaube, da muss ich mal einen Vortrag zu machen. Das ist alles sehr deprimierend, finde ich.