From 3bd6839bfbdd7f4ff5aecb358a81aa9cb8a62ae9 Mon Sep 17 00:00:00 2001 From: Mike Cohen Date: Mon, 30 Jun 2025 14:05:39 +1000 Subject: [PATCH 1/2] Bugfix: Substitution token should not parse XML in template context Fixes: #33 --- cmd/extract_windows.go | 3 ++- debug.go | 2 ++ evtx.go | 24 +++++++++++++++++++----- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/cmd/extract_windows.go b/cmd/extract_windows.go index 7d8289e..ae7c97b 100644 --- a/cmd/extract_windows.go +++ b/cmd/extract_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package main @@ -160,7 +161,7 @@ func doExtract() { } defer fd.Close() - fmt.Printf("Openning message table file %v\n", message_table) + fmt.Printf("Opening message table file %v\n", message_table) reader, err := reader.NewPagedReader(fd, 4096, 100) if err != nil { diff --git a/debug.go b/debug.go index f5a46bf..574839f 100644 --- a/debug.go +++ b/debug.go @@ -11,3 +11,5 @@ func debug(format string, args ...interface{}) { fmt.Printf(format, args...) } } + +func DlvBreak() {} diff --git a/evtx.go b/evtx.go index ca2c216..14c894a 100644 --- a/evtx.go +++ b/evtx.go @@ -67,6 +67,7 @@ type EVTXHeader struct { MinorVersion uint16 MajorVersion uint16 HeaderBlockSize uint16 + ChunkCount uint16 _ [76]byte FileFlags uint32 CheckSum uint32 @@ -619,6 +620,7 @@ func ReadPrefixedUnicodeString(ctx *ParseContext, is_null_terminated bool) strin buffer := ctx.ConsumeBytes(count * 2) result := UTF16LEToUTF8(buffer) debug("ReadPrefixedUnicodeString exit: %x %s\n", ctx.Offset(), string(result)) + return string(result) } @@ -648,7 +650,6 @@ func ParseOpenStartElement(ctx *ParseContext, debug("ParseOpenStartElement Enter: %x\n", ctx.Offset()) /* dependencyID := ctx.ConsumeUint16() - elementLength := ctx.ConsumeUint32() */ // ctx.SkipBytes(2 + 4) @@ -656,7 +657,9 @@ func ParseOpenStartElement(ctx *ParseContext, ctx.SkipBytes(2) } - ctx.SkipBytes(4) + elementLength := ctx.ConsumeUint32() + debug("ParseOpenStartElement elementLength: %x\n", elementLength) + nameBuffer := ReadName(ctx) attributeListLength := uint32(0) @@ -732,7 +735,8 @@ func ParseTemplateInstance(ctx *ParseContext) bool { /* tempResLen := ctx.ConsumeUint32() */ - ctx.SkipBytes(4) + template_definition_data := int(ctx.ConsumeUint32()) + debug("ParseTemplateInstance template_definition_data %x\n", template_definition_data) // Template arguments should not be unreasonable here. Just cap // them at a reasonable size. @@ -745,7 +749,7 @@ func ParseTemplateInstance(ctx *ParseContext) bool { template, pres := ctx.GetTemplateByID(short_id) if !pres { - // longID := ctx.ConsumeBytes(16) + // longGUID := ctx.ConsumeBytes(16) ctx.SkipBytes(16) templateBodyLen := int(ctx.ConsumeUint32()) @@ -847,9 +851,18 @@ func ParseTemplateInstance(ctx *ParseContext) bool { str += fmt.Sprintf("-%d", ctx.ConsumeUint32()) } arg_values[idx] = str + case 0x21: // BinXml new_ctx := ctx.Copy() - ParseBinXML(new_ctx, TemplateContext) + + // Substitution args are not encoded in template context. + /* + https://github.com/libyal/libevtx/blob/main/documentation/Windows%20XML%20Event%20Log%20(EVTX).asciidoc#token_types + > According to [MS-EVEN6] the dependency identifier is not present + > when the element start is used in a substitution token with value + > type: Binary XML (0x21). + */ + ParseBinXML(new_ctx, !TemplateContext) ctx.SkipBytes(arg.argLen) arg_values[idx] = new_ctx.CurrentTemplate().Expand(nil) @@ -943,6 +956,7 @@ func ParseBinXML(ctx *ParseContext, template_context bool) { case 0x0B /* PIDataToken */ : case 0x0C /* TemplateInstanceToken */ : keep_going = ParseTemplateInstance(ctx) + case 0x0D /* NormalSubstitutionToken */, 0x0E: /* OptionalSubstitutionToken */ keep_going = ParseOptionalSubstitution(ctx) From c119d1401865e12b288656a5111604ce23911d73 Mon Sep 17 00:00:00 2001 From: Mike Cohen Date: Mon, 30 Jun 2025 14:13:07 +1000 Subject: [PATCH 2/2] Added test --- cmd/parse.go | 7 +++ fixtures/CAPI2_Operational.golden | 56 ++++++++++++++++++ parser_test.go | 16 +++++ ...t-Windows-CAPI2_Operational_EventID70.evtx | Bin 0 -> 69632 bytes 4 files changed, 79 insertions(+) create mode 100644 fixtures/CAPI2_Operational.golden create mode 100644 testdata/Microsoft-Windows-CAPI2_Operational_EventID70.evtx diff --git a/cmd/parse.go b/cmd/parse.go index f1b7395..1012ca4 100644 --- a/cmd/parse.go +++ b/cmd/parse.go @@ -21,6 +21,9 @@ var ( parse_file_message_file = parse.Flag("messagedb", "Path to messages database."). String() + parse_file_disable_message = parse.Flag("disable_messages", "Disable message resolver."). + Bool() + start_record_id = parse.Flag("start", "First EventID to dump").Int() number_of_records = parse.Flag("number", "How many records to print"). Default("99999999").Int() @@ -78,6 +81,10 @@ func (self *parsingContext) Parse() { } func NewParsingContext() *parsingContext { + if *parse_file_disable_message { + return &parsingContext{evtx.NullResolver{}} + } + if *parse_file_message_file != "" { resolver, err := evtx.NewDBResolver(*parse_file_message_file) kingpin.FatalIfError(err, " %v", err) diff --git a/fixtures/CAPI2_Operational.golden b/fixtures/CAPI2_Operational.golden new file mode 100644 index 0000000..90881eb --- /dev/null +++ b/fixtures/CAPI2_Operational.golden @@ -0,0 +1,56 @@ +{ + "System": { + "Provider": { + "Name": "Microsoft-Windows-CAPI2", + "Guid": "{5bbca4a8-b209-48dc-a8c7-b23d3e5216fb}" + }, + "EventID": { + "Value": 70 + }, + "Version": 0, + "Level": 4, + "Task": 70, + "Opcode": 0, + "Keywords": 4611686018427388032, + "TimeCreated": { + "SystemTime": 1751191789.1541903 + }, + "EventRecordID": 707, + "Correlation": { + "ActivityID": "0E2A62B5-0688-42DE-9AE1-A2AD54A8CE40" + }, + "Execution": { + "ProcessID": 23708, + "ThreadID": 17980 + }, + "Channel": "Microsoft-Windows-CAPI2/Operational", + "Computer": "ILDHBZJST3", + "Security": { + "UserID": "S-1-5-21-1332095402-2705258134-1427695613-1001" + } + }, + "UserData": { + "CryptAcquireCertificatePrivateKey": { + "Certificate": { + "fileRef": "3AA55F503B946700761B3990B569E6F248C3D31D.cer", + "subjectName": "5ae3f7e8-6eef-4e9e-8fc9-94f5d0c0862b" + }, + "Flags": { + "value": "10040", + "CRYPT_ACQUIRE_SILENT_FLAG": "true", + "CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG": "true" + }, + "EventAuxInfo": { + "ProcessName": "RuntimeBroker.exe" + }, + "CorrelationAuxInfo": { + "TaskId": "{59A34F27-7A62-4C7D-9DE1-9E1F0E2382D5}", + "SeqNumber": "2" + }, + "Result": { + "value": "0" + } + } + }, + "Message": "" + } diff --git a/parser_test.go b/parser_test.go index 602b2fe..57aa7eb 100644 --- a/parser_test.go +++ b/parser_test.go @@ -44,6 +44,22 @@ func (self *EVTXTestSuite) TestCollector() { goldie.Assert(self.T(), fixture_name, out) } +func (self *EVTXTestSuite) TestTemplates() { + cmdline := []string{ + "parse", "testdata/Microsoft-Windows-CAPI2_Operational_EventID70.evtx", + "--disable_messages", + } + cmd := exec.Command(self.binary, cmdline...) + out, err := cmd.CombinedOutput() + assert.NoError(self.T(), err) + + out = bytes.ReplaceAll(out, []byte{'\r', '\n'}, []byte{'\n'}) + fixture_name := "CAPI2_Operational" + fmt.Printf("Testing fixture %v\n", fixture_name) + + goldie.Assert(self.T(), fixture_name, out) +} + func TestEvtx(t *testing.T) { suite.Run(t, &EVTXTestSuite{}) } diff --git a/testdata/Microsoft-Windows-CAPI2_Operational_EventID70.evtx b/testdata/Microsoft-Windows-CAPI2_Operational_EventID70.evtx new file mode 100644 index 0000000000000000000000000000000000000000..686234cb9e6e66b323770156ca960cec461cbc5d GIT binary patch literal 69632 zcmeI0eQcFi9mjvqeR+C&TW^7i&V_6g$PCRW?fO0@;r8}kKnks;(BK>PbuH@)(3e5T zBE}7jM5iVWUDPQtnJ$CupE+DGGg&l}ESs4zM)763WOIx^9L{CLnfm>n=Xvnbu0yjd z{_{OI&-0v@-#O=ZKEL1jozv#Wh6|%(LpDFf!iuB##w>2O#G5bjuFKgg|J?Yl2hji` zAOa#F0wN#+A|L`HAOa#F0wN#+*Cmi28_tbP+-1>U_ltdxawPmyU{kr-ij`fLt~>B` z1US#-`(OHIvDisJD2?OWpEP@})a=Ewpl%E5`clQxK|UL{FKjV;0rFcCLCb4tv(MmR zs=ykebSUh967w+~2<1mgf->LBQTIYvAJzUC51z0A$0KM-ld{41#!yI0-# z`+IJGwDa5)h*2s#H+LtpTp-hvXq-Yr(rdI4 zh)vsmyWQ5~H)R8;8G=k7@-_I5qUNAYqBLp4kgP#@67}otZnRC{dz;nb*MMBDwctH; zb^57mN-=M4?kh!ho9yvzrya(~({4sMI2Q9O=yfCh;l=dYk(710*=I4=AfyhW_Axw# zkAx!IzMilhP<9-RT+jz?`vR5MS+7%d5Q^W8>DF1!vKV+PRQHdC1FeOdZMGRV4+px% zj##7h<2Qie8}PKCW4~K%D{>7e529_IYZDR-(Yo$8_c<-#XzsbO;)UVjf3I+8Pw1J-hF@lx;)vVZfh+1qU%EMU&XsFnAXV zW$<984PP$9q@&K7IfxEH0XoMZ+8mw+2-2JHIFi1`ccMmxiAPQH;YD;_oY> z-HidMGBpXv$1-V4Q1sd4IHB(7!=DNFeb>NM?rttk*53E4&fGNqP^dvkLmTz@#{=i#C`NcO>zCY;_d zTGCtnKW?z~&PBL$Y03Z~ojQT(S}+}>GR?nXBIR(8!=bpuV6S7sG?vkU4YI}B?Bn)P zn7s!ZjMi#H_dL2YeYYls78-U2Qw2sayBRHhbe0Tv__hmFIC4m)+$!SDoBu`*erYD}~{vgcxz0EG7y}
  • Eyl=H~1KYxdDbzm-KFh5wrL;;#a^^ zi*lQ_BCa+emhwbtMy?KZ^{8z{xeZcH$mQ`aAi6RF=g``Yrw(tPOjO6`!A~ulpDu&B zXApq*p@AE;_@r2a;rdWt4~3fXWq5DGmkKhx4`Hey$Qx z^dq=m#*%nlVmh8kLOt&CJ2AW2ukjRkN8zb<;EPFzbqs0^p&DrB8_VAdPv@S|Fhh zgJsdZ1G}^rL+!XmavP@Dv*-lz>@23_4Qn;_K+CJ;kd7`MS*UObN=^XSB&4?C@p%bk zdhjo0^8!_|T^J>2(2-unQ@suAnZzMP)7Ie4C>YP4$2igDv+Ke#sQq@4dcGQ8L$3nw zoD3T)H~7F8oN!y+5fh#It-x6ii_pt>t~aAhQ`0SSD78ChXvcc%T#nxY?3ag3J>23p zNHpTq_pe@`-z;^}=>P!FU{(EYHMExp;dr(hdPUdbPkqC8+k~Dw1_}s|czk*g#cM5& zFpg<^d2fW;(VeCpyy$ZisF^~Z_na=M+iS;8JoQM^;ZvQDJ~puIv&X;v)PHkBT70y{ z0(!aExUn@}ihakp#2Cm}*ySQ%5D$A%|IF#x)=Ouvbe<_6%{}~3RrNw$YH*36_QBm} zCkV`7l{`3V97C4(BflN1op3AWbv3&0Vu~;#+usnZc)$Jyz86;nQ8V#m#nSUv@7J~W z{^%v%ua}|I{Gd+xcM9q_IyjeYL{BbsH@*W_P{%65c_1>I}K7 zcOP~*Z+c9@sR}oWIs4I$X94Kd(2hU(;on!i7j_IL*INWcKmYKmYKmYKmYKmYKmYKmYKmYKmYKmY zKmYKmYKmYKmYKmYKmYKmYKmYKmYKmYKmYKmYKmY zKmYKmYKmYKmYKmYKmYKmJ)`NH6cH?2b!#Zs