Windows Integrity Control: Schreibzugriff auf eine Named Pipe eines Services über anonymen Zugriff auf Vista, Windows 2008 Server und Windows 7

Mein Problem war ein Service, der eine Pipe als Interface zur Verfügung stellt, auf die von beliebigen PCs zugegriffen werden soll. Insbesondere eben auch von PCs außerhalb der Domäne in der der Service läuft der die Named Pipe zur Verfügung stellt. Die Clients melden sich über VPN an dem entsprechenden Server an, gehören aber eben selbst nicht zur Domäne.

Von einem Rechner, der nicht in der Domäne des Rechners liegt, auf eine Pipe eines Rechners in einer Domäne zuzugreifen war schon immer mit extra Vorkehrungen verbunden. Der Code für den anonymen Zugriff auf eine Pipe finden wir als KB Artikel http://support.microsoft.com/kb/813414/en-us. Über die Qualität dieses Beispiels möchte ich mich hier allerdings nicht auslassen.

Dieses Sample ist nett für alle die XP und Windows 2003 Server nutzen. Und interessanter Weise funktioniert es auch auf Windows Vista, Windows 7 oder Windows Server 2008. Aber nur weil dieses Beispiel die Pipe nur lesend benutzt.

Dieses Programm berücksichtigt nicht die neuen Sicherheitsfunktionen von Vista, Windows 7 oder Windows Server 2008. Es klappt genau dann nicht, wenn der Server eben auf einem Vista, Windows 7, oder Server 2008 läuft UND die Pipe für Lesen und Schreiben geöffnet wird.  ERROR_ACCESS_DENIED (5) ist das was der Client dann bekommt, wenn die Pipe lesend und schreibend geöffnet werden soll.

Was ist das Problem ❓

Das Problem liegt in einem Sicherheitsmechanismus, der sogenannten Windows Integrity.
Ich rate jedem die Literatur der nachfolgenden beiden Links:

 

Kurzbeschreibung:
Prozessen oder auch anderen Systemobjekten wird ein Inegrity-Level zugeordnet.
Greift nun ein Prozess auf ein Systemobjekt zu, dann werden nicht nur die allgemeinen Rechte geprüft (z.B. Rechte für Anonymen Zugriff, oder die entsprechenden Gruppenrechte), sondern auch der Integrity Level dieses Prozesses wird mit dem der Ressource verglichen.
Liegt nun der Integrity Level des Clients niedriger als der Intergrity Level des Objektes, dann greift eine neue Policy, die dann festlegt was passiert. In den meisten Fällen bedeutet dies, dass der Lesezugriff zugelassen wird, aber der Schreibzugriff untersagt wird.

Übrigens passiert das gleiche, wenn man ein Addin für den IE8 auf Vista (und später hat). Der IE8 läuft im geschützten Modus und damit im Integrity Level Low. Normale Programme laufen im Integrity Level Medium.
Möchte nun das IE8 Addin eine Pipe oder einen Shared Memory Block eines Services oder eines „normalen“ anderen Programmes, schreibend öffnen, dann passiert das gleiche. Der Zugriff wird reduziert auf nur lesen.
Und dieses Thema wird hauptsächlich in der Doku und im Netz diskutiert (und zum Teil, gelöst).

Ich will das jetzt hier nicht über die Maßen ausdehnen. Ich kann nur anraten, die entsprechenden Artikel wirklich einmal zu lesen.

Jetzt zurück zu dem was NICHT in diesen entsprechenden Artikeln und Forumbeiträgen steht.

1. Anonymen Zugriff wird automatisch der Integrity Level Untrusted zugewiesen. Die Doku zeigt in dem Beispiel nur die Vorgehensweise für den Integrity Level low.
Mein Fehler war es, die ganze Zeit SECURITY_MANDATORY_LOW_RID zu verwenden. Aber ich sagte es schon: Der  Anonyme Zugriff erfolgt im Integrity Level Untrusted.

2. Ich arbeite mit SDDL String und auch dort in den Headern ist Low das niedrigste für das es im Netz und auch in der Doku (http://msdn.microsoft.com/en-us/library/bb625963.aspx) findet. Dort steht ziemlich weit unten ein Beispiel, um ein Objekt einem „Low“-Integrity-Prozess zugänglich zu machen. Und dort finden wir folgende SDDL Definition:

#define LOW_INTEGRITY_SDDL_SACL_W L"S:(ML;;NW;;;LW)"

3. Anonymer Zugriff ist also Integrity Level Untrusted (ich weiß ich wiederhole mich).
Und nun wird es spaßig. Es gibt auch gar keinen SDDL Sid für untrusted, es gibt nur die folgenden Definitionen in den Headern:

// Integrity Labels
#define SDDL_ML_LOW                         TEXT("LW")
#define SDDL_ML_MEDIUM                      TEXT("ME")
#define SDDL_ML_MEDIUM_PLUS                 TEXT("MP")
#define SDDL_ML_HIGH                        TEXT("HI")
#define SDDL_ML_SYSTEM                      TEXT("SI")

Alles das hat mich in die Irre geführt und 2 Tage Arbeit gekostet.

Als mir klar war, dass ich eine Regel für den Untrusted Intergity Level benötige, war die Lösung gefunden.
Bauen wir uns einen eigenen SID! Aus der Doku können wir die entsprechenden RIDs finden und uns nun folgenden SID konstruieren: „S-1-16-0“.
Und das bringt uns zu dem SDDL String für den Level untrusted:

#define UNTRUSTED_INTEGRITY_SDDL_SACL _T("S:(ML;;NW;;;S-1-16-0)")

Ich habe das oben beschriebene Sample etwas modifiziert, dass man genau dieses Problem des anonymen Zugriffs auf eine Pipe testen kann. Das Beispiel kann man hier herunterladen.
Ich habe das Sample dazu etwas umgebaut:

  • so dass lesender und schreibender Zugriff auch benutzt wird.
  • dass SDDL verwendet wird, was den Code extrem simplifiziert.
  • dass zumindest etwas an dem ekligem Code lesbarer wurde.
  • dass ein paar Bugs raus sind.

Ich bitte dennoch dies nur als Sample zu betrachten und nicht als Beispiel Software, die meinem Anspruch an C/C++ Code genügt 🙂

PS: Eine entsprechende Diskussion über das Problem findet sich in nntp://microsoft.public.de.vc
http://groups.google.de/group/microsoft.public.de.vc/browse_thread/thread/3be20505999b8aab
Danke noch mal explizit an den Regular Andreas Heyer für seinen wertvollen Diskussionsbeitrag.

Alle reden von Sichertheit in der IT-Branche… Alle?

Bei einem Kunden muss ich aktuell einem Problem auf den Grund gehen, das irgendwie mit Locks in den tiefsten Tiefen des SQL-Server 2005 zu tun hat. Aus diesem Grund habe ich Einblick in den originalen – doch etwas größeren – Datenbestand erhalten. Besagter Kunde ist in seiner Branche ziemlich populär. Es werden in dieser Datenbank 750.000 Kunden verwaltet. Alles aktive Kunden eines bestimmten Produkts einer kleinen geographischen Zone in Deutschland. Qualitativ wirklich aktuelles Material, denn alle diese Datensätze sind wirklich aktive Kunden, d.h. bekommen mindestens einmal im Jahr eine Rechnung.

Um sich nun mit unserer Anwendung als Benutzer Administrator anzumelden benötigte ich noch ein Kennwort. Das hatte mir keiner mitgeteilt (bis jetzt). Aber was soll’s, probieren wir doch mal 😉

  1. Versuch: Der Firmenname
  2. Versuch: Der Produktname
  3. Versuch: admin… Bingo 😮

Ich will nicht davon reden, wieviele Aktivität betrieben wird,  die Datenbanken nach außen abzuschirmen. Auch will ich nicht über die interne Panik klagen, die uns gegenüber immer geschoben wird, dass alles hoch sicher und geheim zu behandeln ist.

Jeder Angestellte, mit etwas Spielwitz kann in etwa 20 Sekunden als Admin an alle Daten…

Nur als Anmerkung: Dieses Sicherheits-Problem ist natürlich mittlerweile behoben… :mrgreen: