Windows Impersonation Bugs

In seiner langen Geschichte hat Windows leider immer wieder bewiesen, wie ein Feature zum Bug werden kann.

Dazu fällt mir immer eine Anekdote zum IIS mit ActiveX ein und wie ich lernen musste, dass “Plugins” ein wahrer Teufel sein können.


Ich hatte schon in meiner Visual-Basic Zeit den Traum, die eigene Software durch Fremdkomponenten erzweitern zu können. Das ging im klassischen VB natürlich nur durch ActiveX Komponenten.

Und nachdem Microsoft die VB6 IDE damals “teuer” verkaufte, erdachte ich mir einen Weg, wie man das kostenlos (bzw. lizenzfrei) ebenso erreichen konnte, und das war: VB5CCE, Visual Basic 5 Control Creation Edition.

Microsoft hatte so um das Jahr 1996/97 herum parallel zu Visual Basic 5 eine kostenfreie verkleinerte IDE herausgebracht, mit der man keine fertigen Programme, sondern nur reine Active-X Controls entwickeln konnte.
Das war offenbar damals “der heiße Scheiß” für den Internet Information Server und den Internet Explorer 4 und 5, die solche Active-X Controls herunterladen und ausführen konnten.

Noch 5 Jahre später wurde die VB5CCE immer noch Heft-CDs beigelegt, um Programmieranfängern eine kostenfrei Übungsumgebung bereitstellen zu können. Und eben da wurde ich (verspätet) ebenso darauf aufmerksam.

ActiveX Controls (*.ocx Dateien) mussten genau wie andere COM DLLs per regsvr32 registriert werden und obgleich VB5CEE einen Build nur zuließ, wenn mindestens ein grafisches “Control” drinnen war, so gestattete es, dass parallel auch eine beliebige Anzahl von nicht-grafischen Klassen ebenso per COM exportiert werden durften.
So konnte man also “gratis” COM-Klassen implementieren und per VB5CCE in nativen Code kompilieren.

Der RevertToSelf Bug

Früher war Software noch effizient und mit Ressourcen wurde sparsam umgegangen. Doch diese Stärke wurde beim IIS schnell zur Schwäche. Denn auch dort dachte man, dass man in ASP (dem Server-VB-Dialekt) native COM-Komponenten laden und ausführen kann.

Der Windows Scripting Host hatte es vorgemacht, wie aufwendige Funktionen in C++ oder VB geschrieben wurden und ASP-Script Code dann einfach Methoden der nativen Objekte aufrief.

“Security” wurde bei Microsoft natürlich immer schon GROSS geschrieben (Vorsicht: Ironie) und so wurde jeder Thread im IIS mit APIs wie ImpersonateLoggedOnUser auf einen nicht privilegierten Account oder den per Web-Login übermittelten Account gesetzt, damit er auch keine kritischen Daten lesen kann, die ihn nichts angehen.

Hatte nun aber ein kluger Entwickler in seinem über ASP eingebundenen COM-Plugin das Bedürfnis mehr Daten auszulesen, dann führte er einfach den Aufruf RevertToSelf aus und schon hatte der Thread LOCALSYSTEM Rechte … also die höchste Berechtigungsstufe überhaupt. Und am Domänencontroller war das noch schlimmer.

Das kam daher, dass der IIS Host Dienst (wie die meisten Systemdienste) als LOCALSYSTEM ausgeführt wurde und ASP Aufgaben in seinen Threads liefen. Die ASP-VBScript Schnittstellen waren damit natürlich auf den Account beschränkt, mit dem der Thread “impersonated” wurde, doch native Plugins konnten LOCALSYSTEM mit einem Aufruf zurückerobern und dann recht viel Blödsinn damit anstellen.

Plugins richtig auslagern

Aus diesem damals publik gewordenen Bug habe ich gelernt, dass Plugins IMMER eine Bedrohung für ein System sind. Man muss Maßnahmen ergreifen, dass kein ungeprüfter Fremdcode im Kontext der eigenen Anwendung ausgeführt werden darf.
Und das gilt erst Recht, wenn man Dienste (Services, Daemons) entwickelt.

  1. Prozess-Isolation
    Davon abgesehen, dass “Thread-Isolation” wie im obigen Beispiel nur unter Windows funktioniert und damit nicht portierbar ist, gibt es auch andere Gründe im eigenen Prozessraum keinen (oder möglichst wenig) Fremdcode auszuführen.
    “Globale Zustände” wie Ausführungsprioritäten (nice()), Zugriffsmasken (umask()) oder regionale Einstellungen (setlocale()) werden in Threads gesetzt, strahlen aber (je nach OS und Implementierung) auf den ganzen Prozess aus und bringen dann andere Komponenten durcheinander.
    Es macht also Sinn, hierfür einen eigenen Prozess zu starten, das Plugin dort zu starten und z.B. über Pipes mit ihm zu “reden”.
    Außerdem kann man Prozesse mit weniger Rechten starten, die dann keine Chance haben, Privilegien zurückzuerobern.
  2. Signaturprüfung
    Damit Fremdcode nicht einfach “eingespeist” werden kann, sollte man vor dem Laden eines Plugins eine Form von digitaler Signatur darin auslesen und überprüfen.
    Das kann auch über das Betriebssystem gemacht werden, setzt aber voraus, dass Signaturschlüssel entsprechend installiert sind und die eigene Laderoutine diese Prüfung auch nicht umgeht.
    Oder man überlegt sich selbst so ein Schema und erwartet, dass die Plugin-Binärdatei eine Signatur im Anhang hat, die man gegen seinen eigenen Erwartungswert vor dem Laden prüft.
    Dass das natürlich alles kryptographisch gut ausgearbeitet und geprüft sein muss, versteht sich von selbst.

Fazit

Es sind also wieder diese “typischen” Bugs von vor 20 Jahren, die mir auch heute durch den Kopf gehen, wenn es um die Planung von neuer Software geht. Und leider machen viele Architekten den Fehler, dass sie immer nur an Flexibilität denken um Software möglichst “erweiterbar” zu gestalten.

Genau dann wiederholen sich solche Dinge wie damals und auch noch so moderne Software wird anfällig für Angriffe.

Was das anbelangt, war weder früher noch heute alles besser.
Wir irren uns leider nur langsam voran.


Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!

Meine Dokus über:
 
Externe Links zu: