Die Registry und Privilegien

Der folgende Artikel bezieht sich auf die Betriebsysteme Windows2000/XP. Die hier aufgeführten Tests wurden unter Windows2000 unter einem Administratoraccount durchgeführt.
Für das Nachvollziehen der hier gemachten Aussagen sind folgende Zusatzprogramme erforderlich:
- PrivAktivate
- Eine Version von Profan² bzw. XProfan.

Einen etwas genaueren Blick auf eine Sache zu riskieren, kann manchmal sehr interessante Ergebnisse hervorbringen, und dieser "genauere Blick" soll hier einmal auf den Registryschlüssel 'Security' unter HKEY_LOCAL_MACHINE fallen, der normalerweise ziemlich unscheinbar aussieht:

Aber ist das wirklich alles, was dieser Schlüssel zu bieten hat? Wir schauen jetzt mal etwas genauer!

Dazu starten wir zuerst PrivAktivate. Über den Menüpunkt 'Programm' und 'PrivAktivate als Service starten' verleihen wir PrivAktivate den Access Token des Betriebssystems mit dessen Privilegien und Zugriffsrechten. Über den zweiten Button von links kann nun RegEdit gestartet werden.


Jetzt sieht die Sache schon ganz anders aus...


Diesen Schlüssel darf also offensichtlich nur das Betriebsystem auslesen. Ein Blick auf die Zugriffsrechte bestätigt dieses:



Unter dem Schlüssel 'SECURITY\Policy\Accounts' finden wir jetzt einige cryptische Unterschlüssel.


Ein Blick in PrivAktivate zeigt, das es sich bei diesen Unterschlüsseln um String-SIDs, also quasi um verschlusselte Gruppen- und Accountnamen, handelt.


Auch unter den "Gruppen- und Accountnamen" befinden sich wiederum Unterschlüssel, und unser Augenmerk richten wir jetzt auf den Unterschlüssel 'Privlgs' unter dem String-SID für die Gruppe Administratoren.


'Privlgs' klingt irgendwie nach "Privileges" also "Privilegien" - stehen dort etwa die Privilegien der jeweiligen Gruppen? Und wenn ja, in welcher Form stehen die da??? Also erst einmal ein Blick auf den dort stehenden Standardwert:

Das ganze sieht etwas verwirrend aus, deshalb hier das was bei mir da unter Windows2000 zu finden ist jetzt mit mehr Struktur:

11 00 00 00 => Das entspricht der Zahl 17!

00 00 00 00

08 00 00 00 00 00 00 00

00 00 00 00

11 00 00 00 00 00 00 00

03 00 00 00

12 00 00 00 00 00 00 00

00 00 00 00

0C 00 00 00 00 00 00 00

00 00 00 00

13 00 00 00 00 00 00 00

00 00 00 00

18 00 00 00 00 00 00 00

00 00 00 00

09 00 00 00 00 00 00 00

00 00 00 00

14 00 00 00 00 00 00 00

00 00 00 00

16 00 00 00 00 00 00 00

00 00 00 00

0B 00 00 00 00 00 00 00

00 00 00 00

0D 00 00 00 00 00 00 00

00 00 00 00

0E 00 00 00 00 00 00 00

00 00 00 00

0A 00 00 00 00 00 00 00

00 00 00 00

0F 00 00 00 00 00 00 00

00 00 00 00

05 00 00 00 00 00 00 00

00 00 00 00

17 00 00 00 00 00 00 00

00 00 00 00

19 00 00 00 00 00 00 00

00 00 00 00



So, jetzt vergleichen wir das ganze mal mit den Rückgaben dieses Quelltextes, der die Privilegien über LsaEnumerateAccountRights ausliest:

DEF @LsaOpenPolicy(4) !"advapi32","LsaOpenPolicy"

DEF @LsaClose(1) !"advapi32","LsaClose"

DEF @LsaNtStatusToWinError(1) !"advapi32","LsaNtStatusToWinError"

DEF @LsaEnumerateAccountRights(4) !"advapi32","LsaEnumerateAccountRights"

DEF @MultiByteToWideChar(6) !"kernel32","MultiByteToWideChar"

DEF @LookupAccountName(7) !"advapi32","LookupAccountNameA"

DEF @GetACP(0) !"kernel32","GetACP"

DEF @CopyMemory(3) !"kernel32","RtlMoveMemory"

DEF @WideCharToMultiByte(8) !"kernel32","WideCharToMultiByte"

DEF @LsaFreeMemory(1) !"advapi32","LsaFreeMemory"

DEF @LocalSize(1) !"kernel32","LocalSize"

DEF @LookupPrivilegeValue(3) !"advapi32","LookupPrivilegeValueA" 'Ermittelt aus dem Namen eines Privilegs dessen Luid.

Declare Fehler&,UNICODE#,Attribut#,POLICY_Handle#

Declare Policy_Handle&,PrivilegSET#,PRIVILEG#,Gesammtlänge&

Declare SID#,DOMAIN#,SID_TYPE_INDICATOR#,DOMAIN#,Attribut#,ALLPRIVS#,COUNT#,SID#,DOMAINNAME_SIZE_ADDR#

Declare SYSTEM$,DOMAIN$,SID_Size#,Account$,LUID#,LUID$

Declare PrivPointer#,ComparePriv#,Zähler%,Länge%,Zeiger&

Struct LSA_UNICODE_STRING=Length%,MaximumLength%,Buffer&

DIM UNICODE#,LSA_UNICODE_STRING

Clear UNICODE#

Struct LSA_OBJECT_ATTRIBUTES=Length&,RootDirectory&,Length%,MaximumLength%,Buffer&,Attributes&,SecurityDescriptor#(4),SecurityQualityOfService#(4)

DIM Attribut#,LSA_OBJECT_ATTRIBUTES

Clear Attribut#

DIM LUID#,8

Dim PrivPointer#,4

DIM ALLPRIVS#,2000

DIM COUNT#,4

DIM DOMAIN#,256

DIM SID#,1

DIM SID_Size#,4

DIM DOMAINNAME_SIZE_ADDR#,4

DIM SID_TYPE_INDICATOR#,256

DIM POLICY_Handle#,4

DIM PRIVILEG#,100

DIM PrivilegSET#,100

DIM ComparePriv#,100

Windowstyle 31

Windowtitle "Privilegien und Accountrechte listen"

Window 0,0-640,440

CLEAR SID#,DOMAIN#,SID_TYPE_INDICATOR#,Attribut#,ALLPRIVS#,COUNT#,DOMAINNAME_SIZE_ADDR#

Long SID_Size#,0=1

LONG DOMAINNAME_SIZE_ADDR#,0=255

LET SYSTEM$=""

LET ACCOUNT$=@input$("Accountnamen eingeben (kein Alias):","Account","Administratoren")

LET Fehler&=@LookupAccountName(@ADDR(System$),@addr(ACCOUNT$),SID#,SID_Size#,DOMAIN#,DOMAINNAME_SIZE_ADDR#,SID_TYPE_INDICATOR#)

Dispose SID#

DIM SID#,@long(SID_Size#,0)

CLEAR SID#,DOMAIN#,SID_TYPE_INDICATOR#,SYSTEM$

LONG DOMAINNAME_SIZE_ADDR#,0=255

LET Fehler&=@LookupAccountName(@ADDR(System$),@addr(ACCOUNT$),SID#,SID_Size#,DOMAIN#,DOMAINNAME_SIZE_ADDR#,SID_TYPE_INDICATOR#)

If Fehler&=1

LET Fehler&=@LsaOpenPolicy(0,Attribut#,$800,POLICY_Handle#)

Let Fehler&=@LsaNtStatusToWinError(Fehler&)

LET Policy_Handle&=@LONG(POLICY_Handle#,0)

IF Fehler&=0

Clear PrivPointer#

Clear ALLPRIVS#

LET Fehler&=@LsaEnumerateAccountRights(Policy_Handle&,SID#,PrivPointer#,Count#)

Let Fehler&=@LsaNtStatusToWinError(Fehler&)

LET Gesammtlänge&=@LocalSize(@LONG(PrivPointer#,0))

Case FEHLER&=0 : @CopyMemory(ALLPRIVS#,@LONG(PrivPointer#,0),Gesammtlänge&)

Clear Zähler%

WHILENOT Zähler%=@long(COUNT#,0)

Clear PrivilegSet#

Clear ComparePriv#

Let Länge%=@Word(ALLPRIVS#,8*Zähler%)

LET Zeiger&=@Long(ALLPRIVS#,8*Zähler%+4)

@CopyMemory(ComparePriv#,Zeiger&,Länge%)

Let Fehler&=@WideCharToMultiByte(@GetACP(),0,ComparePriv#,@INT(LÄnge%/2),PrivilegSet#,100,0,0)

Addstring @String$(PrivilegSet#,0)

'Hier werden die Privilegnamen der Privilegien in deren LUIDs, das sind 8 Byte

'große Kennstrukturen, umgewandelt.

Clear LUID#

Let Fehler&=@LookupPrivilegeValue(0,PrivilegSet#,LUID#)

LET LUID$="$"+@Right$("00"+@HEX$(@BYTE(LUID#,0)),2)

LET LUID$=LUID$+@Right$("00"+@HEX$(@BYTE(LUID#,2)),2)

LET LUID$=LUID$+@Right$("00"+@HEX$(@BYTE(LUID#,4)),2)

LET LUID$=LUID$+@Right$("00"+@HEX$(@BYTE(LUID#,6)),2)

IF @val(LUID$)<>0

Addstring "Luid des Privilegs="+LUID$

endif

inc Zähler%

Wend

LET FEHLER&=@LsaFreeMemory(@LONG(PrivPointer#,0))

LET Fehler&=@LsaClose(Policy_Handle&)

Let Fehler&=@LsaNtStatusToWinError(Fehler&)

@EDITBOX("Gefundene Privilegien...",1)

Endif

endif

Dispose PrivPointer#

Dispose POLICY_Handle#

Dispose ALLPRIVS#

Dispose COUNT#

Dispose DOMAIN#

Dispose SID#

Dispose SID_Size#

Dispose DOMAINNAME_SIZE_ADDR#

Dispose SID_TYPE_INDICATOR#

Dispose POLICY_Handle#

Dispose PRIVILEG#

Dispose PrivilegSET#

Dispose ComparePriv#

Dispose LUID#

While 0=0

Waitinput

Wend



Hier die Ergebnisse:

SeSecurityPrivilege

Luid des Privilegs=$08000000

SeBackupPrivilege

Luid des Privilegs=$11000000

SeRestorePrivilege

Luid des Privilegs=$12000000

SeSystemtimePrivilege

Luid des Privilegs=$0C000000

SeShutdownPrivilege

Luid des Privilegs=$13000000

SeRemoteShutdownPrivilege

Luid des Privilegs=$18000000

SeTakeOwnershipPrivilege

Luid des Privilegs=$09000000

SeDebugPrivilege

Luid des Privilegs=$14000000

SeSystemEnvironmentPrivilege

Luid des Privilegs=$16000000

SeSystemProfilePrivilege

Luid des Privilegs=$0B000000

SeProfileSingleProcessPrivilege

Luid des Privilegs=$0D000000

SeIncreaseBasePriorityPrivilege

Luid des Privilegs=$0E000000

SeLoadDriverPrivilege

Luid des Privilegs=$0A000000

SeCreatePagefilePrivilege

Luid des Privilegs=$0F000000

SeIncreaseQuotaPrivilege

Luid des Privilegs=$05000000

SeChangeNotifyPrivilege

Luid des Privilegs=$17000000

SeUndockPrivilege

Luid des Privilegs=$19000000

SeInteractiveLogonRight

SeNetworkLogonRight


In dieser Registrystruktur scheint also zuerst ein Longwert zu stehen, der die Anzahl der Privilegien angibt. Danach kommen offensichtlich die LUIDs der einzelnen Privilegien, die durch eine weitere LongInt Zahl voneinander getrennt sind. Aber was ist das für eine LongIntzahl? Aufschluss darüber könnte die LUID_AND_ATTRIBUTES Struktur aus der WIN32.HLP geben:

Zitat:"

The LUID_AND_ATTRIBUTES structure represents a locally unique identifier (LUID) and its attributes.

Members:

Luid

Specifies an LUID value.

Attributes

Specifies attributes of the LUID. This value contains up to 32 one-bit flags. Its meaning is dependent on the definition and use of the LUID.

Remarks:

An LUID_AND_ATTRIBUTES structure can represent an LUID whose attributes change frequently, such as when it is used to represent privileges in the PRIVILEGE_SET structure. Privileges are represented by LUIDs and have attributes indicating whether they are currently enabled or disabled."


Zitat:"

SE_PRIVILEGE_ENABLED = $2

SE_PRIVILEGE_ENABLED_BY_DEFAULT = $1"


Diese dort zu findenden Attribute scheinen also die Grundeinstellungen der Privilegien zu verkörpern.

Sehr interessant ist in diesem Zusammenhang auch die MSDN Beschreibung der Funktion LsaRemoveAccountRights:

Zitat:"

LsaRemoveAccountRights

The LsaRemoveAccountRights function removes one or more privileges from an account.

You can specify the privileges to be removed, or you can set a flag to remove all privileges.

When you remove all privileges, the function deletes the account. If you specify

privileges not held by the account, the function ignores them."

Hier wird gesagt, das ein Account gelöscht wird, wenn alle Privilegien des Accounts entfernt wurden. Das ist natürlich nicht so - was wirklich entfernt wird ist das Account Objekt, das durch den hier angesprochenen Registryschlüssel mit dem Namen des jeweiligen Accounts oder der Gruppe verkörpert wird.
Was also gelöscht wird, ist der Eintrag in der Registry und nicht die Möglichkeit, sich einzuloggen.


Impressum