Linking Processes to Users

In the course of an investigation, it may be critical to be able to link up a process that's running to a particular user account. Particularly in a multi-user environment such as Windows Terminal Server, this isn't always as easy as checking who was logged in at the time.

Luckily, each process in Windows has an associated token, a chunk of metadata that describes what Security Identifier (SID) owns the process and what privileges have been granted to it. As Larry Osterman explains, A SID is essentially a unique ID that is assigned to a user or group, and is broken into several parts: the revision (currently always set to 1), the identifier authority (describing what authority created the SID, and hence how to interpret the subauthoriries), and finally a list of subauthorities.

In general, when users see SIDs (which they rarely do), they are in what's called the Security Descriptor Definition Language (SDDL) form. This is a string that looks like:
S-1-5-21-1957994488-484763869-854245398-513

Here, "1" is the revision, "5" is the identifier authority, and the remaining portions are the subauthorities. The exact data structure for a SID is:
typedef struct _SID {
BYTE Revision;
BYTE SubAuthorityCount;
SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
DWORD SubAuthority[ANYSIZE_ARRAY];
} SID, *PISID;

The SID_IDENTIFIER_AUTHORITY here is actually an array of 6 characters. However, at the moment only the final character is used. Osterman's article enumerates all the possible identifier authorities; for our purposes we will be focusing on the NT authority, which is {0,0,0,0,0,5}. This is the authority which describes accounts managed by the NT security subsystem.

So how can we get this information from a memory image? We start, as usual, by looking at the _EPROCESS structure. Inside it, we find the Token member at offset 0xc8. However, the member doesn't directly point to an object of type _TOKEN, as we might expect. Instead, it is described as an _EX_FAST_REF. These types of objects are basically an optimization used by Windows to store both a pointer to and object and the object's reference count, all in a single DWORD. In an _EX_FAST_REF, the last 3 bits are co-opted to encode the reference count of the object. To get the actual pointer, you can mask off the last 3 bits, like so:
token_address = proc.Token.Value & ~0x7

Now, on to the token itself. Each token contains a list of user and group SIDs. The relevant members of the _TOKEN structure (for our immediate purpose) are UserAndGroupCount (unsigned long) and UserAndGroups (pointer to array of _SID_AND_ATTRIBUTES), at offsets 0x4c and 0x68, respectively. _SID_AND_ATTRIBUES, in turn, contains a pointer to the SID itself and a DWORD of flags giving the SID's attributes (the meaning of which are dependent on the type of SID; for group SIDs, the flags can be found in winnt.h).

Unfortunately, just having the SIDs by themselves may not be so meaningful to you. Actual account names would be better; luckily, these can be found by looking in the registry. The key HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList contains all the local user account SIDs on the machine, along with the location of their profile on disk. The username can usually be inferred from this; e.g. a profile directory of %SystemDrive%\Documents and Settings\Bob would imply that the username is Bob.

Aside from the individual account SIDs, there are also a number of well-known group SIDs. These are SIDs that Microsoft has set aside for specific purposes, and will be the same on any Windows machine. A full list is available in KB243330, "Well-known security identifiers in Windows operating systems".

As a reward for sitting through all that dry description, here's a nice juicy tool that you can use to get started looking at the SIDs associated with a process in a memory image, written as a plugin for the just-released Volatility 1.3. You can download getsids.py here. To use it, just drop it in your plugins directory (memory_plugins) and run:
$ python volatility getsids -f xp-laptop-2005-07-04-1430.img
[...]
dd.exe (3300): S-1-5-21-1957994488-484763869-854245398-1006
dd.exe (3300): S-1-5-21-1957994488-484763869-854245398-513 (Domain Users)
dd.exe (3300): S-1-1-0 (Everyone)
dd.exe (3300): S-1-5-32-544 (Administrators)
dd.exe (3300): S-1-5-32-545 (Users)
dd.exe (3300): S-1-5-4 (Interactive)
dd.exe (3300): S-1-5-11 (Authenticated Users)
dd.exe (3300): S-1-5-5-0-49673 (Logon Session)
dd.exe (3300): S-1-2-0 (Users with the ability to log in locally)

Comments

Gene Belcher said…
You can map a certain SID to a username by querying the following registry location:

"Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\"

Append the SID you're interested as above and then look in the value "ProfileImagePath".

This is what the getsids plugin is doing if you look at the volatility plugin code:

https://github.com/volatilityfoundation/volatility/blob/master/volatility/plugins/getsids.py

Hope that helps

Popular posts from this blog

Someone’s Been Messing With My Subnormals!

Decrypting LSA Secrets

SysKey and the SAM