Wednesday, September 19, 2012

MoVP 2.3 Event Logs and Service SIDs

Month of Volatility Plugins

In this post we will discuss how you can recover event logs from Windows XP/2003 machines from memory as well as how to calculate Service SIDs which can potentially be used to link specific event records with the windows service that generated them and are also found in other evidence sources such as process and file permissions.


Event logs are a staple in forensic investigations, because they can provide a wealth of information.  They are meant as a troubleshooting mechanism for sysadmins, but can also help determine what happened when incidents occur.  Event logs can include information from misbehaved application to unauthorized access to a machine.  The events contain timestamps that can help one establish a timeline of the incident. 

Event logs are often collected from disk when a machine is investigated, but sometimes the only piece of evidence an analyst is given is a memory dump.  Event logs can be recovered from memory with some caveats in mind.  In this blogpost we will only discuss how to obtain and parse Windows XP/2003 (evt) binary event logs from memory.
Finding Event Logs

Event logs are handled by the services.exe process and are mapped in the process' address space.  If you run the vadinfo command on a Windows XP/2003 machine you can see mapped files in the output, some of which will be event logs:

$ ./ -f XPSP3.vmem --profile=WinXPSP3x86 pslist | grep services
Volatile Systems Volatility Framework 2.2_alpha
0x81d97020 services.exe            692    648     16      352      0      0 2010-12-27 21:34:32

$ ./ -f XPSP3.vmem --profile=WinXPSP3x86 vadinfo -p 692 | less
VAD node @ 0x8230af40 Start 0x009c0000 End 0x009cffff Tag Vad
Flags: Protection: 4
ControlArea @82040f50 Segment e16ad7d8
Dereference list: Flink 00000000, Blink 00000000
NumberOfSectionReferences:          1 NumberOfPfnReferences:           1
NumberOfMappedViews:                1 NumberOfUserReferences:          2
WaitingForDeletion Event:  00000000
Control Flags: Accessed: 1, File: 1, HadUserReference: 1
FileObject @82040ed8, Name: \WINDOWS\system32\config\SecEvent.Evt
First prototype PTE: e17481b8 Last contiguous PTE: e1748230

VAD node @ 0x81d8fd60 Start 0x009b0000 End 0x009bffff Tag Vad
Flags: Protection: 4
ControlArea @822fe348 Segment e16ad8c0
Dereference list: Flink 00000000, Blink 00000000
NumberOfSectionReferences:          2 NumberOfPfnReferences:           2
NumberOfMappedViews:                2 NumberOfUserReferences:          2
WaitingForDeletion Event:  00000000
Control Flags: Accessed: 1, File: 1, HadUserReference: 1, WasPurged: 1
FileObject @822fe2d0, Name: \WINDOWS\system32\config\AppEvent.Evt
First prototype PTE: e173f5d8 Last contiguous PTE: e173f650


Now that we know where these files reside, let's see about getting them out of memory.  One method is using vaddump to extract the VAD sections that contain the event logs and then parsing them with an external tool.  Another method is writing our own plugin to extract and parse the event logs on the fly, which can be more efficient and at the same time eliminate dependencies on external tools. However, we can still make dumping the raw logs optional.

Getting the data is simple, the idea is to find the services.exe process and then find the event logs and extract their data.  Here's the code (taken from the calculate function of the evtlogs plugin):

[1] for proc in tasks.pslist(addr_space):
[2]    if str(proc.ImageFileName).lower() == "services.exe":
[3]        for vad, process_space in proc.get_vads(vad_filter = proc._mapped_file_filter):
[4]            if vad.FileObject.FileName:

[5]                name = str(vad.FileObject.FileName).lower()
[6]                if name.endswith(".evt"):
[7]                    data = process_space.zread(vad.Start, vad.Length)
[8]                    yield name, data

At line 1 and 2, we traverse the list of active processes using Volatility's process enumeration API and skip anything not named services.exe. At line 3, we traverse the VADs using a generic filter that only finds mapped files. At lines 4, 5, and 6 we ensure the VAD's ControlArea and FileObject pointers are valid and that the FileName ends with ".evt" - the extension for Windows XP/2003 event logs. At line 7, we acquire the entire length of the VAD segment from process memory - using the zread function which pads any unavailable pages (i.e. not memory resident due to paging) with zeros to retain the original size.

Once we have the data, we can parse the event log.

Event Log Structures

In order to parse the event logs we need to know what structures we need to make sense of the binary data.  The structures for the event log file were taken from Harlan Carvey's WFA 2nd Edition and can be seen below:

>>> dt("EVTLogHeader")
'EVTLogHeader' (48 bytes)
0x0   : HeaderSize                     ['int']
0x4   : Magic                          ['int'] 
0x10  : OffsetOldest                   ['int']
0x14  : OffsetNextToWrite              ['int']
0x18  : NextID                         ['int']
0x1c  : OldestID                       ['int']
0x20  : MaxSize                        ['int']
0x28  : RetentionTime                  ['int']
0x2c  : RecordSize                     ['int']

>>> dt("EVTRecordStruct")
'EVTRecordStruct' (56 bytes)
0x0   : RecordLength                   ['int']
0x4   : Magic                          ['int']
0x8   : RecordNumber                   ['int']
0xc   : TimeGenerated                  ['unsigned int']
0x10  : TimeWritten                    ['unsigned int']
0x14  : EventID                        ['unsigned short']
0x18  : EventType                      ['Enumeration', {'target': 'unsigned short', 'choices': {8: 'Success', 1: 'Error', 2: 'Warning', 4: 'Info', 16: 'Failure'}}]
0x1a  : NumStrings                     ['unsigned short']
0x1c  : EventCategory                  ['unsigned short']
0x1e  : ReservedFlags                  ['unsigned short']
0x20  : ClosingRecordNum               ['int']
0x24  : StringOffset                   ['int']
0x28  : SidLength                      ['int']
0x2c  : SidOffset                      ['int']
0x30  : DataLength                     ['int']
0x34  : DataOffset                     ['int']

Here are some notes about the structure members:
  • Both Magic values are LfLe which provides a good mark to scan for in memory when looking for log headers and records. 
  • TimeGenerated and TimeWritten are timestamps for the time the log entry was generated and the time the log entry was written to the log respectively.
  • EventID is specific to the event source and uniquely identifies the event 
  • EventType describes the type, or level, of event (i.e. error, info, etc)
  • NumStrings is the number of description strings, starting at StringOffset in the event message  
  • SidLength is 0 if no SID is present in the record, otherwise it is the length of the SID which starts at SidOffset. The SID can help link the event back to the service or user that created it. 
  • DataLength is the number of bytes found at DataOffset that contain the event data. 
An example EVTLogHeader can be seen below:

0000000: 3000 0000 4c66 4c65 0100 0000 0100 0000  0...LfLe........
0000010: 3000 0000 38a7 0400 c203 0000 0100 0000  0...8...........
0000020: 60a7 0400 0000 0000 803a 0900 3000 0000  `........:..0...

HeaderSize 0x30 (48) bytes
Magic (LfLe)
OffsetOldest (0x30)
OffsetNextToWrite (0x7a38)
NextID (0x3c2)

An example EVTRecordStruct can be seen below:

0000030: d400 0000 4c66 4c65 0100 0000 de03 ff4f  ....LfLe.......O
0000040: de03 ff4f 0202 0000 0800 0100 0100 0000  ...O............
0000050: 0000 0000 7600 0000 0c00 0000 6a00 0000  ....v.......j...
0000060: 0000 0000 cc00 0000 5300 6500 6300 7500  ........S.e.c.u.
0000070: 7200 6900 7400 7900 0000 5500 5300 4500  r.i.t.y...U.S.E.
0000080: 5200 2d00 3600 3700 3100 3300 4200 4500  R.-.
0000090: 3300 4100 4600 4500 0000 0101 0000 0000  3.A.F.E.........

RecordLength (0xd4)
Magic (LfLe)
RecordNumber (0x1)
TimeGenerated (UNIX time) (Thu Jul 12 17:05:34 2012)
TimeWritten (UNIX time) (Thu Jul 12 17:05:34 2012)
EventID (0x202)
EventType (0x8) (Success)
NumStrings (0x1)

Security Identifiers (SIDs)

We are also able to make use of other existing structures in the framework, such as SIDs.  There are different types of SIDs:
  • Known SIDs (already defined in Volatility)
  • User SIDs
  • Service SIDs
User SIDs can be found at the following registry key/value:
  • HLKM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\<SID> => ProfileImagePath
Service SIDs have been in use since at least Windows XP Service Pack 3 and Windows 2003 Service Pack 2. They have a S-1-5-80- prefix with the rest of the SID being composed of the SHA1 of the (Unicode uppercase) service name and then converted to decimal.  A list of services can be obtained from HKLM\SYSTEM\Services out of the cached registry in memory.  Once we have this list, we can calculate the Service SIDs using the following function, which is included as a new helper plugin called getservicesids:

def createservicesid(svc):
    """ Calculate the Service SID """
    uni = ''.join([c + '\x00' for c in svc])
    sha = hashlib.sha1(uni.upper()).digest()
    dec = list()
    for i in range(5):
        dec.append(struct.unpack('<I', sha[i * 4 : i * 4 + 4])[0])
    return 'S-1-5-80-' + '-'.join([str(n) for n in dec])

Here's an example of a service SID as output by the getsids plugin:

$ ./ -f XPSP3.vmem --profile=WinXPSP3x86 getsids | less
svchost.exe (1524): S-1-5-19 (NT Authority)
svchost.exe (1524): S-1-5-19 (NT Authority)
svchost.exe (1524): S-1-1-0 (Everyone)
svchost.exe (1524): S-1-5-32-545 (Users)
svchost.exe (1524): S-1-5-6 (Service)
svchost.exe (1524): S-1-5-11 (Authenticated Users)
svchost.exe (1524): S-1-5-80-324959683-3395802011-921526492-919036580-1730255754
svchost.exe (1524): S-1-5-5-0-65853 (Logon Session)
svchost.exe (1524): S-1-2-0 (Local (Users with the ability to log in locally))
svchost.exe (1524): S-1-1-0 (Everyone)
svchost.exe (1524): S-1-5-11 (Authenticated Users)
svchost.exe (1524): S-1-2-0 (Local (Users with the ability to log in locally))
svchost.exe (1524): S-1-5-32-545 (Users)

The default output of getservicesids produces a Python dictionary that contains the service name and the pre-calculated service SIDs.  If we look at this dictionary we can see that the SID S-1-5-80-324959683-3395802011-921526492-919036580-1730255754 corresponds to "WebClient".

Service SIDs can also appear in file permissions, for example looking at SIDs associated with event logs on a Windows 7 machine using EnCase we can see the Service SID of the "eventlog" service:

Therefore the getservicesids plugin can be useful for investigating Service SIDs from various pieces of evidence in memory as well as disk.

SIDs in Event Logs

If the SidLength member of a EVTRecordStruct object is greater than zero, the SidOffset member points to the location of the SID from within the record itself.  If a SID is found during the parsing process of an event log record, its value is compared against all known SIDs, user SIDs and Service SIDs.  There is a dictionary of known Service SIDs contained in getservicesids that is queried, unless the user specifies the -v/--verbose option, at which point the Service SIDs are calculated on the fly from services found in the registry.

Output of evtlogs Plugin

Event logs are parsed in the following format:

Time | Type | Event Log File Name | Computer Name | SID | Source | Event ID | Event Type | Message (semicolon separated)

In addition to parsed output, you can also dump the event logs in their raw binary form by using the --save-evt option. 

Example Run

Here we can see an example run of  evtlogs against a public sample: cve2011_0611.dmp.  The memory sample was collected after a Word document with a Flash 0day (CVE-2011-0611) was opened on the machine.  

$ ./ -f cve2011_0611.dmp --profile=WinXPSP3x86 evtlogs -v --save-evt -D output/
Volatile Systems Volatility Framework 2.2_alpha
Saved raw .evt file to osession.evt
Parsed data sent to osession.txt
Saved raw .evt file to internet.evt
Parsed data sent to internet.txt
Saved raw .evt file to appevent.evt
Parsed data sent to appevent.txt
Saved raw .evt file to odiag.evt
Parsed data sent to odiag.txt
Saved raw .evt file to sysevent.evt
Parsed data sent to sysevent.txt
Saved raw .evt file to secevent.evt
Parsed data sent to secevent.txt

Some of the event logs are empty or uninteresting without any new entries past 2010 which is months before the sample was created.  After looking through the parsed files a bit, we see that there are interesting entries in osession.evt which is associated with Microsoft Word.  Since the exploit was embedded into a word document, this might be important for our investigation.  Looking at its contents, notice the last entry we see:

$ cat osession.txt
2011-04-10 09:14:22|osession.evt|FINANCE1|N/A|Microsoft Office 12 Sessions|7000|Info|0;Microsoft Office Word;12.0.4518.1014;12.0.4518.1014;1368;0
2011-04-10 09:33:40|osession.evt|FINANCE1|N/A|Microsoft Office 12 Sessions|7000|Info|0;Microsoft Office Word;12.0.6425.1000;12.0.6425.1000;1086;60
2011-04-10 09:36:39|osession.evt|FINANCE1|N/A|Microsoft Office 12 Sessions|7000|Info|0;Microsoft Office Word;12.0.6545.5000;12.0.6425.1000;120;0
2011-04-10 21:43:54|osession.evt|FINANCE1|N/A|Microsoft Office 12 Sessions|7000|Info|0;Microsoft Office Word;12.0.6545.5000;12.0.6425.1000;110;0
2011-04-10 22:29:44|osession.evt|FINANCE1|N/A|Microsoft Office 12 Sessions|7000|Info|0;Microsoft Office Word;12.0.6545.5000;12.0.6425.1000;80;60
2011-04-10 22:30:18|osession.evt|FINANCE1|N/A|Microsoft Office 12 Sessions|7003|Warning|0;Microsoft Office Word;12.0.6545.5000;12.0.6425.1000

Since there is a code of 7003, this verifies that the last word session terminated unexpectedly.  This provides us a possible clue as to how the machine may have been compromised and when used in conjunction with other data sources in a timeline, the picture becomes even clearer, but that is outside the scope of this blogpost.

The Security Event Log

If security events are logged on a machine, logon and logoff events can be recovered as well.  To figure out if a machine is configured for logging security events and if so, which of these events to expect, the following registry key must be queried: HKLM\SECURITY\Policy\PolAdtEv:

$ ./ -f WinXPSP2x64.vmem --profile=WinXPSP2x64 printkey -K "Policy\PolAdtEv"
Volatile Systems Volatility Framework 2.2_alpha
Legend: (S) = Stable   (V) = Volatile

Registry: \Device\HarddiskVolume1\WINDOWS\system32\config\SECURITY
Key name: PolAdtEv (S)
Last updated: 2012-01-17 12:01:27


REG_NONE                      : (S)
0x00000000  01 aa 0c 00 00 00 00 00 01 00 00 00 00 00 00 00   ................
0x00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x00000020  00 00 00 00 01 00 00 00 09 00 00 00               ............

For this machine we can see that the policy is enabled, but we don't know which events are being logged without deciphering the binary data.  A description can be seen below in a graphic generated from this Microsoft KB article: as well as from Harlan Carvey's RegRipper plugin auditpol:

So we can see that auditing is enabled, logons and logoffs success events are recorded as well as account successful account logon events.  If we look at the secevent.evt log we can see that there are entries for these types of events:

2012-01-17 17:06:18|secevent.evt|MACHINENAME|S-1-5-19 (NT Authority)|Security|528|Success|LOCAL SERVICE;NT AUTHORITY;(0x0,0x3E5);5;Advapi  ;Negotiate;;-;MACHINENAME$;;(0x0,0x3E7);252;-;-;-
2012-01-17 17:06:18|secevent.evt|MACHINENAME|S-1-5-19 (NT Authority)|Security|576|Success|LOCAL SERVICE;NT AUTHORITY;(0x0,0x3E5);SeAuditPrivilege                       SeAssignPrimaryTokenPrivilege                   SeImpersonatePrivilege
2012-01-17 17:06:19|secevent.evt|MACHINENAME|S-1-5-20 (NT Authority)|Security|528|Success|NETWORK SERVICE;NT AUTHORITY;(0x0,0x3E4);5;Advapi  ;Negotiate;;-;MACHINENAME$;;(0x0,0x3E7);252;-;-;-
2012-01-17 17:12:44|secevent.evt|USER-FZV1NPLBJK|S-1-5-21-2853598632-914166033-100590561-500 (Administrator)|Security|576|Success|Administrator;USER-FZV1NPLBJK;(0x0,0x204D3);SeSecurityPrivilege                       SeBackupPrivilege                       SeRestorePrivilege                      SeTakeOwnershipPrivilege

2012-01-17 17:22:34|secevent.evt|USER-FZV1NPLBJK|S-1-5-7 (Anonymous)|Security|540|Success|;;(0x0,0x13F7C);3;NtLmSsp ;NTLM;;-;-;-;-;-;-;-;-

2012-01-17 17:23:31|secevent.evt|USER-FZV1NPLBJK|S-1-5-21-2853598632-914166033-100590561-500 (Administrator)|Security|680|Success|MICROSOFT_AUTHENTICATION_PACKAGE_V1_0;Administrator;USER-FZV1NPLBJK;0x0


Here we have seen that the evtlogs plugin can be useful in memory forensic investigations especially when the disk evidence is inaccessible.  There are, however, things that should be kept in mind when doing this type of analysis:
  • The event log may or may not be entirely in memory.  You may get partial, missing or overwritten records.
  • If the event log is deleted using ClearEventLog then they are removed from memory as well as from disk.  When this occurs the event log will contain a header, but no entries initially and new entries start with an ID of 1.  The header will show that the event log has been wiped since NextID will be 0x1 and OldestID will be 0x0.  This can be an indication that something is "awry" on its own, since an event log that is empty or sparse may be suspect on a machine that has been running for sometime.  In order to investigate the header, use the --save-evt option to dump the raw event logs.
  • The evtlogs plugin does not handle log "wrap around" or cases when event logs overwrite themselves.  Instead users are given an option to dump raw event logs in order to parse with other tools.
  • The evtlogs plugin does not gather description strings from the designated DLL in the registry, instead these message strings are semicolon separated in the output. 
  • An empty event log does not necessarily mean that it has been wiped.  Security events are not always logged by default and the settings should be checked by confirming with the registry.

No comments:

Post a Comment