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.