Windows Impersonation Bugs
« | 08 Aug 2021 | »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 erweitern 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.
- 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. - 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.