Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

mgrzeg.net - Admin on Rails :)

nt!_TOKEN czyli o żetonie procesu słów kilka

Zabawy ze strukturami jądra ciąg dalszy. Dziś rzucimy okiem na żeton procesu i pobawimy się trochę niektórymi jego polami.
Rozpocznę od krótkiego wprowadzenia, po czym wyjaśnię zagadkę z poprzedniego wpisu, a co będzie dalej, to się okaże :)

Żeton

Każdy proces działający w systemie posiada znacznik dostępu, dzięki któremu system może zidentyfikować kontekst procesu. Kontekst zawiera sporo różnych informacji: przywileje, konta, grupy skojarzone z procesem, etc. Podczas logowania Lsass tworzy początkowy znacznik, który przydzielany jest startowemu procesowi (domyślnie Userinit.exe, uruchamianemu przez Winlogon), a następnie dziedziczony przez kolejne procesy potomne. Alternatywną opcją jest skorzystanie z funkcji LogonUser tworzącej nowy znacznik, który można następnie przekazać do funkcji CreateProcessAsUser. Pozostaje jeszcze wywołanie funkcji CreateProcessWithLogon, która wykonuje obie poprzednie czynności: tworzy nowy znacznik, a następnie przydziela go nowemu procesowi. I to właśnie robi polecenie runas.
Na poziomie jądra systemu znacznik reprezentowany jest przez strukturę _TOKEN. W Windows XP SP3 wygląda tak:

0: kd> dt nt!_token
   +0x000 TokenSource      : _TOKEN_SOURCE
   +0x010 TokenId          : _LUID
   +0x018 AuthenticationId : _LUID
   +0x020 ParentTokenId    : _LUID
   +0x028 ExpirationTime   : _LARGE_INTEGER
   +0x030 TokenLock        : Ptr32 _ERESOURCE
   +0x038 AuditPolicy      : _SEP_AUDIT_POLICY
   +0x040 ModifiedId       : _LUID
   +0x048 SessionId        : Uint4B
   +0x04c UserAndGroupCount : Uint4B
   +0x050 RestrictedSidCount : Uint4B
   +0x054 PrivilegeCount   : Uint4B
   +0x058 VariableLength   : Uint4B
   +0x05c DynamicCharged   : Uint4B
   +0x060 DynamicAvailable : Uint4B
   +0x064 DefaultOwnerIndex : Uint4B
   +0x068 UserAndGroups    : Ptr32 _SID_AND_ATTRIBUTES
   +0x06c RestrictedSids   : Ptr32 _SID_AND_ATTRIBUTES
   +0x070 PrimaryGroup     : Ptr32 Void
   +0x074 Privileges       : Ptr32 _LUID_AND_ATTRIBUTES
   +0x078 DynamicPart      : Ptr32 Uint4B
   +0x07c DefaultDacl      : Ptr32 _ACL
   +0x080 TokenType        : _TOKEN_TYPE
   +0x084 ImpersonationLevel : _SECURITY_IMPERSONATION_LEVEL
   +0x088 TokenFlags       : Uint4B
   +0x08c TokenInUse       : UChar
   +0x090 ProxyData        : Ptr32 _SECURITY_TOKEN_PROXY_DATA
   +0x094 AuditData        : Ptr32 _SECURITY_TOKEN_AUDIT_DATA
   +0x098 OriginatingLogonSession : _LUID
   +0x0a0 VariablePart     : Uint4B

natomiast w Windows 7 SP1 64-bit ma następujący kształt:

0: kd> dt nt!_token
   +0x000 TokenSource      : _TOKEN_SOURCE
   +0x010 TokenId          : _LUID
   +0x018 AuthenticationId : _LUID
   +0x020 ParentTokenId    : _LUID
   +0x028 ExpirationTime   : _LARGE_INTEGER
   +0x030 TokenLock        : Ptr64 _ERESOURCE
   +0x038 ModifiedId       : _LUID
   +0x040 Privileges       : _SEP_TOKEN_PRIVILEGES
   +0x058 AuditPolicy      : _SEP_AUDIT_POLICY
   +0x074 SessionId        : Uint4B
   +0x078 UserAndGroupCount : Uint4B
   +0x07c RestrictedSidCount : Uint4B
   +0x080 VariableLength   : Uint4B
   +0x084 DynamicCharged   : Uint4B
   +0x088 DynamicAvailable : Uint4B
   +0x08c DefaultOwnerIndex : Uint4B
   +0x090 UserAndGroups    : Ptr64 _SID_AND_ATTRIBUTES
   +0x098 RestrictedSids   : Ptr64 _SID_AND_ATTRIBUTES
   +0x0a0 PrimaryGroup     : Ptr64 Void
   +0x0a8 DynamicPart      : Ptr64 Uint4B
   +0x0b0 DefaultDacl      : Ptr64 _ACL
   +0x0b8 TokenType        : _TOKEN_TYPE
   +0x0bc ImpersonationLevel : _SECURITY_IMPERSONATION_LEVEL
   +0x0c0 TokenFlags       : Uint4B
   +0x0c4 TokenInUse       : UChar
   +0x0c8 IntegrityLevelIndex : Uint4B
   +0x0cc MandatoryPolicy  : Uint4B
   +0x0d0 LogonSession     : Ptr64 _SEP_LOGON_SESSION_REFERENCES
   +0x0d8 OriginatingLogonSession : _LUID
   +0x0e0 SidHash          : _SID_AND_ATTRIBUTES_HASH
   +0x1f0 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH
   +0x300 pSecurityAttributes : Ptr64 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
   +0x308 VariablePart     : Uint8B


Poza pojawieniem się kilku nowych pól, zmiany nastąpiły również w najważniejszych elementach struktury. Zacznijmy od porównania pola Privileges:

XP:
   +0x054 PrivilegeCount   : Uint4B
   +0x074 Privileges       : Ptr32 _LUID_AND_ATTRIBUTES
7:
   +0x040 Privileges       : _SEP_TOKEN_PRIVILEGES

W XP jest osobne pole na licznik przywilejów (czego nie ma w 7), a informacja o przywilejach procesu przechowywana jest w strukturze _LUID_AND_ATTRIBUTES, dokładnie takiej samej, jaka jest używana do przechowywania informacji o powiązanych z procesem użytkowniku i grupach.:

0: kd> dt nt!_LUID_AND_ATTRIBUTES .
   +0x000 Luid             :
      +0x000 LowPart          : Uint4B
      +0x004 HighPart         : Int4B
   +0x008 Attributes       : Uint4B

W Viście i 7 natomiast pojawiła się nowa struktura: _SEP_TOKEN_PRIVILEGES:

0: kd> dt nt!_SEP_TOKEN_PRIVILEGES
   +0x000 Present          : Uint8B
   +0x008 Enabled          : Uint8B
   +0x010 EnabledByDefault : Uint8B

Każde z pól to 64-bitowa liczba, która spokojnie pozwoli na pomieszczenie 34 przywilejów dostępnych w 7. Nazwy mówią same za siebie, ale na wszelki wypadek wyjaśniam:

  • Present - przywileje obecne w żetonie;
  • Enabled - przywileje włączone;
  • EnabledByDefault - przywileje domyślnie włączone.

Przywileje, które są obecne, ale nie są włączone, można włączyć (lub wyłączyć te już włączone) korzystając z funkcji AdjustTokenPrivileges. API systemowe nie pozwala jednak na dodawanie, tudzież usuwanie przywilejów ze znacznika. My pobawmy się jednak trochę debuggerem i sprawdźmy, czy jednak czegoś nie da się w tej sprawie zrobić :)

Weźmy sobie nikomu nie wadzący proces (cmd.exe)

0: kd> !process 0n2740
Searching for Process with Cid == ab4
Cid handle table at fffff8a0012d7000 with 551 entries in use
PROCESS fffffa8002db2b30
...
    Token                             fffff8a001ae3270

Wiem, pokazywałem wcześniej strukturę _TOKEN, my jednak jesteśmy twardzielami i nie skorzystamy z gotowca, tylko sięgniemy po strukturę _EPROCESS:

0: kd> dt -b -v nt!_eprocess Token. fffffa8002db2b30
struct _EPROCESS, 135 elements, 0x4d0 bytes
   +0x208 Token  : struct _EX_FAST_REF, 3 elements, 0x8 bytes
      +0x000 Object : 0xfffff8a0`01ae327e
      +0x000 RefCnt : Bitfield 0y1110
      +0x000 Value  : 0xfffff8a0`01ae327e

Jak widać, w ramach procesu znacznik reprezentowany jest przez strukturę _EX_FAST_REF, która składa się z 3 elementów, ale co ciekawe - każdy z nich przesunięty jest o +0x000 w stosunku do początku struktury. Jeszcze jeden zrzut:

0: kd> dt -b -v nt!_eprocess Token.
struct _EPROCESS, 135 elements, 0x4d0 bytes
   +0x208 Token  : struct _EX_FAST_REF, 3 elements, 0x8 bytes
      +0x000 Object : Ptr64 to
      +0x000 RefCnt : Bitfield Pos 0, 4 Bits
      +0x000 Value  : Uint8B

i wszystko jasne. Wygląda więc na to, że mamy do czynienia z unią, co już zdążył zauważyć Nir Sofer, autor serwisu nirsoft.net. W przypadku ‘7’ jest jednak drobna zmiana - pole RefCnt zajmuje 4 bity, a nie 3 tak, jak to miało miejsce wcześniej. Robimy zatem zrzut (pomijamy 4 bity):

0: kd> dt nt!_token Privileges. 0xfffff8a001ae327e&0xfffffffffffffff0
   +0x040 Privileges  :
      +0x000 Present     : 0x6`02880000
      +0x008 Enabled     : 0x800000
      +0x010 EnabledByDefault : 0x800000

Gdy przypomnimy sobie zagadkę z poprzedniego wpisu, to widać było, że w 3 poleceniu używam obciętej liczby i dopiero teraz staje się jasne dlaczego tak właśnie zrobiłem.
Taki zestaw bitów odpowiada następującemu zestawowi przywilejów:

0: kd> !token fffff8a001ae3270
...
Privs:
 19 0x000000013 SeShutdownPrivilege               Attributes -
 23 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default
 25 0x000000019 SeUndockPrivilege                 Attributes -
 33 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes -
 34 0x000000022 SeTimeZonePrivilege               Attributes -

Procexp - lista przywilejów procesu 2740

A może byśmy tak spróbowali jednak dodać jakieś przywileje? :)
Pamiętając o tym, że pole Privileges przesunięte jest o 0x40 bajtów od początku struktury TOKEN i w sumie zajmuje 3*8=24 (0x18) bajtów, robimy kolejny krok:

1: kd> f 0xfffff8a001ae3270+0x40 l18 0xff
Filled 0x18 bytes

i sprawdzamy:

2: kd> !token fffff8a001ae3270
...
Privs:
 00 0x000000000 Unknown Privilege                 Attributes - Enabled Default
 01 0x000000001 Unknown Privilege                 Attributes - Enabled Default
 02 0x000000002 SeCreateTokenPrivilege            Attributes - Enabled Default
 03 0x000000003 SeAssignPrimaryTokenPrivilege     Attributes - Enabled Default
 04 0x000000004 SeLockMemoryPrivilege             Attributes - Enabled Default
 05 0x000000005 SeIncreaseQuotaPrivilege          Attributes - Enabled Default
 06 0x000000006 SeUnsolicitedInputPrivilege       Attributes - Enabled Default
 07 0x000000007 SeTcbPrivilege                    Attributes - Enabled Default
 08 0x000000008 SeSecurityPrivilege               Attributes - Enabled Default
 09 0x000000009 SeTakeOwnershipPrivilege          Attributes - Enabled Default
 10 0x00000000a SeLoadDriverPrivilege             Attributes - Enabled Default
 11 0x00000000b SeSystemProfilePrivilege          Attributes - Enabled Default
 12 0x00000000c SeSystemtimePrivilege             Attributes - Enabled Default
 13 0x00000000d SeProfileSingleProcessPrivilege   Attributes - Enabled Default
 14 0x00000000e SeIncreaseBasePriorityPrivilege   Attributes - Enabled Default
 15 0x00000000f SeCreatePagefilePrivilege         Attributes - Enabled Default
 16 0x000000010 SeCreatePermanentPrivilege        Attributes - Enabled Default
 17 0x000000011 SeBackupPrivilege                 Attributes - Enabled Default
 18 0x000000012 SeRestorePrivilege                Attributes - Enabled Default
 19 0x000000013 SeShutdownPrivilege               Attributes - Enabled Default
 20 0x000000014 SeDebugPrivilege                  Attributes - Enabled Default
 21 0x000000015 SeAuditPrivilege                  Attributes - Enabled Default
 22 0x000000016 SeSystemEnvironmentPrivilege      Attributes - Enabled Default
 23 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default
 24 0x000000018 SeRemoteShutdownPrivilege         Attributes - Enabled Default
 25 0x000000019 SeUndockPrivilege                 Attributes - Enabled Default
 26 0x00000001a SeSyncAgentPrivilege              Attributes - Enabled Default
 27 0x00000001b SeEnableDelegationPrivilege       Attributes - Enabled Default
 28 0x00000001c SeManageVolumePrivilege           Attributes - Enabled Default
 29 0x00000001d SeImpersonatePrivilege            Attributes - Enabled Default
 30 0x00000001e SeCreateGlobalPrivilege           Attributes - Enabled Default
 31 0x00000001f SeTrustedCredManAccessPrivilege   Attributes - Enabled Default
 32 0x000000020 SeRelabelPrivilege                Attributes - Enabled Default
 33 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes - Enabled Default
 34 0x000000022 SeTimeZonePrivilege               Attributes - Enabled Default
 35 0x000000023 SeCreateSymbolicLinkPrivilege     Attributes - Enabled Default
 36 0x000000024 Unknown Privilege                 Attributes - Enabled Default
 37 0x000000025 Unknown Privilege                 Attributes - Enabled Default
 38 0x000000026 Unknown Privilege                 Attributes - Enabled Default
 39 0x000000027 Unknown Privilege                 Attributes - Enabled Default
 40 0x000000028 Unknown Privilege                 Attributes - Enabled Default
 41 0x000000029 Unknown Privilege                 Attributes - Enabled Default
 42 0x00000002a Unknown Privilege                 Attributes - Enabled Default
 43 0x00000002b Unknown Privilege                 Attributes - Enabled Default
 44 0x00000002c Unknown Privilege                 Attributes - Enabled Default
 45 0x00000002d Unknown Privilege                 Attributes - Enabled Default
 46 0x00000002e Unknown Privilege                 Attributes - Enabled Default
 47 0x00000002f Unknown Privilege                 Attributes - Enabled Default
 48 0x000000030 Unknown Privilege                 Attributes - Enabled Default
 49 0x000000031 Unknown Privilege                 Attributes - Enabled Default
 50 0x000000032 Unknown Privilege                 Attributes - Enabled Default
 51 0x000000033 Unknown Privilege                 Attributes - Enabled Default
 52 0x000000034 Unknown Privilege                 Attributes - Enabled Default
 53 0x000000035 Unknown Privilege                 Attributes - Enabled Default
 54 0x000000036 Unknown Privilege                 Attributes - Enabled Default
 55 0x000000037 Unknown Privilege                 Attributes - Enabled Default
 56 0x000000038 Unknown Privilege                 Attributes - Enabled Default
 57 0x000000039 Unknown Privilege                 Attributes - Enabled Default
 58 0x00000003a Unknown Privilege                 Attributes - Enabled Default
 59 0x00000003b Unknown Privilege                 Attributes - Enabled Default
 60 0x00000003c Unknown Privilege                 Attributes - Enabled Default
 61 0x00000003d Unknown Privilege                 Attributes - Enabled Default
 62 0x00000003e Unknown Privilege                 Attributes - Enabled Default
 63 0x00000003f Unknown Privilege                 Attributes - Enabled Default
1: kd> g

Procexp - lista przywilejów procesu 2740 po modyfikacji

Wygląda więc na to, że manipulując obiektami jądra byliśmy w stanie zmienić listę przywilejów procesu. Zwracam uwagę na to, że wystarczyła nam znajomość adresu struktury EPROCESS, a korzystając z odpowiednich przesunięć i prostych operacji bitowych byliśmy w stanie dojść do miejsca, gdzie przechowywane są przywileje i odpowiednio je zmodyfikować. Mogłem zrobić jeszcze zrzut z użyciem whoami /all, ale po modyfikacji listy przywilejów, po której pojawiło się mnóstwo ‘Unknown Privilege’ whoami nie daje sobie z tym rady i przerywa działanie, nie wypisując niczego o przywilejach.

Wspomnieni w komentarzach do jednego z poprzednich wpisów Greg Hoglund i James Butler opisali technikę pozwalającą na modyfikowanie listy przywilejów w przypadku Windows XP. Nie była tak elastyczna i prosta, jak ta opisana przeze mnie odnośnie 7, a do tego były przypadki, kiedy nie można było dodać jakiegoś przywileju, ze względu na ograniczony obszar pamięci przeznaczony na struktury LUID_AND_ATTRIBUTES.
Z grubsza metoda polegała na włączaniu wyłączonych przywilejów, a w przypadku, gdy był jakiś ‘zbędny’, a nie było tego, na którym nam zależało - istniejący był modyfikowany. Zainteresowanych odsyłam do rewelacyjnej książki "Rootkity. Sabotowanie jądra systemu Windows" Grega Hoglunda i Jamesa Butlera, wydanej kilka lat temu przez Helion.

UserAndGroups

Jedną z ważniejszych modyfikacji struktury EPROCESS w stosunku do tej z Windows XP i wcześniejszych można zauważyć w przypadku pól opisujących powiązanego z procesem użytkownika i grupy. W przypadku XP mieliśmy dwa pola:

0: kd> dt nt!_token
   +0x04c UserAndGroupCount : Uint4B
   +0x068 UserAndGroups    : Ptr32 _SID_AND_ATTRIBUTES

natomiast w przypadku Windows 7 mamy:

0: kd> dt nt!_token
   +0x078 UserAndGroupCount : Uint4B
   +0x090 UserAndGroups    : Ptr64 _SID_AND_ATTRIBUTES
   +0x0e0 SidHash          : _SID_AND_ATTRIBUTES_HASH

Obok dwóch pól określających liczbę powiązanych z procesem grup i użytkowników, mamy dodatkowo pole SidHash, które zawiera ‘skrót’ pola UserAndGroups.
Zostajemy przy ‘7’ i zacznijmy od opisania pola UserAndGroups. Bierzemy nasz zmodyfikowany wcześniej proces i robimy kilka kolejnych zrzutów, przy czym dla porządku zaczynamy od zrzutu znacznika:

2: kd> !token fffff8a001ae3270
_TOKEN fffff8a001ae3270
TS Session ID: 0x1
User: S-1-5-21-3511919770-900232348-1433614282-1000
Groups:
 00 S-1-5-21-3511919770-900232348-1433614282-513
    Attributes - Mandatory Default Enabled
 01 S-1-1-0
    Attributes - Mandatory Default Enabled
 02 S-1-5-32-544
    Attributes - DenyOnly
 03 S-1-5-32-545
    Attributes - Mandatory Default Enabled
 04 S-1-5-4
    Attributes - Mandatory Default Enabled
 05 S-1-2-1
    Attributes - Mandatory Default Enabled
 06 S-1-5-11
    Attributes - Mandatory Default Enabled
 07 S-1-5-15
    Attributes - Mandatory Default Enabled
 08 S-1-5-5-0-104916
    Attributes - Mandatory Default Enabled LogonId
 09 S-1-2-0
    Attributes - Mandatory Default Enabled
 10 S-1-5-64-10
    Attributes - Mandatory Default Enabled
 11 S-1-16-8192
    Attributes - GroupIntegrity GroupIntegrityEnabled

My jednak jesteśmy twardzielami i pogrzebiemy w żywym mięsku :)

2: kd> dt nt!_token UserAndGroups fffff8a001ae3270
   +0x090 UserAndGroups : 0xfffff8a0`01ae3578 _SID_AND_ATTRIBUTES

Mamy adres obiektu typu _SID_AND_ATTRIBUTES, sprawdźmy jego zawartość

2: kd> dt -b -v nt!_SID_AND_ATTRIBUTES 0xfffff8a0`01ae3578
struct _SID_AND_ATTRIBUTES, 2 elements, 0x10 bytes
   +0x000 Sid              : 0xfffff8a0`01ae3648
   +0x008 Attributes       : 0

Przy czym sama struktura _SID_AND_ATTRIBUTES ma następujący kształt

2: kd> dt -b -v nt!_SID_AND_ATTRIBUTES
struct _SID_AND_ATTRIBUTES, 2 elements, 0x10 bytes
   +0x000 Sid              : Ptr64 to
   +0x008 Attributes       : Uint4B

Użyjmy rozszerzenia debuggera trybu jądra do interpretacji zawartości pola Sid:

2: kd> !sid 0xfffff8a0`01ae3648
SID is: S-1-5-21-3511919770-900232348-1433614282-1000

Aha! SID usera, który jest skojarzony z procesem. OK, ale gdzie jest reszta sidów - grupy, etc.???

SID od środka

Mark Russinovich w swojej książce o Windows 2000 tak pisze o SIDach:

"Zamiast używania nazw (które mogą być niepowtarzalne, ale nie muszą), do identyfikacji elementów działających w systemie Windows 2000 używa identyfikatorów bezpieczeństwa (ang. security identifier - SIDs). Takie identyfikatory posiadają uzytkownicy, grupy lokalne i domeny, komputery lokalne, domeny i członkowie domeny. SID jest wartością numeryczną o zmiennej długości, składającą się z numeru modyfikacji struktury SID, 48-bitowej wartości autoryzacji identyfikatora i zmiennej 32-bitowej wartości podautoryzacji lub identyfikatora względnego (ang. relative identifier - RID)."

A tak pisze o SID Jeffrey Richter w "Programming Server-Side Applications for Microsoft Windows 2000":

"A SID is a unique binary representation of a trustee.
[..]
SID Format
A SID is typically formatted as S-R-I-S-S...
where
 S is written literally as “S” and indicates that this series of numbers is SID.
 R is the SID’s revision level, indicated by a number (currently 1).
 I is a 48-bit number indicating the authority.
 S is a 32-bit number indicating a subauthority, also known as a relative identifier, or RID.
 S is another subauthority. There can be any number of subauhtorities in a SID."

Więcej na temat SIDów można poczytać we wspomnianych książkach, a także w tekście Joasi Subik na polskich stronach TechNet
Uzbrojeni w niezbędną wiedzę zaglądamy do windbg i robimy 2 zrzuty: pierwszy, w którym próbujemy skorzystać z definicji struktury _SID, po czym drugi - surowy zrzut pamięci, zaczynając od adresu zapisanego w polu Sid:

2: kd> dt -b -v nt!_sid 0xfffff8a0`01ae3648
struct _SID, 4 elements, 0xc bytes
   +0x000 Revision         : 0x1 ''
   +0x001 SubAuthorityCount : 0x5 ''
   +0x002 IdentifierAuthority : struct _SID_IDENTIFIER_AUTHORITY, 1 elements, 0x6 bytes
      +0x000 Value            : (6 elements)   ""
       [00] 0 ''
       [01] 0 ''
       [02] 0 ''
       [03] 0 ''
       [04] 0 ''
       [05] 0x5 ''
   +0x008 SubAuthority     : (1 elements)
    [00] 0x15
2: kd> dd 0xfffff8a0`01ae3648
fffff8a0`01ae3648  00000501 05000000 00000015 d153a49a
fffff8a0`01ae3658  35a8749c 557337ca 000003e8 00000501
fffff8a0`01ae3668  05000000 00000015 d153a49a 35a8749c
fffff8a0`01ae3678  557337ca 00000201 00000101 01000000
fffff8a0`01ae3688  00000000 00000201 05000000 00000020
fffff8a0`01ae3698  00000220 00000201 05000000 00000020
fffff8a0`01ae36a8  00000221 00000101 05000000 00000004
fffff8a0`01ae36b8  00000101 02000000 00000001 00000101

Jak widać pierwszy zrzut nie daje nam kompletu informacji, ale naprowadza na właściwy trop.
I tak:

fffff8a0`01ae3648  00000501 05000000 00000015 d153a49a
fffff8a0`01ae3658  35a8749c 557337ca 000003e8 00000501
0x01
= 1 - Revision
0x05 = 5 - SubAuthorityCount - bardzo ważne pole - tu jest informacja o tym, ile jest w sumie RIDów
0x000000000005 = 5 - IdentifierAuthority
0x00000015 = 21 - SubAuthority #1
0xd153a49a = 3511919770 - Subauthority #2
35a8749c = 900232348 - Subauthority #3
557337ca = 1433614282 - Subauthority #4
000003e8 = 1000 - Subauthority #5 - ostatnie.
Po złożeniu całości otrzymujemy:
1-5-21-3511919770-900232348-1433614282-1000
i porównując to z SIDem:
S-1-5-21-3511919770-900232348-1433614282-1000
widzimy, że wszystko się zgadza.
Tuż za nim rozpoczyna się kolejny SID.
00000501 - początek kolejnego SIDa.

Podsumowując: na numer rewizji + licznik RIDów + wartośćautoryzacji identyfikatora potrzebujemy w sumie 8 bajtów. Licznik określa nam ile mamy RIDów, każdy o rozmiarze 4 bajty. Zatem przy 5 SubAuhtorities mamy 8 + 5 * 4 = 28 bajtów. Inaczej - (liczba RID + 2) * 4 = rozmiar w bajtach SIDa.
Przy takiej arytmetyce, SID S-1-1-0 będzie zajmował 3 * 4 = 12 bajtów, a  S-1-5-5-0-104916 - 5 * 4 = 20 bajtów.
Sprawdźmy jeszcze dalsze sidy, korzystając z naszej arytmetyki, ale najpiej spójrzmy ileż to mamy SIDów:

2: kd> dt nt!_token UserAndGroupCount fffff8a001ae3270
   +0x078 UserAndGroupCount : 0xd

a zatem 13. Do dzieła!

2: kd> !sid 0xfffff8a0`01ae3648
SID is: S-1-5-21-3511919770-900232348-1433614282-1000
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28
SID is: S-1-1-0
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28+0n12
SID is: S-1-5-32-544
2: kd> !sid 0xfffff8a0`01ae3648
SID is: S-1-5-21-3511919770-900232348-1433614282-1000
2: kd> !sid 0xfffff8a0`01ae3648+0n28
SID is: S-1-5-21-3511919770-900232348-1433614282-513
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28
SID is: S-1-1-0
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28+0n12
SID is: S-1-5-32-544
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28+0n12+0n16
SID is: S-1-5-32-545
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28+0n12+0n16+0n16
SID is: S-1-5-4
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28+0n12+0n16+0n16+0n12
SID is: S-1-2-1
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28+0n12+0n16+0n16+0n12+0n12
SID is: S-1-5-11
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28+0n12+0n16+0n16+0n12+0n12+0n12
SID is: S-1-5-15
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28+0n12+0n16+0n16+0n12+0n12+0n12+0n12
SID is: S-1-5-5-0-104916
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28+0n12+0n16+0n16+0n12+0n12+0n12+0n12+0n20
SID is: S-1-2-0
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28+0n12+0n16+0n16+0n12+0n12+0n12+0n12+0n20+0n12
SID is: S-1-5-64-10
2: kd> !sid 0xfffff8a0`01ae3648+0n28+0n28+0n12+0n16+0n16+0n12+0n12+0n12+0n12+0n20+0n12+0n16
SID is: S-1-16-8192

Uff, nikt nie powiedział, że będzie łatwo... ale na tym koniec :)
Jeszcze mały zrzut surówki dla ostatniego SIDa:

2: kd> dd 0xfffff8a0`01ae3648+0n28+0n28+0n12+0n16+0n16+0n12+0n12+0n12+0n12+0n20+0n12+0n16
fffff8a0`01ae370c  00000101 10000000 00002000 00000000
fffff8a0`01ae371c  00000000 00000000 00000000 80080001

Tuż za ostatnim SIDem jest ciąg zer. Nie jest to jednak żaden ogranicznik - po prostu dalej jest sobie jakieś coś innego, dlatego tak ważne jest pamiętanie o tym ile SIDów mamy do odczytania.
W przypadku naszego procesu cała pamięć potrzebna do zapisania wszystkich SIDów powiązanych z procesem zajmowała
28+28+12+16+16+12+12+12+12+20+12+16+12=208 bajtów.
Spytacie pewnie po co te wszystkie wyliczenia? Za chwilę wszystko się wyjaśni.
Policzmy jeszcze szybko pamięć zajmowaną przez SIDy powiązane z innym procesem, którego identyfikator jest w każdym systemie ten sam: 0.

2: kd> !process 0
PROCESS fffffa80018cf5f0
    Token                             fffff8a000004c50
2: kd> !token fffff8a000004c50
_TOKEN fffff8a000004c50
TS Session ID: 0
User: S-1-5-18
Groups:
 00 S-1-5-32-544
    Attributes - Default Enabled Owner
 01 S-1-1-0
    Attributes - Mandatory Default Enabled
 02 S-1-5-11
    Attributes - Mandatory Default Enabled
 03 S-1-16-16384
    Attributes - GroupIntegrity GroupIntegrityEnabled

W tym przypadku mamy (3+4+3+3+3) * 4 = 64 bajty, a więc mniej, niż w przypadku naszego procesu.

Wróćmy na chwilę do czasów Windows XP i sprawdźmy jak to wtedy wyglądało.

SID w Windows XP

Bierzemy nikomu nic nie wadzący proces cmd.exe zwykłego użytkownika (test) i robimy kilka zrzutów:

0: kd> !process 0n1552
    Token                             e11f5450
0: kd> !token e11f5450
_TOKEN e11f5450
TS Session ID: 0
User: S-1-5-21-1614895754-1637723038-682003330-1004
Groups:
 00 S-1-5-21-1614895754-1637723038-682003330-513
    Attributes - Mandatory Default Enabled
 01 S-1-1-0
    Attributes - Mandatory Default Enabled
 02 S-1-5-32-545
    Attributes - Mandatory Default Enabled
 03 S-1-5-4
    Attributes - Mandatory Default Enabled
 04 S-1-5-11
    Attributes - Mandatory Default Enabled
 05 S-1-5-5-0-62008
    Attributes - Mandatory Default Enabled LogonId
 06 S-1-2-0
    Attributes - Mandatory Default Enabled

Przechodzimy do badanego XP i w badanym cmd wykonujemy proste:

>dir c:\ > c:\windows\system32\dirtest.txt
Odmowa dostępu.

A więc wszystko się zgadza - użytkownik nie należy do żadnej grupy, która ma prawa zapisu w katalogu c:\windows\system32.

Procexp: użytkownik i lista grup powiązanych z procesem 1552

Znajdujemy miejsce, gdzie znajduje się tablica SIDów:

0: kd> dt nt!_token UserAndGroups e11f5450
   +0x068 UserAndGroups : 0xe11f5520 _SID_AND_ATTRIBUTES
0: kd> dt nt!_SID_AND_ATTRIBUTES 0xe11f5520
   +0x000 Sid              : 0xe11f5560 Void
   +0x004 Attributes       : 0

Weźmy dla przykładu sid 
02 S-1-5-32-545
który odpowiada lokalnej grupie Użytkownicy. Jest on odległy o 68 bajtów od adresu wskazywanego przez pole Sid, więc:

0: kd> dd 0xe11f5560+0n68
e11f55a4  00000201 05000000 00000020 00000221

Sprawdźmy, co się stanie, gdy RID Użytkowników (545) zamienimy na Administratorzy (544). Przesuwamy się o 68 bajtów od początku pierwszego SIDa oraz o 12 bajtów od początku bieżącego i wstawiamy 0x20 zamiast obecnego 0x21

0: kd> f 0xe11f5560+0n68+0n12 l1 0x20
Filled 0x1 bytes

Sprawdzamy zmianę wykonując zrzut znacznika

0: kd> !token e11f5450
_TOKEN e11f5450
TS Session ID: 0
User: S-1-5-21-1614895754-1637723038-682003330-1004
Groups:
 00 S-1-5-21-1614895754-1637723038-682003330-513
    Attributes - Mandatory Default Enabled
 01 S-1-1-0
    Attributes - Mandatory Default Enabled
 02 S-1-5-32-544
    Attributes - Mandatory Default Enabled
 03 S-1-5-4
    Attributes - Mandatory Default Enabled
 04 S-1-5-11
    Attributes - Mandatory Default Enabled
 05 S-1-5-5-0-62008
    Attributes - Mandatory Default Enabled LogonId
 06 S-1-2-0
    Attributes - Mandatory Default Enabled

a na końcu przechodzimy do działającego XP i sprawdzamy w badanym cmd:

>dir c:\ > c:\windows\system32\dirtest.txt
>dir c:\WINDOWS\system32\dirtest.txt
 Wolumin w stacji C nie ma etykiety.
 Numer seryjny woluminu: 34EF-5BE7
 Katalog: c:\WINDOWS\system32
2011-06-22  14:26               640 dirtest.txt
               1 plik(ów)             640 bajtów
               0 katalog(ów)  39 861 952 512 bajtów wolnych

Procexp: użytkownik i lista grup powiązanych z procesem 1552 po modyfikacji

I okazuje się, że bez problemu zmieniliśmy procesowi przynależność do grupy - z Users do Admins - szybki awans :)
OK, to może zrobimy to samo w Windows 7? Otóż nie. W najlepszym wypadku taka modyfikacja zakończy się pięknym niebieskim ekranem śmierci. Dlaczego? Otóż począwszy od Visty w ramach struktury _TOKEN pojawiło się nowe pole: SidHash.

SidHash

Bezpośrednie manipulacje na polu Sid pozwalały w Windows XP na pełną dowolność jeśli idzie o przynależność do grup, etc. Wystarczyło znać sid grupy (użytkownika), który zabezpieczał jakiś zasób, a w jednej chwili mogliśmy mieć do niego dostęp. Sprawa jest o tyle kiepska, że logując się do domeny jako zwykły użytkownik, ale mając prawo do ładowania sterowników, można było ustawić sobie w żetonie sid domenowej grupy administratorów i... niech będzie zagadką to, co dzięki temu można było zrobić :)
Począwszy od Visty, w ramach struktury _TOKEN pojawiło się pole SidHash, które - jak nazwa wskazuje - będąc niejako ‘skrótem’ tablicy SIDów miało na celu dodatkowo zabezpieczyć żeton przed manipulacją. Teraz nie wystarcza już samo zmodyfikowanie tablicy SIDów, coś należy jeszcze począć z ‘podpisem’ (‘skrótem’) tej tablicy, albowiem za każdym razem, gdy proces jest autoryzowany, system weryfikuje poprawność tablicy wyliczając SidHash i porównując go z tym zapisanym w polu SidHash. Zobaczmy jednak co to za stwór.

2: kd> dt -b -v nt!_token SidHash. fffff8a001ae3270
struct _TOKEN, 33 elements, 0x310 bytes
   +0x0e0 SidHash  : struct _SID_AND_ATTRIBUTES_HASH, 3 elements, 0x110 bytes
      +0x000 SidCount : 0xd
      +0x008 SidAttr  : 0xfffff8a0`01ae3578
      +0x010 Hash     : (32 elements)
      [00] 0x140c
       [01] 0x52
       [02] 0
       [03] 0
       [04] 0x220
       [05] 0
       [06] 0
       [07] 0
       [08] 1
       [09] 0
       [10] 0x800
       [11] 0x80
       [12] 0
       [13] 0
       [14] 0
       [15] 0x100
       [16] 0x1de6
       [17] 0
       [18] 0x18
       [19] 0
       [20] 0
       [21] 0
       [22] 0
       [23] 0
       [24] 0
       [25] 0
       [26] 0
       [27] 0
       [28] 0
       [29] 0x200
       [30] 1
       [31] 0

Jak widać, pole SidHash to _SID_AND_ATTRIBUTES_HASH, struktura składająca się z 3 pól: SidCount (to samo, co w polu UserAndGroupCount struktury TOKEN), SidAttr (czyli to samo, co w UserAndGroups) oraz Hash, zawierającego sam skrót. Jak się okazuje, na pole to przeznaczone są 0x110 = 272 bajty, z czego 16 na dwa pierwsze pola, a reszta na 32-elementową tablicę Hash. Z prostego rachunku wynika, że każdy element tej tablicy to 8-bajtowa liczba, a zatem surowy zrzut tej tablicy wygląda tak:

2: kd> dd fffff8a001ae3270+0x0e0+0x010 l40
fffff8a0`01ae3360  0000140c 00000000 00000052 00000000
fffff8a0`01ae3370  00000000 00000000 00000000 00000000
fffff8a0`01ae3380  00000220 00000000 00000000 00000000
fffff8a0`01ae3390  00000000 00000000 00000000 00000000
fffff8a0`01ae33a0  00000001 00000000 00000000 00000000
fffff8a0`01ae33b0  00000800 00000000 00000080 00000000
fffff8a0`01ae33c0  00000000 00000000 00000000 00000000
fffff8a0`01ae33d0  00000000 00000000 00000100 00000000
fffff8a0`01ae33e0  00001de6 00000000 00000000 00000000
fffff8a0`01ae33f0  00000018 00000000 00000000 00000000
fffff8a0`01ae3400  00000000 00000000 00000000 00000000
fffff8a0`01ae3410  00000000 00000000 00000000 00000000
fffff8a0`01ae3420  00000000 00000000 00000000 00000000
fffff8a0`01ae3430  00000000 00000000 00000000 00000000
fffff8a0`01ae3440  00000000 00000000 00000200 00000000
fffff8a0`01ae3450  00000001 00000000 00000000 00000000

A teraz kilka ‘faktów’, sprawdzonych przeze mnie eksperymentalnie.

  • ten sam zestaw SIDów dla dwóch różnych procesów tego samego usera prowadzi do tego samego SidHash;
  • drobna różnica w tablicy SIDów (np. proces usera o RIDzie 1001 zamiast 1000, reszta taka sama) prowadzi do niewielkiej różnicy w SidHash i widoczna jest ‘gołym’ okiem;
  • zmiana grupy (np. z Users na Admins (545->544) prowadzi do niewielkiej zmiany w SidHash, również widocznej ‘gołym okiem’;

    i najważniejsze
  • ten sam zestaw tablicy SIDów dla procesów z dwóch różnych maszyn, niezależnie od przynależności do domeny prowadzi do tego samego SidHash.

Coś mi mówi, że wykonując odpowiednio dużo testów można odgadnąć ‘algorytm’ wyliczania SidHash. W tym momencie zmartwię wszystkich oczekujących na fajerwerki: nie, nie podjąłem się tego zadania, ale wygląda na wykonalne w skończonym czasie :)
Znajomość tych kilku faktów stanowi podstawę metody opisywanej w ‘literaturze’ jako 'kradzież znacznika', ale ze względu na pewne ograniczenia może być zastąpiona metodą która:

  1. skopiuje liczbę ‘kradzionych’ elementów _SID_AND_ATTRIBUTES do pól UserAndGroups struktury Token oraz SidCount struktury SidHash;
  2. skopiuje zawartość (nie adres!) ‘kradzionego’ zestawu SIDów w miejsce istniejących, czyli w miejsce wskazywane przez pole UserAndGroups (stąd wyliczenia rozmiaru pamięci zajmowanego przez SIDy użytkownika i grup, które tak skrzętnie wcześniej wyznaczaliśmy!);
  3. skopiuje zawartość tablicy Hash struktury SidHash ‘kradzionego’ znacznika.

A oto przykład do wzięcia: (zaczerpnięte z procesu System o ID=4, ale tak naprawdę to może być dowolny inny proces):

A. Liczba SIDów:
2: kd> dt nt!_token UserAndGroupCount fffff8a000004c50
   +0x078 UserAndGroupCount : 5

B. Lista SIDów:
2: kd> !token fffff8a000004c50
User: S-1-5-18
Groups:
 00 S-1-5-32-544
    Attributes - Default Enabled Owner
 01 S-1-1-0
    Attributes - Mandatory Default Enabled
 02 S-1-5-11
    Attributes - Mandatory Default Enabled
 03 S-1-16-16384
    Attributes - GroupIntegrity GroupIntegrityEnabled
2: kd> dd 0xfffff8a0`00004fa8 l10
fffff8a0`00004fa8  00000101 05000000 00000012 00000201
fffff8a0`00004fb8  05000000 00000020 00000220 00000101
fffff8a0`00004fc8  01000000 00000000 00000101 05000000
fffff8a0`00004fd8  0000000b 00000101 10000000 00004000

C. SidHash
2: kd> dt -b -v nt!_token SidHash. fffff8a000004c50
struct _TOKEN, 33 elements, 0x310 bytes
   +0x0e0 SidHash  : struct _SID_AND_ATTRIBUTES_HASH, 3 elements, 0x110 bytes
      +0x000 SidCount : 5
      +0x008 SidAttr  : 0xfffff8a0`00004f58
      +0x010 Hash     : (32 elements)
      [00] 0x16
       [01] 0
       [02] 1
       [03] 0
       [04] 0
       [05] 0
       [06] 0
       [07] 0
       [08] 0
       [09] 0
       [10] 0
       [11] 8
       [12] 0
       [13] 0
       [14] 0
       [15] 0
       [16] 0x1c
       [17] 1
       [18] 2
       [19] 0
       [20] 0
       [21] 0
       [22] 0
       [23] 0
       [24] 0
       [25] 0
       [26] 0
       [27] 0
       [28] 0
       [29] 0
       [30] 0
       [31] 0

czyli

2: kd> dd fffff8a000004c50+0x0e0+0x010 l40
fffff8a0`00004d40  00000016 00000000 00000000 00000000
fffff8a0`00004d50  00000001 00000000 00000000 00000000
fffff8a0`00004d60  00000000 00000000 00000000 00000000
fffff8a0`00004d70  00000000 00000000 00000000 00000000
fffff8a0`00004d80  00000000 00000000 00000000 00000000
fffff8a0`00004d90  00000000 00000000 00000008 00000000
fffff8a0`00004da0  00000000 00000000 00000000 00000000
fffff8a0`00004db0  00000000 00000000 00000000 00000000
fffff8a0`00004dc0  0000001c 00000000 00000001 00000000
fffff8a0`00004dd0  00000002 00000000 00000000 00000000
fffff8a0`00004de0  00000000 00000000 00000000 00000000
fffff8a0`00004df0  00000000 00000000 00000000 00000000
fffff8a0`00004e00  00000000 00000000 00000000 00000000
fffff8a0`00004e10  00000000 00000000 00000000 00000000
fffff8a0`00004e20  00000000 00000000 00000000 00000000
fffff8a0`00004e30  00000000 00000000 00000000 00000000

Tablica Hash zmieści się na pewno - jest takiego samego rozmiaru we wszystkich systemach. Pozostaje pytanie o tablicę SIDów. Szczerze -  nie spotkałem się z procesem użytkownika, którego tablica SIDów nie mieściłaby się w 64 bajtach. Jeśli znajdziecie taki - dajcie znać :) A póki co - miłej zabawy w ‘przemienianiu’ procesów użytkownika w systemowe ;)

Opublikowane 22 czerwca 2011 19:46 przez mgrzeg
Filed under: , , ,

Powiadamianie o komentarzach

Jeżeli chciałbyś otrzymywać email gdy ta wypowiedź zostanie zaktualizowana, to zarejestruj się tutaj

Subskrybuj komentarze za pomocą RSS

Komentarze:

Brak komentarzy

Co o tym myślisz?

(wymagane) 
(opcjonalne)
(wymagane) 

  
Wprowadź kod: (wymagane)
Wyślij

Subskrypcje

W oparciu o Community Server (Personal Edition), Telligent Systems