Skip to content

Commit fbf6266

Browse files
BinaryFissionGamespjanotti
authored andcommitted
[receiver/windowseventlog] Add Execution and Security information to 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 <[email protected]>
1 parent 5e10bfc commit fbf6266

File tree

4 files changed

+309
-2
lines changed

4 files changed

+309
-2
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: windowseventlogreceiver
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add parsing for Security and Execution event fields.
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [27810]
14+
15+
# If your change doesn't affect end users or the exported elements of any package,
16+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
17+
# Optional: The change log or logs in which this entry should be included.
18+
# e.g. '[user]' or '[user, api]'
19+
# Include 'user' if the change is relevant to end users.
20+
# Include 'api' if there is a change to a library API.
21+
# Default: '[user]'
22+
change_logs: ["user"]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
2+
<System>
3+
<Provider Name="Microsoft-Windows-Eventlog" Guid="{fc65ddd8-d6ef-4962-83d5-6e5cfe9ce148}" />
4+
<EventID>1102</EventID>
5+
<Version>1</Version>
6+
<Level>4</Level>
7+
<Task>104</Task>
8+
<Opcode>0</Opcode>
9+
<Keywords>0x4020000000000000</Keywords>
10+
<TimeCreated SystemTime="2023-10-12T10:38:24.543506200Z" />
11+
<EventRecordID>2590526</EventRecordID>
12+
<Correlation />
13+
<Execution ProcessID="1472" ThreadID="7784" />
14+
<Channel>Security</Channel>
15+
<Computer>test.example.com</Computer>
16+
<Security UserID="S-1-5-18" />
17+
</System>
18+
<UserData>
19+
<LogFileCleared xmlns="http://manifests.microsoft.com/win/2004/08/windows/eventlog">
20+
<SubjectUserSid>S-1-5-21-1148437859-4135665037-1195073887-1000</SubjectUserSid>
21+
<SubjectUserName>test_user</SubjectUserName>
22+
<SubjectDomainName>TEST</SubjectDomainName>
23+
<SubjectLogonId>0xa8bb72</SubjectLogonId>
24+
<ClientProcessId>4536</ClientProcessId>
25+
<ClientProcessStartKey>17732923532772643</ClientProcessStartKey>
26+
</LogFileCleared>
27+
</UserData>
28+
</Event>

pkg/stanza/operator/input/windows/xml.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ type EventXML struct {
2828
Opcode string `xml:"System>Opcode"`
2929
RenderedKeywords []string `xml:"RenderingInfo>Keywords>Keyword"`
3030
Keywords []string `xml:"System>Keywords"`
31+
Security *Security `xml:"System>Security"`
32+
Execution *Execution `xml:"System>Execution"`
3133
EventData []EventDataEntry `xml:"EventData>Data"`
3234
}
3335

@@ -118,9 +120,21 @@ func (e *EventXML) parseBody() map[string]interface{} {
118120
"keywords": keywords,
119121
"event_data": parseEventData(e.EventData),
120122
}
123+
121124
if len(details) > 0 {
122125
body["details"] = details
123126
}
127+
128+
if e.Security != nil && e.Security.UserID != "" {
129+
body["security"] = map[string]any{
130+
"user_id": e.Security.UserID,
131+
}
132+
}
133+
134+
if e.Execution != nil {
135+
body["execution"] = e.Execution.asMap()
136+
}
137+
124138
return body
125139
}
126140

@@ -181,3 +195,50 @@ type EventDataEntry struct {
181195
Name string `xml:"Name,attr"`
182196
Value string `xml:",chardata"`
183197
}
198+
199+
// Security contains info pertaining to the user triggering the event.
200+
type Security struct {
201+
UserID string `xml:"UserID,attr"`
202+
}
203+
204+
// Execution contains info pertaining to the process that triggered the event.
205+
type Execution struct {
206+
// ProcessID and ThreadID are required on execution info
207+
ProcessID uint `xml:"ProcessID,attr"`
208+
ThreadID uint `xml:"ThreadID,attr"`
209+
// These remaining fields are all optional for execution info
210+
ProcessorID *uint `xml:"ProcessorID,attr"`
211+
SessionID *uint `xml:"SessionID,attr"`
212+
KernelTime *uint `xml:"KernelTime,attr"`
213+
UserTime *uint `xml:"UserTime,attr"`
214+
ProcessorTime *uint `xml:"ProcessorTime,attr"`
215+
}
216+
217+
func (e Execution) asMap() map[string]any {
218+
result := map[string]any{
219+
"process_id": e.ProcessID,
220+
"thread_id": e.ThreadID,
221+
}
222+
223+
if e.ProcessorID != nil {
224+
result["processor_id"] = *e.ProcessorID
225+
}
226+
227+
if e.SessionID != nil {
228+
result["session_id"] = *e.SessionID
229+
}
230+
231+
if e.KernelTime != nil {
232+
result["kernel_time"] = *e.KernelTime
233+
}
234+
235+
if e.UserTime != nil {
236+
result["user_time"] = *e.UserTime
237+
}
238+
239+
if e.ProcessorTime != nil {
240+
result["processor_time"] = *e.ProcessorTime
241+
}
242+
243+
return result
244+
}

pkg/stanza/operator/input/windows/xml_test.go

Lines changed: 198 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,162 @@ func TestParseBody(t *testing.T) {
116116
require.Equal(t, expected, xml.parseBody())
117117
}
118118

119+
func TestParseBodySecurityExecution(t *testing.T) {
120+
xml := EventXML{
121+
EventID: EventID{
122+
ID: 1,
123+
Qualifiers: 2,
124+
},
125+
Provider: Provider{
126+
Name: "provider",
127+
GUID: "guid",
128+
EventSourceName: "event source",
129+
},
130+
TimeCreated: TimeCreated{
131+
SystemTime: "2020-07-30T01:01:01.123456789Z",
132+
},
133+
Computer: "computer",
134+
Channel: "application",
135+
RecordID: 1,
136+
Level: "Information",
137+
Message: "message",
138+
Task: "task",
139+
Opcode: "opcode",
140+
Keywords: []string{"keyword"},
141+
EventData: []EventDataEntry{
142+
{Name: "name", Value: "value"}, {Name: "another_name", Value: "another_value"},
143+
},
144+
Execution: &Execution{
145+
ProcessID: 13,
146+
ThreadID: 102,
147+
},
148+
Security: &Security{
149+
UserID: "my-user-id",
150+
},
151+
RenderedLevel: "rendered_level",
152+
RenderedTask: "rendered_task",
153+
RenderedOpcode: "rendered_opcode",
154+
RenderedKeywords: []string{"RenderedKeywords"},
155+
}
156+
157+
expected := map[string]interface{}{
158+
"event_id": map[string]interface{}{
159+
"id": uint32(1),
160+
"qualifiers": uint16(2),
161+
},
162+
"provider": map[string]interface{}{
163+
"name": "provider",
164+
"guid": "guid",
165+
"event_source": "event source",
166+
},
167+
"system_time": "2020-07-30T01:01:01.123456789Z",
168+
"computer": "computer",
169+
"channel": "application",
170+
"record_id": uint64(1),
171+
"level": "rendered_level",
172+
"message": "message",
173+
"task": "rendered_task",
174+
"opcode": "rendered_opcode",
175+
"keywords": []string{"RenderedKeywords"},
176+
"execution": map[string]any{
177+
"process_id": uint(13),
178+
"thread_id": uint(102),
179+
},
180+
"security": map[string]any{
181+
"user_id": "my-user-id",
182+
},
183+
"event_data": map[string]interface{}{"name": "value", "another_name": "another_value"},
184+
}
185+
186+
require.Equal(t, expected, xml.parseBody())
187+
}
188+
189+
func TestParseBodyFullExecution(t *testing.T) {
190+
processorID := uint(3)
191+
sessionID := uint(2)
192+
kernelTime := uint(3)
193+
userTime := uint(100)
194+
processorTime := uint(200)
195+
196+
xml := EventXML{
197+
EventID: EventID{
198+
ID: 1,
199+
Qualifiers: 2,
200+
},
201+
Provider: Provider{
202+
Name: "provider",
203+
GUID: "guid",
204+
EventSourceName: "event source",
205+
},
206+
TimeCreated: TimeCreated{
207+
SystemTime: "2020-07-30T01:01:01.123456789Z",
208+
},
209+
Computer: "computer",
210+
Channel: "application",
211+
RecordID: 1,
212+
Level: "Information",
213+
Message: "message",
214+
Task: "task",
215+
Opcode: "opcode",
216+
Keywords: []string{"keyword"},
217+
EventData: []EventDataEntry{
218+
{Name: "name", Value: "value"}, {Name: "another_name", Value: "another_value"},
219+
},
220+
Execution: &Execution{
221+
ProcessID: 13,
222+
ThreadID: 102,
223+
ProcessorID: &processorID,
224+
SessionID: &sessionID,
225+
KernelTime: &kernelTime,
226+
UserTime: &userTime,
227+
ProcessorTime: &processorTime,
228+
},
229+
Security: &Security{
230+
UserID: "my-user-id",
231+
},
232+
RenderedLevel: "rendered_level",
233+
RenderedTask: "rendered_task",
234+
RenderedOpcode: "rendered_opcode",
235+
RenderedKeywords: []string{"RenderedKeywords"},
236+
}
237+
238+
expected := map[string]interface{}{
239+
"event_id": map[string]interface{}{
240+
"id": uint32(1),
241+
"qualifiers": uint16(2),
242+
},
243+
"provider": map[string]interface{}{
244+
"name": "provider",
245+
"guid": "guid",
246+
"event_source": "event source",
247+
},
248+
"system_time": "2020-07-30T01:01:01.123456789Z",
249+
"computer": "computer",
250+
"channel": "application",
251+
"record_id": uint64(1),
252+
"level": "rendered_level",
253+
"message": "message",
254+
"task": "rendered_task",
255+
"opcode": "rendered_opcode",
256+
"keywords": []string{"RenderedKeywords"},
257+
"execution": map[string]any{
258+
"process_id": uint(13),
259+
"thread_id": uint(102),
260+
"processor_id": processorID,
261+
"session_id": sessionID,
262+
"kernel_time": kernelTime,
263+
"user_time": userTime,
264+
"processor_time": processorTime,
265+
},
266+
"security": map[string]any{
267+
"user_id": "my-user-id",
268+
},
269+
"event_data": map[string]interface{}{"name": "value", "another_name": "another_value"},
270+
}
271+
272+
require.Equal(t, expected, xml.parseBody())
273+
}
274+
119275
func TestParseNoRendered(t *testing.T) {
120276
xml := EventXML{
121277
EventID: EventID{
@@ -252,7 +408,7 @@ func TestInvalidUnmarshal(t *testing.T) {
252408
require.Error(t, err)
253409

254410
}
255-
func TestUnmarshal(t *testing.T) {
411+
func TestUnmarshalWithEventData(t *testing.T) {
256412
data, err := os.ReadFile(filepath.Join("testdata", "xmlSample.xml"))
257413
require.NoError(t, err)
258414

@@ -283,7 +439,47 @@ func TestUnmarshal(t *testing.T) {
283439
{Name: "Time", Value: "2022-04-28T19:48:52Z"},
284440
{Name: "Source", Value: "RulesEngine"},
285441
},
286-
Keywords: []string{"0x80000000000000"},
442+
Keywords: []string{"0x80000000000000"},
443+
Security: &Security{},
444+
Execution: &Execution{},
445+
}
446+
447+
require.Equal(t, xml, event)
448+
}
449+
450+
func TestUnmarshalWithUserData(t *testing.T) {
451+
data, err := os.ReadFile(filepath.Join("testdata", "xmlSampleUserData.xml"))
452+
require.NoError(t, err)
453+
454+
event, err := unmarshalEventXML(data)
455+
require.NoError(t, err)
456+
457+
xml := EventXML{
458+
EventID: EventID{
459+
ID: 1102,
460+
},
461+
Provider: Provider{
462+
Name: "Microsoft-Windows-Eventlog",
463+
GUID: "{fc65ddd8-d6ef-4962-83d5-6e5cfe9ce148}",
464+
},
465+
TimeCreated: TimeCreated{
466+
SystemTime: "2023-10-12T10:38:24.543506200Z",
467+
},
468+
Computer: "test.example.com",
469+
Channel: "Security",
470+
RecordID: 2590526,
471+
Level: "4",
472+
Message: "",
473+
Task: "104",
474+
Opcode: "0",
475+
Keywords: []string{"0x4020000000000000"},
476+
Security: &Security{
477+
UserID: "S-1-5-18",
478+
},
479+
Execution: &Execution{
480+
ProcessID: 1472,
481+
ThreadID: 7784,
482+
},
287483
}
288484

289485
require.Equal(t, xml, event)

0 commit comments

Comments
 (0)