Thursday, August 31, 2006

Getting user info from an image

A while ago, I posted on how to determine a user's group membership from the Registry files in an image. Andreas Schuster also posted a glorious bit of eye candy, as well.

Sometimes, it pays to revisit or clarify things.

Windows 2000 and XP maintain user and group information in the SAM portion of the Registry, which is in the SAM file on disk. This file is a Registry file and can be parsed using tools such as the Offline Registry Parser. However, the information you would look for is encoded in binary Registry values and needs to be parsed into something understandable. The best source of information for this is the sam.h file included in the source code for Peter Nordahl's chntpw tool. This tool is Linux-based, so it doesn't use the Windows API. Also, it's written in C, which means that the structures it uses are easily translated into other languages, such as (you guessed it) Perl.

For information about users, go to the key SAM\Domains\Account\Users\, where you'll see subkeys that look like "000001F4" and "000003EA". Translate these from hex to decimal (1F4 = 500, 3EA = 1002) and you'll see that you're looking at user RIDs. Within each of these keys, you'll find F and V values, both of which are binary. The F value contains the type of account and various account settings, and the V value contains the user name, full name, comment, password hashes, etc.

The structures I use in my ProScript look like this:

V structure:
# $v is the binary contents of the V value
my $header = substr($v,0,44);
my @vals = unpack("V*",$header);
my $name = _uniToAscii(substr($v,($vals[3] + 0xCC),$vals[4]));
my $fullname = _uniToAscii(substr($v,($vals[6] + 0xCC),$vals[7])) if ($vals[7] > 0);
my $comment = _uniToAscii(substr($v,($vals[9] + 0xCC),$vals[10])) if ($vals[10] > 0);

F structure:
# unpack() string is a little messy, but used this way for clarity
# $f is the binary data of the F value
my @vals = unpack("x8V2x8V2x8V2Vx4vx6vvx12",$f);
# Note: Times maintained by Windows are 64-bit FILETIME objects
#$vals[0] and $vals[1] = lockout time
#$vals[2] and $vals[3] = creation time
#$vals[4] and $vals[5] = login time
my $rid = $vals[6];
my $acb = $vals[7];
my $failedcnt = $vals[8];
my $logins = $vals[9];

The $acb value holds the ACB flags settings, which tells us things like if the account is locked out, etc.

In order to get the group membership, you would need to go to the SAM\Domains\Builtin\Aliases key, where you would find subkeys similar to the ones above, only these are the group RIDs. Each of these subkeys contains a C value, which is also a binary data type. The code that I use to parse this structure looks like (it's messy, I know):

# $keyInfo->{strValueData} is the binary contents of the C value
my @users = ();
my $header = substr($keyInfo->{strValueData},0,0x34);
my @vals = unpack("V*",$header);
my $grpname = _uniToAscii(substr($keyInfo->{strValueData},(0x34 + $vals[4]),$vals[5]));
my $comment = _uniToAscii(substr($keyInfo->{strValueData},(0x34 + $vals[7]),$vals[8]));
my $num = $vals[12];

At this point, $num is the number of group members, and all that's left at this point is to parse through the rest of the data, beginning at the offset located in $vals[10], collecting the SIDs of the users. Again, Andreas's blog entry has some nice graphics to go along with it.

There's one other place to look for user account settings on the system. Locate the F value under the SAM\Domains\Account key...this is where the info is kept. Peter's sam.h file contains the actual structure...I haven't written code yet to parse it yet.

I hope that helps some folks out there. I should probably write a version of the Offline Registry Parser that just dumps this info out of the SAM file.

1 comment:

JimmyW said...

Thanks again, Harlan, This is just what I need for a reference!