Skip to content

Commit

Permalink
[receiver/windowseventlog] Add Execution and Security information to …
Browse files Browse the repository at this point in the history
…parsed event log (open-telemetry#27864)

**Description:**
Adds parsing for Execution and Security sections of the event log, as
defined in the schema here:
https://learn.microsoft.com/en-us/windows/win32/wes/eventschema-systempropertiestype-complextype

**Link to tracking Issue:** open-telemetry#27810

**Testing:**
* Added some unit tests
* Tested on a windows machine to make sure it parsed correctly on a real
system

---------

Co-authored-by: Paulo Janotti <pjanotti@splunk.com>
  • Loading branch information
2 people authored and sigilioso committed Oct 27, 2023
1 parent b665836 commit 31f98e6
Show file tree
Hide file tree
Showing 4 changed files with 309 additions and 2 deletions.
22 changes: 22 additions & 0 deletions .chloggen/feat_windows-evlog_security-and-execution.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: windowseventlogreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add parsing for Security and Execution event fields.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [27810]

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: ["user"]
28 changes: 28 additions & 0 deletions pkg/stanza/operator/input/windows/testdata/xmlSampleUserData.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Name="Microsoft-Windows-Eventlog" Guid="{fc65ddd8-d6ef-4962-83d5-6e5cfe9ce148}" />
<EventID>1102</EventID>
<Version>1</Version>
<Level>4</Level>
<Task>104</Task>
<Opcode>0</Opcode>
<Keywords>0x4020000000000000</Keywords>
<TimeCreated SystemTime="2023-10-12T10:38:24.543506200Z" />
<EventRecordID>2590526</EventRecordID>
<Correlation />
<Execution ProcessID="1472" ThreadID="7784" />
<Channel>Security</Channel>
<Computer>test.example.com</Computer>
<Security UserID="S-1-5-18" />
</System>
<UserData>
<LogFileCleared xmlns="http://manifests.microsoft.com/win/2004/08/windows/eventlog">
<SubjectUserSid>S-1-5-21-1148437859-4135665037-1195073887-1000</SubjectUserSid>
<SubjectUserName>test_user</SubjectUserName>
<SubjectDomainName>TEST</SubjectDomainName>
<SubjectLogonId>0xa8bb72</SubjectLogonId>
<ClientProcessId>4536</ClientProcessId>
<ClientProcessStartKey>17732923532772643</ClientProcessStartKey>
</LogFileCleared>
</UserData>
</Event>
61 changes: 61 additions & 0 deletions pkg/stanza/operator/input/windows/xml.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ type EventXML struct {
Opcode string `xml:"System>Opcode"`
RenderedKeywords []string `xml:"RenderingInfo>Keywords>Keyword"`
Keywords []string `xml:"System>Keywords"`
Security *Security `xml:"System>Security"`
Execution *Execution `xml:"System>Execution"`
EventData []EventDataEntry `xml:"EventData>Data"`
}

Expand Down Expand Up @@ -118,9 +120,21 @@ func (e *EventXML) parseBody() map[string]interface{} {
"keywords": keywords,
"event_data": parseEventData(e.EventData),
}

if len(details) > 0 {
body["details"] = details
}

if e.Security != nil && e.Security.UserID != "" {
body["security"] = map[string]any{
"user_id": e.Security.UserID,
}
}

if e.Execution != nil {
body["execution"] = e.Execution.asMap()
}

return body
}

Expand Down Expand Up @@ -181,3 +195,50 @@ type EventDataEntry struct {
Name string `xml:"Name,attr"`
Value string `xml:",chardata"`
}

// Security contains info pertaining to the user triggering the event.
type Security struct {
UserID string `xml:"UserID,attr"`
}

// Execution contains info pertaining to the process that triggered the event.
type Execution struct {
// ProcessID and ThreadID are required on execution info
ProcessID uint `xml:"ProcessID,attr"`
ThreadID uint `xml:"ThreadID,attr"`
// These remaining fields are all optional for execution info
ProcessorID *uint `xml:"ProcessorID,attr"`
SessionID *uint `xml:"SessionID,attr"`
KernelTime *uint `xml:"KernelTime,attr"`
UserTime *uint `xml:"UserTime,attr"`
ProcessorTime *uint `xml:"ProcessorTime,attr"`
}

func (e Execution) asMap() map[string]any {
result := map[string]any{
"process_id": e.ProcessID,
"thread_id": e.ThreadID,
}

if e.ProcessorID != nil {
result["processor_id"] = *e.ProcessorID
}

if e.SessionID != nil {
result["session_id"] = *e.SessionID
}

if e.KernelTime != nil {
result["kernel_time"] = *e.KernelTime
}

if e.UserTime != nil {
result["user_time"] = *e.UserTime
}

if e.ProcessorTime != nil {
result["processor_time"] = *e.ProcessorTime
}

return result
}
200 changes: 198 additions & 2 deletions pkg/stanza/operator/input/windows/xml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,162 @@ func TestParseBody(t *testing.T) {
require.Equal(t, expected, xml.parseBody())
}

func TestParseBodySecurityExecution(t *testing.T) {
xml := EventXML{
EventID: EventID{
ID: 1,
Qualifiers: 2,
},
Provider: Provider{
Name: "provider",
GUID: "guid",
EventSourceName: "event source",
},
TimeCreated: TimeCreated{
SystemTime: "2020-07-30T01:01:01.123456789Z",
},
Computer: "computer",
Channel: "application",
RecordID: 1,
Level: "Information",
Message: "message",
Task: "task",
Opcode: "opcode",
Keywords: []string{"keyword"},
EventData: []EventDataEntry{
{Name: "name", Value: "value"}, {Name: "another_name", Value: "another_value"},
},
Execution: &Execution{
ProcessID: 13,
ThreadID: 102,
},
Security: &Security{
UserID: "my-user-id",
},
RenderedLevel: "rendered_level",
RenderedTask: "rendered_task",
RenderedOpcode: "rendered_opcode",
RenderedKeywords: []string{"RenderedKeywords"},
}

expected := map[string]interface{}{
"event_id": map[string]interface{}{
"id": uint32(1),
"qualifiers": uint16(2),
},
"provider": map[string]interface{}{
"name": "provider",
"guid": "guid",
"event_source": "event source",
},
"system_time": "2020-07-30T01:01:01.123456789Z",
"computer": "computer",
"channel": "application",
"record_id": uint64(1),
"level": "rendered_level",
"message": "message",
"task": "rendered_task",
"opcode": "rendered_opcode",
"keywords": []string{"RenderedKeywords"},
"execution": map[string]any{
"process_id": uint(13),
"thread_id": uint(102),
},
"security": map[string]any{
"user_id": "my-user-id",
},
"event_data": map[string]interface{}{"name": "value", "another_name": "another_value"},
}

require.Equal(t, expected, xml.parseBody())
}

func TestParseBodyFullExecution(t *testing.T) {
processorID := uint(3)
sessionID := uint(2)
kernelTime := uint(3)
userTime := uint(100)
processorTime := uint(200)

xml := EventXML{
EventID: EventID{
ID: 1,
Qualifiers: 2,
},
Provider: Provider{
Name: "provider",
GUID: "guid",
EventSourceName: "event source",
},
TimeCreated: TimeCreated{
SystemTime: "2020-07-30T01:01:01.123456789Z",
},
Computer: "computer",
Channel: "application",
RecordID: 1,
Level: "Information",
Message: "message",
Task: "task",
Opcode: "opcode",
Keywords: []string{"keyword"},
EventData: []EventDataEntry{
{Name: "name", Value: "value"}, {Name: "another_name", Value: "another_value"},
},
Execution: &Execution{
ProcessID: 13,
ThreadID: 102,
ProcessorID: &processorID,
SessionID: &sessionID,
KernelTime: &kernelTime,
UserTime: &userTime,
ProcessorTime: &processorTime,
},
Security: &Security{
UserID: "my-user-id",
},
RenderedLevel: "rendered_level",
RenderedTask: "rendered_task",
RenderedOpcode: "rendered_opcode",
RenderedKeywords: []string{"RenderedKeywords"},
}

expected := map[string]interface{}{
"event_id": map[string]interface{}{
"id": uint32(1),
"qualifiers": uint16(2),
},
"provider": map[string]interface{}{
"name": "provider",
"guid": "guid",
"event_source": "event source",
},
"system_time": "2020-07-30T01:01:01.123456789Z",
"computer": "computer",
"channel": "application",
"record_id": uint64(1),
"level": "rendered_level",
"message": "message",
"task": "rendered_task",
"opcode": "rendered_opcode",
"keywords": []string{"RenderedKeywords"},
"execution": map[string]any{
"process_id": uint(13),
"thread_id": uint(102),
"processor_id": processorID,
"session_id": sessionID,
"kernel_time": kernelTime,
"user_time": userTime,
"processor_time": processorTime,
},
"security": map[string]any{
"user_id": "my-user-id",
},
"event_data": map[string]interface{}{"name": "value", "another_name": "another_value"},
}

require.Equal(t, expected, xml.parseBody())
}

func TestParseNoRendered(t *testing.T) {
xml := EventXML{
EventID: EventID{
Expand Down Expand Up @@ -252,7 +408,7 @@ func TestInvalidUnmarshal(t *testing.T) {
require.Error(t, err)

}
func TestUnmarshal(t *testing.T) {
func TestUnmarshalWithEventData(t *testing.T) {
data, err := os.ReadFile(filepath.Join("testdata", "xmlSample.xml"))
require.NoError(t, err)

Expand Down Expand Up @@ -283,7 +439,47 @@ func TestUnmarshal(t *testing.T) {
{Name: "Time", Value: "2022-04-28T19:48:52Z"},
{Name: "Source", Value: "RulesEngine"},
},
Keywords: []string{"0x80000000000000"},
Keywords: []string{"0x80000000000000"},
Security: &Security{},
Execution: &Execution{},
}

require.Equal(t, xml, event)
}

func TestUnmarshalWithUserData(t *testing.T) {
data, err := os.ReadFile(filepath.Join("testdata", "xmlSampleUserData.xml"))
require.NoError(t, err)

event, err := unmarshalEventXML(data)
require.NoError(t, err)

xml := EventXML{
EventID: EventID{
ID: 1102,
},
Provider: Provider{
Name: "Microsoft-Windows-Eventlog",
GUID: "{fc65ddd8-d6ef-4962-83d5-6e5cfe9ce148}",
},
TimeCreated: TimeCreated{
SystemTime: "2023-10-12T10:38:24.543506200Z",
},
Computer: "test.example.com",
Channel: "Security",
RecordID: 2590526,
Level: "4",
Message: "",
Task: "104",
Opcode: "0",
Keywords: []string{"0x4020000000000000"},
Security: &Security{
UserID: "S-1-5-18",
},
Execution: &Execution{
ProcessID: 1472,
ThreadID: 7784,
},
}

require.Equal(t, xml, event)
Expand Down

0 comments on commit 31f98e6

Please sign in to comment.