diff --git a/pkg/event/event_windows.go b/pkg/event/event_windows.go index 4308ce2f0..97e22e8c7 100644 --- a/pkg/event/event_windows.go +++ b/pkg/event/event_windows.go @@ -21,6 +21,11 @@ package event import ( "encoding/binary" "fmt" + "os" + "strings" + "sync" + "unsafe" + "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/rabbitstack/fibratus/pkg/sys" "github.com/rabbitstack/fibratus/pkg/sys/etw" @@ -29,10 +34,6 @@ import ( "github.com/rabbitstack/fibratus/pkg/util/hostname" "github.com/rabbitstack/fibratus/pkg/util/ntstatus" "golang.org/x/sys/windows" - "os" - "strings" - "sync" - "unsafe" ) var ( @@ -111,9 +112,9 @@ func (e *Event) adjustPID() { case Process: // process start events may be logged in the context of the parent or child process. // As a result, the ProcessId member of EVENT_TRACE_HEADER may not correspond to the - // process being created, so we set the event pid to be the one of the parent process + // process being created, so we extract the process id from the event parameters if e.IsCreateProcess() { - e.PID, _ = e.Params.GetPpid() + e.PID, _ = e.Params.GetPid() } case Net: if !e.IsDNS() { @@ -266,8 +267,24 @@ func (e *Event) IsOpenDisposition() bool { return e.IsCreateFile() && e.Params.MustGetUint32(params.FileOperation) == windows.FILE_OPEN } -// StackID returns the integer that is used to identify the callstack present in the StackWalk event. -func (e *Event) StackID() uint64 { return uint64(e.PID + e.Tid) } +// StackID returns the integer that is used to stich the callstack present in the StackWalk event. +func (e *Event) StackID() uint64 { + if e.IsCreateProcess() { + return uint64(e.Params.MustGetPpid() + e.Tid) + } + return uint64(e.PID + e.Tid) +} + +// StackPID returns the process id as seen the creator +// from the callstack execution perspective. For example, +// the pid associated with CreateProcess events is the +// parent, not the process being created. +func (e *Event) StackPID() uint32 { + if e.IsCreateProcess() { + return e.Params.MustGetPpid() + } + return e.PID +} // RundownKey calculates the rundown event hash. The hash is // used to determine if the rundown event was already processed. diff --git a/pkg/filter/accessor_windows.go b/pkg/filter/accessor_windows.go index aa18c401d..c4721ba75 100644 --- a/pkg/filter/accessor_windows.go +++ b/pkg/filter/accessor_windows.go @@ -21,17 +21,17 @@ package filter import ( "errors" "expvar" - "github.com/rabbitstack/fibratus/pkg/fs" - "github.com/rabbitstack/fibratus/pkg/network" - psnap "github.com/rabbitstack/fibratus/pkg/ps" - "github.com/rabbitstack/fibratus/pkg/util/cmdline" - "github.com/rabbitstack/fibratus/pkg/util/signature" "net" "path/filepath" "strconv" "strings" "time" + "github.com/rabbitstack/fibratus/pkg/fs" + "github.com/rabbitstack/fibratus/pkg/network" + psnap "github.com/rabbitstack/fibratus/pkg/ps" + "github.com/rabbitstack/fibratus/pkg/util/signature" + "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/rabbitstack/fibratus/pkg/filter/fields" @@ -91,14 +91,7 @@ func newPSAccessor(psnap psnap.Snapshotter) Accessor { return &psAccessor{psnap: func (ps *psAccessor) Get(f Field, e *event.Event) (params.Value, error) { switch f.Name { case fields.PsPid: - // identifier of the process that is generating the event return e.PID, nil - case fields.PsSiblingPid, fields.PsChildPid: - if e.Category != event.Process { - return nil, nil - } - // the id of a created child process. `e.PID` is the parent process id - return e.Params.GetPid() case fields.PsPpid: ps := e.PS if ps == nil { @@ -111,48 +104,24 @@ func (ps *psAccessor) Get(f Field, e *event.Event) (params.Value, error) { return nil, ErrPsNil } return ps.Name, nil - case fields.PsSiblingName, fields.PsChildName: - if e.Category != event.Process { - return nil, nil - } - return e.Params.GetString(params.ProcessName) case fields.PsComm, fields.PsCmdline: ps := e.PS if ps == nil { return nil, ErrPsNil } return ps.Cmdline, nil - case fields.PsSiblingComm, fields.PsChildCmdline: - if e.Category != event.Process { - return nil, nil - } - return e.Params.GetString(params.Cmdline) case fields.PsExe: ps := e.PS if ps == nil { return nil, ErrPsNil } return ps.Exe, nil - case fields.PsSiblingExe, fields.PsChildExe: - if e.Category != event.Process { - return nil, nil - } - return e.Params.GetString(params.Exe) case fields.PsArgs: ps := e.PS if ps == nil { return nil, ErrPsNil } return ps.Args, nil - case fields.PsSiblingArgs, fields.PsChildArgs: - if e.Category != event.Process { - return nil, nil - } - cmndline, err := e.Params.GetString(params.Cmdline) - if err != nil { - return nil, err - } - return cmdline.Split(cmndline), nil case fields.PsCwd: ps := e.PS if ps == nil { @@ -165,40 +134,6 @@ func (ps *psAccessor) Get(f Field, e *event.Event) (params.Value, error) { return nil, ErrPsNil } return ps.SID, nil - case fields.PsSiblingSID, fields.PsChildSID: - if e.Category != event.Process { - return nil, nil - } - sid, err := e.Params.GetSID() - if err != nil { - return nil, err - } - return sid.String(), nil - case fields.PsSiblingDomain, fields.PsChildDomain: - if e.Category != event.Process { - return nil, nil - } - return e.Params.GetString(params.Domain) - case fields.PsSiblingUsername, fields.PsChildUsername: - if e.Category != event.Process { - return nil, nil - } - return e.Params.GetString(params.Username) - case fields.PsChildIsWOW64Field: - if e.Category != event.Process { - return nil, nil - } - return (e.Params.MustGetUint32(params.ProcessFlags) & event.PsWOW64) != 0, nil - case fields.PsChildIsPackagedField: - if e.Category != event.Process { - return nil, nil - } - return (e.Params.MustGetUint32(params.ProcessFlags) & event.PsPackaged) != 0, nil - case fields.PsChildIsProtectedField: - if e.Category != event.Process { - return nil, nil - } - return (e.Params.MustGetUint32(params.ProcessFlags) & event.PsProtected) != 0, nil case fields.PsIsWOW64Field: ps := e.PS if ps == nil { @@ -250,11 +185,6 @@ func (ps *psAccessor) Get(f Field, e *event.Event) (params.Value, error) { return nil, nil } return e.GetParamAsString(params.NTStatus), nil - case fields.PsSiblingSessionID, fields.PsChildSessionID: - if e.Category != event.Process { - return nil, nil - } - return e.Params.GetUint32(params.SessionID) case fields.PsModuleNames: ps := e.PS if ps == nil { @@ -277,25 +207,6 @@ func (ps *psAccessor) Get(f Field, e *event.Event) (params.Value, error) { return nil, ErrPsNil } return ps.UUID(), nil - case fields.PsChildUUID: - if e.Category != event.Process { - return nil, nil - } - - pid, err := e.Params.GetPid() - if err != nil { - return nil, err - } - if ps.psnap == nil { - return nil, nil - } - - proc := ps.psnap.FindAndPut(pid) - if proc == nil { - return nil, ErrPsNil - } - - return proc.UUID(), nil case fields.PsHandleNames: ps := e.PS if ps == nil { @@ -430,21 +341,6 @@ func (ps *psAccessor) Get(f Field, e *event.Event) (params.Value, error) { return nil, ErrPsNil } return ps.IsProtected, nil - case fields.PsChildTokenIntegrityLevel: - if e.Category != event.Process { - return nil, nil - } - return e.GetParamAsString(params.ProcessTokenIntegrityLevel), nil - case fields.PsChildTokenIsElevated: - if e.Category != event.Process { - return nil, nil - } - return e.Params.GetBool(params.ProcessTokenIsElevated) - case fields.PsChildTokenElevationType: - if e.Category != event.Process { - return nil, nil - } - return e.GetParamAsString(params.ProcessTokenElevationType), nil case fields.PsTokenIntegrityLevel: ps := e.PS if ps == nil { @@ -667,13 +563,13 @@ func (t *threadAccessor) Get(f Field, e *event.Event) (params.Value, error) { return e.Callstack.Symbols(), nil case fields.ThreadCallstackAllocationSizes: - return e.Callstack.AllocationSizes(e.PID), nil + return e.Callstack.AllocationSizes(framePID(e)), nil case fields.ThreadCallstackProtections: - return e.Callstack.Protections(e.PID), nil + return e.Callstack.Protections(framePID(e)), nil case fields.ThreadCallstackCallsiteLeadingAssembly: - return e.Callstack.CallsiteInsns(e.PID, true), nil + return e.Callstack.CallsiteInsns(framePID(e), true), nil case fields.ThreadCallstackCallsiteTrailingAssembly: - return e.Callstack.CallsiteInsns(e.PID, false), nil + return e.Callstack.CallsiteInsns(framePID(e), false), nil case fields.ThreadCallstackIsUnbacked: return e.Callstack.ContainsUnbacked(), nil case fields.ThreadCallstack: @@ -1171,18 +1067,9 @@ func (pa *peAccessor) Get(f Field, e *event.Event) (params.Value, error) { // PE enrichment is likely disabled. Load PE data lazily // by only requesting parsing of the PE directories that // are relevant to the fields present in the expression. - // If the field references a child process executable - // original file name as part of the CreateProcess event, - // then the parser obtains the PE metadata for the executable - // path parameter - if (e.PS != nil && e.PS.Exe != "" && p == nil) || f.Name == fields.PePsChildFileName || f.Name == fields.PsChildPeFilename { + if e.PS != nil && e.PS.Exe != "" && p == nil { var err error - var exe string - if (f.Name == fields.PePsChildFileName || f.Name == fields.PsChildPeFilename) && e.IsCreateProcess() { - exe = e.GetParamAsString(params.Exe) - } else { - exe = e.PS.Exe - } + exe := e.PS.Exe p, err = pe.ParseFile(exe, pa.parserOpts()...) if err != nil { return nil, err @@ -1228,9 +1115,7 @@ func (pa *peAccessor) Get(f Field, e *event.Event) (params.Value, error) { p.VerifySignature() } - if f.Name != fields.PePsChildFileName { - e.PS.PE = p - } + e.PS.PE = p switch f.Name { case fields.PeEntrypoint: @@ -1294,7 +1179,7 @@ func (pa *peAccessor) Get(f Field, e *event.Event) (params.Value, error) { return p.VersionResources[pe.LegalCopyright], nil case fields.PeDescription: return p.VersionResources[pe.FileDescription], nil - case fields.PeFileName, fields.PePsChildFileName, fields.PsChildPeFilename: + case fields.PeFileName: return p.VersionResources[pe.OriginalFilename], nil case fields.PeFileVersion: return p.VersionResources[pe.FileVersion], nil diff --git a/pkg/filter/fields/fields_windows.go b/pkg/filter/fields/fields_windows.go index d4e0e021f..a55819043 100644 --- a/pkg/filter/fields/fields_windows.go +++ b/pkg/filter/fields/fields_windows.go @@ -113,70 +113,16 @@ const ( PsParentIsPackagedField Field = "ps.parent.is_packaged" // PsParentIsProtectedField represents the field that indicates if the process is to be run as a protected process PsParentIsProtectedField Field = "ps.parent.is_protected" - - // PsSiblingPid represents the sibling process identifier field. Deprecated - PsSiblingPid Field = "ps.sibling.pid" - // PsSiblingName represents the sibling process name field. Deprecated - PsSiblingName Field = "ps.sibling.name" - // PsSiblingComm represents the sibling process command line field. Deprecated - PsSiblingComm Field = "ps.sibling.comm" - // PsSiblingExe represents the sibling process complete executable path field. Deprecated - PsSiblingExe Field = "ps.sibling.exe" - // PsSiblingArgs represents the sibling process command line arguments path field. Deprecated - PsSiblingArgs Field = "ps.sibling.args" - // PsSiblingSID represents the sibling process security identifier field. Deprecated - PsSiblingSID Field = "ps.sibling.sid" - // PsSiblingSessionID represents the sibling process session id field. Deprecated - PsSiblingSessionID Field = "ps.sibling.sessionid" - // PsSiblingDomain represents the sibling process domain field. Deprecated - PsSiblingDomain Field = "ps.sibling.domain" - // PsSiblingUsername represents the sibling process username field. Deprecated - PsSiblingUsername Field = "ps.sibling.username" // PsUUID represents the unique process identifier PsUUID Field = "ps.uuid" // PsParentUUID represents the unique parent process identifier PsParentUUID Field = "ps.parent.uuid" - // PsChildUUID represents the unique child process identifier - PsChildUUID Field = "ps.child.uuid" - - // PsChildPid represents the child process identifier field - PsChildPid Field = "ps.child.pid" - // PsChildName represents the child process name field - PsChildName Field = "ps.child.name" - // PsChildCmdline represents the child process command line field - PsChildCmdline Field = "ps.child.cmdline" - // PsChildExe represents the child process complete executable path field - PsChildExe Field = "ps.child.exe" - // PsChildArgs represents the child process command line arguments path field - PsChildArgs Field = "ps.child.args" - // PsChildSID represents the child process security identifier field - PsChildSID Field = "ps.child.sid" - // PsChildSessionID represents the child process session id field - PsChildSessionID Field = "ps.child.sessionid" - // PsChildDomain represents the child process domain field - PsChildDomain Field = "ps.child.domain" - // PsChildUsername represents the child process username field - PsChildUsername Field = "ps.child.username" - // PsChildPeFilename represents the original file name of the child process executable provided at compile-time - PsChildPeFilename Field = "ps.child.pe.file.name" - // PsChildIsWOW64Field represents the field that indicates if the 32-bit process is created in 64-bit Windows system - PsChildIsWOW64Field Field = "ps.child.is_wow64" - // PsChildIsPackagedField represents the field that indicates if a process is packaged with the MSIX technology - PsChildIsPackagedField Field = "ps.child.is_packaged" - // PsChildIsProtectedField represents the field that indicates if the process is to be run as a protected process - PsChildIsProtectedField Field = "ps.child.is_protected" // PsTokenIntegrityLevel represents the field that indicates the current process integrity level PsTokenIntegrityLevel = "ps.token.integrity_level" // PsTokenIsElevated represents the field that indicates if the current process token is elevated PsTokenIsElevated = "ps.token.is_elevated" // PsTokenElevationType represents the field that indicates if the current process token elevation type PsTokenElevationType = "ps.token.elevation_type" - // PsChildTokenIntegrityLevel represents the field that indicates the created/child process integrity level - PsChildTokenIntegrityLevel = "ps.child.token.integrity_level" - // PsChildTokenIsElevated represents the field that indicates if the created/child process token is elevated - PsChildTokenIsElevated = "ps.child.token.is_elevated" - // PsChildTokenElevationType represents the field that indicates if the created/child process token elevation type - PsChildTokenElevationType = "ps.child.token.elevation_type" // PsParentTokenIntegrityLevel represents the field that indicates the parent process integrity level PsParentTokenIntegrityLevel = "ps.parent.token.integrity_level" // PsParentTokenIsElevated represents the field that indicates if the parent process token is elevated @@ -315,8 +261,6 @@ const ( PeCertBefore Field = "pe.cert.before" // PeIsModified is the field that indicates whether disk and in-memory PE headers differ PeIsModified Field = "pe.is_modified" - // PePsChildFileName represents the original file name of the child process executable provided at compile-time - PePsChildFileName Field = "pe.ps.child.file.name" // EvtSeq is the event sequence number EvtSeq Field = "evt.seq" @@ -626,7 +570,7 @@ func (f Field) IsFileField() bool { return strings.HasPrefix(string(f), "f func (f Field) IsRegistryField() bool { return strings.HasPrefix(string(f), "registry.") } func (f Field) IsNetworkField() bool { return strings.HasPrefix(string(f), "net.") } func (f Field) IsHandleField() bool { return strings.HasPrefix(string(f), "handle.") } -func (f Field) IsPeField() bool { return strings.HasPrefix(string(f), "pe.") || f == PsChildPeFilename } +func (f Field) IsPeField() bool { return strings.HasPrefix(string(f), "pe.") } func (f Field) IsMemField() bool { return strings.HasPrefix(string(f), "mem.") } func (f Field) IsDNSField() bool { return strings.HasPrefix(string(f), "dns.") } func (f Field) IsThreadpoolField() bool { return strings.HasPrefix(string(f), "threadpool.") } @@ -634,7 +578,7 @@ func (f Field) IsThreadpoolField() bool { return strings.HasPrefix(string(f), "t func (f Field) IsPeSection() bool { return f == PeNumSections } func (f Field) IsPeSymbol() bool { return f == PeSymbols || f == PeNumSymbols || f == PeImports } func (f Field) IsPeVersionResource() bool { - return f == PeCompany || f == PeCopyright || f == PeDescription || f == PeFileName || f == PeFileVersion || f == PeProduct || f == PeProductVersion || f == PePsChildFileName || f == PsChildPeFilename + return f == PeCompany || f == PeCopyright || f == PeDescription || f == PeFileName || f == PeFileVersion || f == PeProduct || f == PeProductVersion } func (f Field) IsPeVersionResources() bool { return f == PeResources } func (f Field) IsPeImphash() bool { return f == PeImphash } @@ -917,31 +861,8 @@ var fields = map[Field]FieldInfo{ PsAccessMask: {PsAccessMask, "process desired access rights", params.AnsiString, []string{"ps.access.mask = '0x1400'"}, nil, nil}, PsAccessMaskNames: {PsAccessMaskNames, "process desired access rights as a string list", params.Slice, []string{"ps.access.mask.names in ('SUSPEND_RESUME')"}, nil, nil}, PsAccessStatus: {PsAccessStatus, "process access status", params.UnicodeString, []string{"ps.access.status = 'access is denied.'"}, nil, nil}, - PsSiblingPid: {PsSiblingPid, "created or terminated process identifier", params.PID, []string{"ps.sibling.pid = 320"}, &Deprecation{Since: "1.10.0", Fields: []Field{PsChildPid}}, nil}, - PsChildPid: {PsChildPid, "created or terminated process identifier", params.PID, []string{"ps.child.pid = 320"}, nil, nil}, - PsSiblingName: {PsSiblingName, "created or terminated process name", params.UnicodeString, []string{"ps.sibling.name = 'notepad.exe'"}, &Deprecation{Since: "1.10.0", Fields: []Field{PsChildName}}, nil}, - PsChildName: {PsChildName, "created or terminated process name", params.UnicodeString, []string{"ps.child.name = 'notepad.exe'"}, nil, nil}, - PsSiblingComm: {PsSiblingComm, "created or terminated process command line", params.UnicodeString, []string{"ps.sibling.comm contains '\\k \\v'"}, &Deprecation{Since: "1.10.0", Fields: []Field{PsChildCmdline}}, nil}, - PsChildCmdline: {PsChildCmdline, "created or terminated process command line", params.UnicodeString, []string{"ps.child.cmdline contains '\\k \\v'"}, nil, nil}, - PsSiblingArgs: {PsSiblingArgs, "created process command line arguments", params.Slice, []string{"ps.sibling.args in ('/cdir', '/-C')"}, &Deprecation{Since: "1.10.0", Fields: []Field{PsChildArgs}}, nil}, - PsChildArgs: {PsChildArgs, "created process command line arguments", params.Slice, []string{"ps.child.args in ('/cdir', '/-C')"}, nil, nil}, - PsSiblingExe: {PsSiblingExe, "created, terminated, or opened process id", params.UnicodeString, []string{"ps.sibling.exe contains '\\Windows\\cmd.exe'"}, &Deprecation{Since: "1.10.0", Fields: []Field{PsChildExe}}, nil}, - PsChildExe: {PsChildExe, "created, terminated, or opened process id", params.UnicodeString, []string{"ps.child.exe contains '\\Windows\\cmd.exe'"}, nil, nil}, - PsSiblingSID: {PsSiblingSID, "created or terminated process security identifier", params.UnicodeString, []string{"ps.sibling.sid contains 'SERVICE'"}, &Deprecation{Since: "1.10.0", Fields: []Field{PsChildSID}}, nil}, - PsChildSID: {PsChildSID, "created or terminated process security identifier", params.UnicodeString, []string{"ps.child.sid contains 'SERVICE'"}, nil, nil}, - PsSiblingSessionID: {PsSiblingSessionID, "created or terminated process session identifier", params.Int16, []string{"ps.sibling.sessionid == 1"}, &Deprecation{Since: "1.10.0", Fields: []Field{PsChildSessionID}}, nil}, - PsChildSessionID: {PsChildSessionID, "created or terminated process session identifier", params.Int16, []string{"ps.child.sessionid == 1"}, nil, nil}, - PsSiblingDomain: {PsSiblingDomain, "created or terminated process domain", params.UnicodeString, []string{"ps.sibling.domain contains 'SERVICE'"}, &Deprecation{Since: "1.10.0", Fields: []Field{PsChildDomain}}, nil}, - PsChildDomain: {PsChildDomain, "created or terminated process domain", params.UnicodeString, []string{"ps.child.domain contains 'SERVICE'"}, nil, nil}, - PsSiblingUsername: {PsSiblingUsername, "created or terminated process username", params.UnicodeString, []string{"ps.sibling.username contains 'system'"}, &Deprecation{Since: "1.10.0", Fields: []Field{PsChildUsername}}, nil}, - PsChildUsername: {PsChildUsername, "created or terminated process username", params.UnicodeString, []string{"ps.child.username contains 'system'"}, nil, nil}, PsUUID: {PsUUID, "unique process identifier", params.Uint64, []string{"ps.uuid > 6000054355"}, nil, nil}, PsParentUUID: {PsParentUUID, "unique parent process identifier", params.Uint64, []string{"ps.parent.uuid > 6000054355"}, nil, nil}, - PsChildUUID: {PsChildUUID, "unique child process identifier", params.Uint64, []string{"ps.child.uuid > 6000054355"}, nil, nil}, - PsChildPeFilename: {PsChildPeFilename, "original file name of the child process executable supplied at compile-time", params.UnicodeString, []string{"ps.child.pe.file.name = 'NOTEPAD.EXE'"}, nil, nil}, - PsChildIsWOW64Field: {PsChildIsWOW64Field, "indicates if the 32-bit child process is created in 64-bit Windows system", params.Bool, []string{"ps.child.is_wow64"}, nil, nil}, - PsChildIsPackagedField: {PsChildIsPackagedField, "indicates if the child process is packaged with the MSIX technology", params.Bool, []string{"ps.child.is_packaged"}, nil, nil}, - PsChildIsProtectedField: {PsChildIsProtectedField, "indicates if the child process is a protected process", params.Bool, []string{"ps.child.is_protected"}, nil, nil}, PsIsWOW64Field: {PsIsWOW64Field, "indicates if the process generating the event is a 32-bit process created in 64-bit Windows system", params.Bool, []string{"ps.is_wow64"}, nil, nil}, PsIsPackagedField: {PsIsPackagedField, "indicates if the process generating the event is packaged with the MSIX technology", params.Bool, []string{"ps.is_packaged"}, nil, nil}, PsIsProtectedField: {PsIsProtectedField, "indicates if the process generating the event is a protected process", params.Bool, []string{"ps.is_protected"}, nil, nil}, @@ -952,9 +873,6 @@ var fields = map[Field]FieldInfo{ PsTokenIntegrityLevel: {PsTokenIntegrityLevel, "process token integrity level", params.UnicodeString, []string{"ps.token.integrity_level = 'SYSTEM'"}, nil, nil}, PsTokenIsElevated: {PsTokenIsElevated, "indicates if the process token is elevated", params.Bool, []string{"ps.token.is_elevated = true"}, nil, nil}, PsTokenElevationType: {PsTokenElevationType, "process token elevation type", params.AnsiString, []string{"ps.token.elevation_type = 'LIMITED'"}, nil, nil}, - PsChildTokenIntegrityLevel: {PsChildTokenIntegrityLevel, "child process token integrity level", params.UnicodeString, []string{"ps.child.token.integrity_level = 'SYSTEM'"}, nil, nil}, - PsChildTokenIsElevated: {PsChildTokenIsElevated, "indicates if the child process token is elevated", params.Bool, []string{"ps.child.token.is_elevated = true"}, nil, nil}, - PsChildTokenElevationType: {PsChildTokenElevationType, "child process token elevation type", params.AnsiString, []string{"ps.child.token.elevation_type = 'LIMITED'"}, nil, nil}, PsParentTokenIntegrityLevel: {PsParentTokenIntegrityLevel, "parent process token integrity level", params.UnicodeString, []string{"ps.parent.token.integrity_level = 'HIGH'"}, nil, nil}, PsParentTokenIsElevated: {PsParentTokenIsElevated, "indicates if the parent process token is elevated", params.Bool, []string{"ps.parent.token.is_elevated = true"}, nil, nil}, PsParentTokenElevationType: {PsParentTokenElevationType, "parent process token elevation type", params.AnsiString, []string{"ps.parent.token.elevation_type = 'LIMITED'"}, nil, nil}, @@ -1089,28 +1007,27 @@ var fields = map[Field]FieldInfo{ return true }}}, - PeCompany: {PeCompany, "internal company name of the file provided at compile-time", params.UnicodeString, []string{"pe.company = 'Microsoft Corporation'"}, nil, nil}, - PeCopyright: {PeCopyright, "copyright notice for the file emitted at compile-time", params.UnicodeString, []string{"pe.copyright = '© Microsoft Corporation'"}, nil, nil}, - PeDescription: {PeDescription, "internal description of the file provided at compile-time", params.UnicodeString, []string{"pe.description = 'Notepad'"}, nil, nil}, - PeFileName: {PeFileName, "original file name supplied at compile-time", params.UnicodeString, []string{"pe.file.name = 'NOTEPAD.EXE'"}, nil, nil}, - PeFileVersion: {PeFileVersion, "file version supplied at compile-time", params.UnicodeString, []string{"pe.file.version = '10.0.18362.693 (WinBuild.160101.0800)'"}, nil, nil}, - PeProduct: {PeProduct, "internal product name of the file provided at compile-time", params.UnicodeString, []string{"pe.product = 'Microsoft® Windows® Operating System'"}, nil, nil}, - PeProductVersion: {PeProductVersion, "internal product version of the file provided at compile-time", params.UnicodeString, []string{"pe.product.version = '10.0.18362.693'"}, nil, nil}, - PeIsDLL: {PeIsDLL, "indicates if the loaded image or created file is a DLL", params.Bool, []string{"pe.is_dll'"}, &Deprecation{Since: "2.0.0", Fields: []Field{FileIsDLL, ImageIsDLL}}, nil}, - PeIsDriver: {PeIsDriver, "indicates if the loaded image or created file is a driver", params.Bool, []string{"pe.is_driver'"}, &Deprecation{Since: "2.0.0", Fields: []Field{FileIsDriver, ImageIsDriver}}, nil}, - PeIsExecutable: {PeIsExecutable, "indicates if the loaded image or created file is an executable", params.Bool, []string{"pe.is_exec'"}, &Deprecation{Since: "2.0.0", Fields: []Field{FileIsExecutable, ImageIsExecutable}}, nil}, - PeImphash: {PeImphash, "import hash", params.AnsiString, []string{"pe.impash = '5d3861c5c547f8a34e471ba273a732b2'"}, nil, nil}, - PeIsDotnet: {PeIsDotnet, "indicates if PE contains CLR data", params.Bool, []string{"pe.is_dotnet"}, nil, nil}, - PeAnomalies: {PeAnomalies, "contains PE anomalies detected during parsing", params.Slice, []string{"pe.anomalies in ('number of sections is 0')"}, nil, nil}, - PeIsSigned: {PeIsSigned, "indicates if the PE has embedded or catalog signature", params.Bool, []string{"pe.is_signed"}, nil, nil}, - PeIsTrusted: {PeIsTrusted, "indicates if the PE certificate chain is trusted", params.Bool, []string{"pe.is_trusted"}, nil, nil}, - PeCertSerial: {PeCertSerial, "PE certificate serial number", params.UnicodeString, []string{"pe.cert.serial = '330000023241fb59996dcc4dff000000000232'"}, nil, nil}, - PeCertSubject: {PeCertSubject, "PE certificate subject", params.UnicodeString, []string{"pe.cert.subject contains 'Washington, Redmond, Microsoft Corporation'"}, nil, nil}, - PeCertIssuer: {PeCertIssuer, "PE certificate CA", params.UnicodeString, []string{"pe.cert.issuer contains 'Washington, Redmond, Microsoft Corporation'"}, nil, nil}, - PeCertAfter: {PeCertAfter, "PE certificate expiration date", params.Time, []string{"pe.cert.after contains '2024-02-01 00:05:42 +0000 UTC'"}, nil, nil}, - PeCertBefore: {PeCertBefore, "PE certificate enrollment date", params.Time, []string{"pe.cert.before contains '2024-02-01 00:05:42 +0000 UTC'"}, nil, nil}, - PeIsModified: {PeIsModified, "indicates if disk and in-memory PE headers differ", params.Bool, []string{"pe.is_modified"}, nil, nil}, - PePsChildFileName: {PePsChildFileName, "original file name of the child process executable supplied at compile-time", params.UnicodeString, []string{"pe.ps.child.file.name = 'NOTEPAD.EXE'"}, &Deprecation{Since: "2.3.0", Fields: []Field{PsChildPeFilename}}, nil}, + PeCompany: {PeCompany, "internal company name of the file provided at compile-time", params.UnicodeString, []string{"pe.company = 'Microsoft Corporation'"}, nil, nil}, + PeCopyright: {PeCopyright, "copyright notice for the file emitted at compile-time", params.UnicodeString, []string{"pe.copyright = '© Microsoft Corporation'"}, nil, nil}, + PeDescription: {PeDescription, "internal description of the file provided at compile-time", params.UnicodeString, []string{"pe.description = 'Notepad'"}, nil, nil}, + PeFileName: {PeFileName, "original file name supplied at compile-time", params.UnicodeString, []string{"pe.file.name = 'NOTEPAD.EXE'"}, nil, nil}, + PeFileVersion: {PeFileVersion, "file version supplied at compile-time", params.UnicodeString, []string{"pe.file.version = '10.0.18362.693 (WinBuild.160101.0800)'"}, nil, nil}, + PeProduct: {PeProduct, "internal product name of the file provided at compile-time", params.UnicodeString, []string{"pe.product = 'Microsoft® Windows® Operating System'"}, nil, nil}, + PeProductVersion: {PeProductVersion, "internal product version of the file provided at compile-time", params.UnicodeString, []string{"pe.product.version = '10.0.18362.693'"}, nil, nil}, + PeIsDLL: {PeIsDLL, "indicates if the loaded image or created file is a DLL", params.Bool, []string{"pe.is_dll'"}, &Deprecation{Since: "2.0.0", Fields: []Field{FileIsDLL, ImageIsDLL}}, nil}, + PeIsDriver: {PeIsDriver, "indicates if the loaded image or created file is a driver", params.Bool, []string{"pe.is_driver'"}, &Deprecation{Since: "2.0.0", Fields: []Field{FileIsDriver, ImageIsDriver}}, nil}, + PeIsExecutable: {PeIsExecutable, "indicates if the loaded image or created file is an executable", params.Bool, []string{"pe.is_exec'"}, &Deprecation{Since: "2.0.0", Fields: []Field{FileIsExecutable, ImageIsExecutable}}, nil}, + PeImphash: {PeImphash, "import hash", params.AnsiString, []string{"pe.impash = '5d3861c5c547f8a34e471ba273a732b2'"}, nil, nil}, + PeIsDotnet: {PeIsDotnet, "indicates if PE contains CLR data", params.Bool, []string{"pe.is_dotnet"}, nil, nil}, + PeAnomalies: {PeAnomalies, "contains PE anomalies detected during parsing", params.Slice, []string{"pe.anomalies in ('number of sections is 0')"}, nil, nil}, + PeIsSigned: {PeIsSigned, "indicates if the PE has embedded or catalog signature", params.Bool, []string{"pe.is_signed"}, nil, nil}, + PeIsTrusted: {PeIsTrusted, "indicates if the PE certificate chain is trusted", params.Bool, []string{"pe.is_trusted"}, nil, nil}, + PeCertSerial: {PeCertSerial, "PE certificate serial number", params.UnicodeString, []string{"pe.cert.serial = '330000023241fb59996dcc4dff000000000232'"}, nil, nil}, + PeCertSubject: {PeCertSubject, "PE certificate subject", params.UnicodeString, []string{"pe.cert.subject contains 'Washington, Redmond, Microsoft Corporation'"}, nil, nil}, + PeCertIssuer: {PeCertIssuer, "PE certificate CA", params.UnicodeString, []string{"pe.cert.issuer contains 'Washington, Redmond, Microsoft Corporation'"}, nil, nil}, + PeCertAfter: {PeCertAfter, "PE certificate expiration date", params.Time, []string{"pe.cert.after contains '2024-02-01 00:05:42 +0000 UTC'"}, nil, nil}, + PeCertBefore: {PeCertBefore, "PE certificate enrollment date", params.Time, []string{"pe.cert.before contains '2024-02-01 00:05:42 +0000 UTC'"}, nil, nil}, + PeIsModified: {PeIsModified, "indicates if disk and in-memory PE headers differ", params.Bool, []string{"pe.is_modified"}, nil, nil}, MemBaseAddress: {MemBaseAddress, "region base address", params.Address, []string{"mem.address = '211d13f2000'"}, nil, nil}, MemRegionSize: {MemRegionSize, "region size", params.Uint64, []string{"mem.size > 438272"}, nil, nil}, diff --git a/pkg/filter/fields/fields_windows_test.go b/pkg/filter/fields/fields_windows_test.go index af082f1f4..b25ff2dab 100644 --- a/pkg/filter/fields/fields_windows_test.go +++ b/pkg/filter/fields/fields_windows_test.go @@ -19,8 +19,9 @@ package fields import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestIsField(t *testing.T) { @@ -44,7 +45,7 @@ func TestIsField(t *testing.T) { } func TestIsDeprecated(t *testing.T) { - deprecated, d := IsDeprecated(PsSiblingPid) + deprecated, d := IsDeprecated(KevtArg) assert.True(t, deprecated) assert.NotNil(t, d) } diff --git a/pkg/filter/filter_test.go b/pkg/filter/filter_test.go index 8e6af3833..9d17d7b87 100644 --- a/pkg/filter/filter_test.go +++ b/pkg/filter/filter_test.go @@ -111,8 +111,9 @@ func TestStringFields(t *testing.T) { } func TestProcFilter(t *testing.T) { - ps1 := &pstypes.PS{ - Name: "wininit.exe", + parent := &pstypes.PS{ + Name: "svchost.exe", + Cmdline: "C:\\Windows\\system32\\svchost.exe -k RPCSS", Username: "SYSTEM", Domain: "NT AUTHORITY", SID: "S-1-5-18", @@ -122,7 +123,7 @@ func TestProcFilter(t *testing.T) { SID: "S-1-5-8", PID: 2034, Parent: &pstypes.PS{ - Name: "System", + Name: "csrss.exe", }, }, IsWOW64: false, @@ -137,24 +138,25 @@ func TestProcFilter(t *testing.T) { Type: event.CreateProcess, Category: event.Process, Params: event.Params{ - params.Cmdline: {Name: params.Cmdline, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\svchost-fake.exe -k RPCSS"}, - params.ProcessName: {Name: params.ProcessName, Type: params.AnsiString, Value: "svchost-fake.exe"}, + params.Cmdline: {Name: params.Cmdline, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\svchost.exe -k DcomLaunch -p -s LSM"}, + params.ProcessName: {Name: params.ProcessName, Type: params.AnsiString, Value: "svchost.exe"}, params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(1234)}, params.ProcessParentID: {Name: params.ProcessParentID, Type: params.PID, Value: uint32(345)}, params.UserSID: {Name: params.UserSID, Type: params.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}}, - params.Username: {Name: params.Username, Type: params.UnicodeString, Value: "loki"}, - params.Domain: {Name: params.Domain, Type: params.UnicodeString, Value: "TITAN"}, + params.Username: {Name: params.Username, Type: params.UnicodeString, Value: "SYSTEM"}, + params.Domain: {Name: params.Domain, Type: params.UnicodeString, Value: "NT AUTHORITY"}, params.ProcessFlags: {Name: params.ProcessFlags, Type: params.Flags, Value: uint32(0x000000E)}, params.ProcessTokenIntegrityLevel: {Name: params.ProcessTokenIntegrityLevel, Type: params.AnsiString, Value: "SYSTEM"}, params.ProcessTokenIsElevated: {Name: params.ProcessTokenIsElevated, Type: params.Bool, Value: true}, - params.ProcessTokenElevationType: {Name: params.ProcessTokenElevationType, Type: params.AnsiString, Value: "FULL"}, + params.ProcessTokenElevationType: {Name: params.ProcessTokenElevationType, Type: params.AnsiString, Value: "DEFAULT"}, }, Name: "CreateProcess", - PID: 1023, + PID: 1234, PS: &pstypes.PS{ Name: "svchost.exe", - Cmdline: "C:\\Windows\\System32\\svchost.exe", - Parent: ps1, + Cmdline: "C:\\Windows\\System32\\svchost.exe -k DcomLaunch -p -s LSM", + Parent: parent, + PID: 1234, Ppid: 345, Username: "SYSTEM", Domain: "NT AUTHORITY", @@ -177,7 +179,7 @@ func TestProcFilter(t *testing.T) { IsPackaged: true, IsWOW64: false, TokenIntegrityLevel: "SYSTEM", - IsTokenElevated: false, + IsTokenElevated: true, TokenElevationType: "DEFAULT", }, } @@ -193,7 +195,7 @@ func TestProcFilter(t *testing.T) { PID: 1023, PS: &pstypes.PS{ Name: "svchost.exe", - Parent: ps1, + Parent: parent, Ppid: 345, Envs: map[string]string{"ALLUSERSPROFILE": "C:\\ProgramData", "OS": "Windows_NT", "ProgramFiles(x86)": "C:\\Program Files (x86)"}, Modules: []pstypes.Module{ @@ -220,25 +222,18 @@ func TestProcFilter(t *testing.T) { {`ps.name = 'svchost.exe'`, true}, {`ps.name = 'svchot.exe'`, false}, - {`ps.name = 'mimikatz.exe' or ps.name contains 'svc'`, true}, + {`ps.name = 'csrss.exe' or ps.name contains 'svc'`, true}, {`ps.name ~= 'SVCHOST.exe'`, true}, - {`ps.cmdline = 'C:\\Windows\\System32\\svchost.exe'`, true}, - {`ps.child.cmdline = 'C:\\Windows\\system32\\svchost-fake.exe -k RPCSS'`, true}, + {`ps.parent.cmdline = 'C:\\Windows\\system32\\svchost.exe -k RPCSS'`, true}, + {`ps.cmdline = 'C:\\Windows\\System32\\svchost.exe -k DcomLaunch -p -s LSM'`, true}, {`ps.username = 'SYSTEM'`, true}, {`ps.domain = 'NT AUTHORITY'`, true}, {`ps.sid = 'S-1-5-18'`, true}, - {`ps.pid = 1023`, true}, - {`ps.child.sid = 'S-1-5-18'`, true}, - {`ps.sibling.pid = 1234`, true}, - {`ps.child.pid = 1234`, true}, - {`ps.child.uuid > 0`, true}, + {`ps.pid = 1234`, true}, + {`ps.parent.sid = 'S-1-5-18'`, true}, + {`ps.uuid > 0`, true}, + {`ps.parent.name = 'svchost.exe'`, true}, {`ps.parent.pid = 5042`, true}, - {`ps.sibling.name = 'svchost-fake.exe'`, true}, - {`ps.child.name = 'svchost-fake.exe'`, true}, - {`ps.sibling.username = 'loki'`, true}, - {`ps.child.username = 'loki'`, true}, - {`ps.sibling.domain = 'TITAN'`, true}, - {`ps.child.domain = 'TITAN'`, true}, {`ps.parent.username = 'SYSTEM'`, true}, {`ps.parent.domain = 'NT AUTHORITY'`, true}, {`ps.envs[ALLUSERSPROFILE] = 'C:\\ProgramData'`, true}, @@ -248,9 +243,6 @@ func TestProcFilter(t *testing.T) { {`ps.envs in ('ALLUSERSPROFILE:C:\\ProgramData')`, true}, {`foreach(ps.envs, $env, substr($env, 0, indexof($env, ':')) = 'OS')`, true}, - {`ps.child.is_wow64`, true}, - {`ps.child.is_packaged`, true}, - {`ps.child.is_protected`, true}, {`ps.is_wow64`, false}, {`ps.is_packaged`, true}, {`ps.is_protected`, false}, @@ -258,11 +250,10 @@ func TestProcFilter(t *testing.T) { {`ps.parent.is_packaged`, false}, {`ps.parent.is_protected`, true}, {`ps.token.integrity_level = 'SYSTEM'`, true}, - {`ps.token.is_elevated = false`, true}, + {`ps.token.is_elevated = true`, true}, {`ps.token.elevation_type = 'DEFAULT'`, true}, - {`ps.child.token.integrity_level = 'SYSTEM'`, true}, - {`ps.child.token.is_elevated = true`, true}, - {`ps.child.token.elevation_type = 'FULL'`, true}, + {`ps.token.integrity_level = 'SYSTEM'`, true}, + {`ps.token.is_elevated = true`, true}, {`ps.parent.token.integrity_level = 'SYSTEM'`, true}, {`ps.parent.token.is_elevated = false`, true}, {`ps.parent.token.elevation_type = 'DEFAULT'`, true}, @@ -271,17 +262,17 @@ func TestProcFilter(t *testing.T) { {`ps.modules IN ('kernel32.dll')`, true}, {`evt.name = 'CreateProcess' and evt.pid != ps.ppid`, true}, - {`ps.parent.name = 'wininit.exe'`, true}, + {`ps.parent.name = 'svchost.exe'`, true}, - {`ps.ancestor[0] = 'svchost.exe'`, false}, - {`ps.ancestor[0] = 'wininit.exe'`, true}, + {`ps.ancestor[0] = 'svchost.exe'`, true}, + {`ps.ancestor[0] = 'csrss.exe'`, false}, {`ps.ancestor[1] = 'services.exe'`, true}, - {`ps.ancestor[2] = 'System'`, true}, + {`ps.ancestor[2] = 'csrss.exe'`, true}, {`ps.ancestor[3] = ''`, true}, - {`ps.ancestor intersects ('wininit.exe', 'services.exe', 'System')`, true}, + {`ps.ancestor intersects ('csrss.exe', 'services.exe', 'svchost.exe')`, true}, - {`foreach(ps._ancestors, $proc, $proc.name in ('wininit.exe', 'services.exe', 'System'))`, true}, - {`foreach(ps._ancestors, $proc, $proc.name in ('wininit.exe', 'services.exe', 'System') and ps.is_packaged, ps.is_packaged)`, true}, + {`foreach(ps._ancestors, $proc, $proc.name in ('csrss.exe', 'services.exe', 'System'))`, true}, + {`foreach(ps._ancestors, $proc, $proc.name in ('csrss.exe', 'services.exe', 'System') and ps.is_packaged, ps.is_packaged)`, true}, {`foreach(ps._ancestors, $proc, $proc.name not in ('svchost.exe', 'WmiPrvSE.exe'))`, true}, {`foreach(ps._ancestors, $proc, $proc.sid = 'S-1-5-8'))`, true}, {`foreach(ps._ancestors, $proc, $proc.name endswith 'ices.exe'))`, true}, @@ -318,7 +309,7 @@ func TestProcFilter(t *testing.T) { } psnap := new(ps.SnapshotterMock) - psnap.On("FindAndPut", uint32(1234)).Return(ps1) + psnap.On("FindAndPut", uint32(1234)).Return(parent) for i, tt := range tests { f := New(tt.filter, cfg, WithPSnapshotter(psnap)) diff --git a/pkg/filter/util.go b/pkg/filter/util.go index 04dc37dd3..5f2a89f94 100644 --- a/pkg/filter/util.go +++ b/pkg/filter/util.go @@ -19,13 +19,14 @@ package filter import ( + "path/filepath" + "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/rabbitstack/fibratus/pkg/filter/fields" "github.com/rabbitstack/fibratus/pkg/util/loldrivers" "github.com/rabbitstack/fibratus/pkg/util/signature" "github.com/rabbitstack/fibratus/pkg/util/va" - "path/filepath" ) // isLOLDriver interacts with the loldrivers client to determine @@ -104,3 +105,11 @@ func getSignature(addr va.Address, filename string, parseCert bool) *signature.S return sign } + +// framePID returns the pid associated with the stack frame. +func framePID(e *event.Event) uint32 { + if !e.Callstack.IsEmpty() && e.Callstack.FrameAt(0).PID != 0 { + return e.Callstack.FrameAt(0).PID + } + return e.PID +} diff --git a/pkg/ps/snapshotter_windows.go b/pkg/ps/snapshotter_windows.go index 68801b5ac..74820a1a4 100644 --- a/pkg/ps/snapshotter_windows.go +++ b/pkg/ps/snapshotter_windows.go @@ -20,15 +20,16 @@ package ps import ( "expvar" - "github.com/rabbitstack/fibratus/pkg/sys" - "github.com/rabbitstack/fibratus/pkg/util/va" - "golang.org/x/sys/windows" "path/filepath" "strconv" "strings" "sync" "time" + "github.com/rabbitstack/fibratus/pkg/sys" + "github.com/rabbitstack/fibratus/pkg/util/va" + "golang.org/x/sys/windows" + "github.com/rabbitstack/fibratus/pkg/config" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" @@ -195,18 +196,8 @@ func (s *snapshotter) Write(e *event.Event) error { s.procs[pid] = proc } - // adjust the process which is generating - // the event. For `CreateProcess` events - // the process context is scoped to the - // parent/creator process. Otherwise, it - // is a regular rundown event that doesn't - // require consulting the process in the - // snapshot state - if e.IsProcessRundown() { - e.PS = proc - } else if !e.IsProcessRundownInternal() && !e.IsCreateProcessInternal() { - e.PS = s.procs[e.PID] - } + // assign process state to the event + e.PS = proc return err } diff --git a/pkg/ps/snapshotter_windows_test.go b/pkg/ps/snapshotter_windows_test.go index 254c76d2f..a868e6afe 100644 --- a/pkg/ps/snapshotter_windows_test.go +++ b/pkg/ps/snapshotter_windows_test.go @@ -19,6 +19,12 @@ package ps import ( + "os" + "path/filepath" + "strings" + "testing" + "time" + "github.com/rabbitstack/fibratus/pkg/config" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" @@ -31,11 +37,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "golang.org/x/sys/windows" - "os" - "path/filepath" - "strings" - "testing" - "time" ) func TestWrite(t *testing.T) { @@ -175,11 +176,7 @@ func TestWrite(t *testing.T) { assert.True(t, (ps.Parent != nil) == (proc.Parent != nil)) if found, _ := psnap.Find(evt.PID); found { assert.NotNil(t, evt.PS) - if evt.IsProcessRundown() { - assert.Equal(t, ps.PID, evt.PS.PID) - } else { - assert.Equal(t, ps.Ppid, evt.PS.PID) - } + assert.Equal(t, ps.PID, evt.PS.PID) } }) } diff --git a/pkg/rules/_fixtures/kill_action.yml b/pkg/rules/_fixtures/kill_action.yml index a00879d0e..539bc1cb2 100644 --- a/pkg/rules/_fixtures/kill_action.yml +++ b/pkg/rules/_fixtures/kill_action.yml @@ -1,7 +1,7 @@ name: Kill calc.exe process id: 172902be-76e9-4ee7-a48a-6275fa571cf4 version: 1.0.0 -condition: evt.name = 'CreateProcess' and ps.child.name = 'calc.exe' +condition: evt.name = 'CreateProcess' and ps.name = 'calc.exe' severity: critical action: - name: kill diff --git a/pkg/rules/_fixtures/sequence_rule_complex.yml b/pkg/rules/_fixtures/sequence_rule_complex.yml index 1a3dad8c2..41b1188c2 100644 --- a/pkg/rules/_fixtures/sequence_rule_complex.yml +++ b/pkg/rules/_fixtures/sequence_rule_complex.yml @@ -4,11 +4,10 @@ version: 1.0.0 condition: > sequence maxspan 1h - |evt.name = 'CreateProcess' and ps.sibling.name + |evt.name = 'CreateProcess' and ps.name in ('firefox.exe', 'chrome.exe', 'edge.exe') - | by ps.sibling.pid - + | by ps.pid |evt.name = 'CreateFile' and file.operation = 'CREATE' and file.extension = '.exe' diff --git a/pkg/rules/_fixtures/sequence_rule_ps_uuid.yml b/pkg/rules/_fixtures/sequence_rule_ps_uuid.yml index f1fb655d0..f0371c976 100644 --- a/pkg/rules/_fixtures/sequence_rule_ps_uuid.yml +++ b/pkg/rules/_fixtures/sequence_rule_ps_uuid.yml @@ -5,12 +5,12 @@ condition: > sequence maxspan 1h by ps.uuid - |evt.name = 'CreateProcess' and ps.child.name - in - ('firefox.exe', 'chrome.exe', 'edge.exe') - | - |evt.name = 'CreateFile' and file.operation = 'CREATE' - and - file.extension = '.exe' - | + |evt.name = 'CreateProcess' and ps.name + in + ('firefox.exe', 'chrome.exe', 'edge.exe') + | + |evt.name = 'CreateFile' and file.operation = 'CREATE' + and + file.extension = '.exe' + | min-engine-version: 2.0.0 diff --git a/pkg/rules/_fixtures/simple_and_sequence_rules/command_shell_spawned_chrome_browser.yml b/pkg/rules/_fixtures/simple_and_sequence_rules/command_shell_spawned_chrome_browser.yml index 45a501e88..be5a90a09 100644 --- a/pkg/rules/_fixtures/simple_and_sequence_rules/command_shell_spawned_chrome_browser.yml +++ b/pkg/rules/_fixtures/simple_and_sequence_rules/command_shell_spawned_chrome_browser.yml @@ -4,5 +4,5 @@ version: 1.0.0 condition: > sequence maxspan 1s |evt.name = 'CreateProcess' and ps.name = 'powershell.exe'| by ps.pid - |evt.name = 'CreateProcess' and ps.sibling.name = 'chrome.exe'| by ps.pid + |evt.name = 'CreateProcess' and ps.name = 'chrome.exe'| by ps.pid min-engine-version: 2.0.0 diff --git a/pkg/rules/_fixtures/simple_and_sequence_rules/process_spawned_by_powershell.yml b/pkg/rules/_fixtures/simple_and_sequence_rules/powershell_process_spawned.yml similarity index 81% rename from pkg/rules/_fixtures/simple_and_sequence_rules/process_spawned_by_powershell.yml rename to pkg/rules/_fixtures/simple_and_sequence_rules/powershell_process_spawned.yml index 2ded698bd..8e0381902 100644 --- a/pkg/rules/_fixtures/simple_and_sequence_rules/process_spawned_by_powershell.yml +++ b/pkg/rules/_fixtures/simple_and_sequence_rules/powershell_process_spawned.yml @@ -1,4 +1,4 @@ -name: Process spawned by powershell +name: Powershell process spawned id: 4155539d-31bd-429e-81f9-c17ee1c01f93 version: 1.0.0 condition: > diff --git a/pkg/rules/_fixtures/simple_and_sequence_rules/spawn_chrome_browser.yml b/pkg/rules/_fixtures/simple_and_sequence_rules/spawn_chrome_browser.yml index 02a91e9ee..56808e2a7 100644 --- a/pkg/rules/_fixtures/simple_and_sequence_rules/spawn_chrome_browser.yml +++ b/pkg/rules/_fixtures/simple_and_sequence_rules/spawn_chrome_browser.yml @@ -2,5 +2,5 @@ name: Spawn Chrome browser id: 5155539d-31bd-429e-81f9-c17ee1c01f93 version: 1.0.0 condition: > - evt.name = 'CreateProcess' and ps.sibling.name = 'chrome.exe' + evt.name = 'CreateProcess' and ps.name = 'chrome.exe' min-engine-version: 2.0.0 diff --git a/pkg/rules/engine_test.go b/pkg/rules/engine_test.go index 80c269d9b..9cc32dac1 100644 --- a/pkg/rules/engine_test.go +++ b/pkg/rules/engine_test.go @@ -19,6 +19,11 @@ package rules import ( + "net" + "os" + "testing" + "time" + "github.com/rabbitstack/fibratus/pkg/alertsender" "github.com/rabbitstack/fibratus/pkg/config" "github.com/rabbitstack/fibratus/pkg/event" @@ -31,10 +36,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sys/windows" - "net" - "os" - "testing" - "time" ) type mockNoopSender struct{} @@ -192,10 +193,10 @@ func TestRunSequenceRule(t *testing.T) { Category: event.Process, Name: "CreateProcess", Tid: 2484, - PID: 859, + PID: 2243, PS: &types.PS{ - Name: "explorer.exe", - Exe: "C:\\Windows\\system32\\explorer.exe", + Name: "firefox.exe", + Exe: "C:\\Program Files\\Firefox\\firefox.exe", }, Params: event.Params{ params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(2243)}, @@ -281,15 +282,15 @@ func TestRunSequenceRuleWithPsUUIDLink(t *testing.T) { Timestamp: time.Now(), Category: event.Process, Name: "CreateProcess", - Tid: 2484, + Tid: 2243, PID: uint32(os.Getpid()), PS: &types.PS{ PID: uint32(os.Getpid()), - Name: "explorer.exe", - Exe: "C:\\Windows\\system32\\explorer.exe", + Name: "firefox.exe", + Exe: "C:\\Program Files\\Firefox\\firefox.exe", }, Params: event.Params{ - params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(2243)}, + params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(os.Getpid())}, params.ProcessName: {Name: params.ProcessName, Type: params.UnicodeString, Value: "firefox.exe"}, }, Metadata: map[event.MetadataKey]any{"foo": "bar", "fooz": "barzz"}, @@ -352,7 +353,7 @@ func TestRunSimpleAndSequenceRules(t *testing.T) { }, Params: event.Params{ params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(2243)}, - params.ProcessName: {Name: params.ProcessName, Type: params.UnicodeString, Value: "firefox.exe"}, + params.ProcessName: {Name: params.ProcessName, Type: params.UnicodeString, Value: "powershell.exe"}, }, Metadata: map[event.MetadataKey]any{"foo": "bar", "fooz": "barzz"}, }, @@ -383,8 +384,12 @@ func TestRunSimpleAndSequenceRules(t *testing.T) { Tid: 2484, PID: 2243, PS: &types.PS{ - Name: "cmd.exe", - Exe: "C:\\Windows\\system32\\cmd.exe", + Name: "chrome.exe", + Exe: "C:\\Program Files\\Chrome\\chrome.exe", + Parent: &types.PS{ + Name: "cmd.exe", + Exe: "C:\\Windows\\system32\\cmd.exe", + }, }, Params: event.Params{ params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(2243)}, @@ -404,7 +409,7 @@ func TestRunSimpleAndSequenceRules(t *testing.T) { rule string eventIDs []uint64 }{ - {"Process spawned by powershell", []uint64{1}}, + {"Powershell process spawned", []uint64{1}}, {"Powershell created a temp file", []uint64{1, 2}}, {"Spawn Chrome browser", []uint64{10}}, {"Command shell spawned Chrome browser", []uint64{1, 10}}, @@ -487,11 +492,11 @@ func TestKillAction(t *testing.T) { Timestamp: time.Now(), Name: "CreateProcess", Tid: 2484, - PID: 859, + PID: pi.ProcessId, Category: event.Process, PS: &types.PS{ - Name: "cmd.exe", - Exe: "C:\\Windows\\system32\\svchost-temp.exe", + Name: "calc.exe", + Exe: "C:\\Windows\\system32\\calc.exe", }, Params: event.Params{ params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: pi.ProcessId}, diff --git a/pkg/rules/sequence_test.go b/pkg/rules/sequence_test.go index 1f300ae0e..3d47d610c 100644 --- a/pkg/rules/sequence_test.go +++ b/pkg/rules/sequence_test.go @@ -19,6 +19,11 @@ package rules import ( + "net" + "strconv" + "testing" + "time" + "github.com/rabbitstack/fibratus/pkg/config" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" @@ -30,10 +35,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sys/windows/registry" - "net" - "strconv" - "testing" - "time" ) func TestSequenceState(t *testing.T) { @@ -45,7 +46,7 @@ func TestSequenceState(t *testing.T) { maxspan 100ms |evt.name = 'CreateProcess' and ps.name = 'cmd.exe'| by ps.exe |evt.name = 'CreateFile' and file.path icontains 'temp'| by file.path - |evt.name = 'CreateProcess'| by ps.child.exe`, + |evt.name = 'CreateProcess'| by ps.exe`, &config.Config{EventSource: config.EventSourceConfig{}, Filters: &config.Filters{}}) require.NoError(t, f.Compile()) @@ -285,13 +286,13 @@ func TestSimpleSequenceMultiplePartials(t *testing.T) { Timestamp: time.Now().Add(time.Duration(i) * time.Millisecond), Name: "CreateProcess", Tid: 2484, - PID: pid, + PID: pid % 2, PS: &pstypes.PS{ Name: "cmd.exe", Exe: "C:\\Windows\\system32\\cmd.exe", }, Params: event.Params{ - params.ProcessID: {Name: params.ProcessID, Type: params.Uint32, Value: pid % 2}, + params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: pid % 2}, }, Metadata: map[event.MetadataKey]any{"foo": "bar", "fooz": "barzz"}, } @@ -329,12 +330,13 @@ func TestSimpleSequenceMultiplePartials(t *testing.T) { PS: &pstypes.PS{ Name: "cmd.exe", Exe: "C:\\Windows\\System32\\cmd.exe", + PID: 859, Parent: &pstypes.PS{ Name: "WmiPrvSE.exe", }, }, Params: event.Params{ - params.ProcessID: {Name: params.ProcessID, Type: params.Uint32, Value: uint32(4143)}, + params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(859)}, }, Metadata: map[event.MetadataKey]any{"foo": "bar", "fooz": "barzz"}, } @@ -348,7 +350,8 @@ func TestSimpleSequenceMultiplePartials(t *testing.T) { Category: event.File, PS: &pstypes.PS{ Name: "cmd.exe", - Exe: "C:\\Windows\\system32\\svchost.exe", + Exe: "C:\\Windows\\system32\\cmd.exe", + PID: 859, }, Params: event.Params{ params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Temp\\file.tmp"}, @@ -357,7 +360,7 @@ func TestSimpleSequenceMultiplePartials(t *testing.T) { } require.False(t, ss.runSequence(e1)) - // expression matched the partial that satisfy the sequence link + // expression matched the partial that satisfies the sequence link assert.Len(t, ss.partials[0], 6) assert.Len(t, ss.partials[1], 0) require.True(t, ss.runSequence(e2)) @@ -447,7 +450,7 @@ func TestComplexSequence(t *testing.T) { f := filter.New(` sequence maxspan 1h - |evt.name = 'CreateProcess' and ps.child.name in ('firefox.exe', 'chrome.exe', 'edge.exe')| by ps.child.pid + |evt.name = 'CreateProcess' and ps.name in ('firefox.exe', 'chrome.exe', 'edge.exe')| by ps.pid |evt.name = 'CreateFile' and file.operation = 'CREATE' and file.extension = '.exe'| by ps.pid |evt.name in ('Send', 'Connect')| by ps.pid `, &config.Config{EventSource: config.EventSourceConfig{EnableFileIOEvents: true}, Filters: &config.Filters{}}) @@ -462,10 +465,10 @@ func TestComplexSequence(t *testing.T) { Category: event.Process, Name: "CreateProcess", Tid: 2484, - PID: 859, + PID: 2243, PS: &pstypes.PS{ - Name: "explorer.exe", - Exe: "C:\\Windows\\system32\\explorer.exe", + Name: "firefox.exe", + Exe: "C:\\Program Files\\Firefox\\firefox.exe", }, Params: event.Params{ params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(2243)}, @@ -691,8 +694,8 @@ func TestSequenceExpire(t *testing.T) { &config.FilterConfig{Name: "System Binary Proxy Execution via Rundll32"}, `sequence maxspan 2m - |evt.name = 'CreateProcess' and ps.child.name = 'rundll32.exe'| by ps.child.pid - |evt.name = 'CreateProcess' and ps.child.name = 'connhost.exe'| by ps.pid + |evt.name = 'CreateProcess' and ps.name = 'rundll32.exe'| by ps.pid + |evt.name = 'CreateProcess' and ps.name = 'connhost.exe'| by ps.parent.pid `, []*event.Event{ { @@ -702,10 +705,10 @@ func TestSequenceExpire(t *testing.T) { Category: event.Process, Name: "CreateProcess", Tid: 2484, - PID: 859, + PID: 2243, PS: &pstypes.PS{ - Name: "explorer.exe", - Exe: "C:\\Windows\\system32\\explorer.exe", + Name: "rundll32.exe", + Exe: "C:\\Windows\\system32\\rundll32.exe", }, Params: event.Params{ params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(2243)}, @@ -720,10 +723,14 @@ func TestSequenceExpire(t *testing.T) { Category: event.Process, Name: "CreateProcess", Tid: 2484, - PID: 2243, + PID: 12243, PS: &pstypes.PS{ - Name: "explorer.exe", - Exe: "C:\\Windows\\system32\\explorer.exe", + Name: "connhost.exe", + Exe: "C:\\Windows\\system32\\connhost.exe", + Parent: &pstypes.PS{ + Name: "rundll32.exe", + PID: 2243, + }, }, Params: event.Params{ params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(12243)}, diff --git a/pkg/symbolize/symbolizer.go b/pkg/symbolize/symbolizer.go index f90bdfacd..e2146f29c 100644 --- a/pkg/symbolize/symbolizer.go +++ b/pkg/symbolize/symbolizer.go @@ -398,9 +398,10 @@ func (s *Symbolizer) processCallstack(e *event.Event) error { return nil } - proc, ok := s.procs[e.PID] + pid := e.StackPID() + proc, ok := s.procs[pid] if !ok { - handle, err := windows.OpenProcess(windows.SYNCHRONIZE|windows.PROCESS_QUERY_INFORMATION, false, e.PID) + handle, err := windows.OpenProcess(windows.SYNCHRONIZE|windows.PROCESS_QUERY_INFORMATION, false, pid) if err != nil { s.pushFrames(addrs, e) return err @@ -410,10 +411,10 @@ func (s *Symbolizer) processCallstack(e *event.Event) error { err = s.r.Initialize(handle, opts) if err != nil { s.pushFrames(addrs, e) - return ErrSymInitialize(e.PID) + return ErrSymInitialize(pid) } - proc = &process{e.PID, handle, time.Now(), 1} - s.procs[e.PID] = proc + proc = &process{pid, handle, time.Now(), 1} + s.procs[pid] = proc } s.pushFrames(addrs, e) @@ -442,7 +443,8 @@ func (s *Symbolizer) pushFrames(addrs []va.Address, e *event.Event) { // symbol or module are not resolved, then we // fall back to Debug API. func (s *Symbolizer) produceFrame(addr va.Address, e *event.Event) callstack.Frame { - frame := callstack.Frame{PID: e.PID, Addr: addr} + pid := e.StackPID() + frame := callstack.Frame{PID: pid, Addr: addr} if addr.InSystemRange() { if s.config.SymbolizeKernelAddresses { frame.Module = s.r.GetModuleName(windows.CurrentProcess(), addr) @@ -452,7 +454,7 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *event.Event) callstack.Fra } // did we hit this address previously? - if sym, ok := s.symbols[e.PID]; ok { + if sym, ok := s.symbols[pid]; ok { if symbol, ok := sym[addr]; ok { symCacheHits.Add(1) frame.Module, frame.Symbol, frame.ModuleAddress = symbol.module, symbol.symbol, symbol.moduleAddress @@ -468,7 +470,7 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *event.Event) callstack.Fra } if mod == nil { // our last resort is to enumerate process modules - modules := sys.EnumProcessModules(e.PID) + modules := sys.EnumProcessModules(pid) for _, m := range modules { b := va.Address(m.BaseOfDll) size := uint64(m.SizeOfImage) @@ -526,7 +528,7 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *event.Event) callstack.Fra } if frame.Module != "" && frame.Symbol != "" { // store resolved symbol information in cache - s.cacheSymbol(e.PID, addr, &frame) + s.cacheSymbol(pid, addr, &frame) return frame } } @@ -534,9 +536,9 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *event.Event) callstack.Fra debugHelpFallbacks.Add(1) // fallback to Debug Help API - proc, ok := s.procs[e.PID] + proc, ok := s.procs[pid] if !ok { - handle, err := windows.OpenProcess(windows.SYNCHRONIZE|windows.PROCESS_QUERY_INFORMATION, false, e.PID) + handle, err := windows.OpenProcess(windows.SYNCHRONIZE|windows.PROCESS_QUERY_INFORMATION, false, pid) if err != nil { return frame } @@ -546,8 +548,8 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *event.Event) callstack.Fra if err != nil { return frame } - proc = &process{e.PID, handle, time.Now(), 1} - s.procs[e.PID] = proc + proc = &process{pid, handle, time.Now(), 1} + s.procs[pid] = proc } proc.keepalive() @@ -564,7 +566,7 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *event.Event) callstack.Fra } // store resolved symbol information in cache - s.cacheSymbol(e.PID, addr, &frame) + s.cacheSymbol(pid, addr, &frame) return frame } diff --git a/pkg/symbolize/symbolizer_test.go b/pkg/symbolize/symbolizer_test.go index 28fcfb8aa..163d8b4e9 100644 --- a/pkg/symbolize/symbolizer_test.go +++ b/pkg/symbolize/symbolizer_test.go @@ -19,6 +19,12 @@ package symbolize import ( + "math/rand" + "os" + "path/filepath" + "testing" + "time" + "github.com/rabbitstack/fibratus/pkg/config" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" @@ -33,11 +39,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "golang.org/x/sys/windows" - "math/rand" - "os" - "path/filepath" - "testing" - "time" ) // MockResolver for unit testing @@ -282,7 +283,7 @@ func TestProcessCallstack(t *testing.T) { e := &event.Event{ Type: event.CreateProcess, Tid: 2484, - PID: uint32(os.Getpid()), + PID: 2232, CPU: 1, Seq: 2, Name: "CreatedProcess", @@ -290,7 +291,8 @@ func TestProcessCallstack(t *testing.T) { Category: event.Process, Host: "archrabbit", Params: event.Params{ - params.Callstack: {Name: params.Callstack, Type: params.Slice, Value: []va.Address{0x7ffb5c1d0396, 0x7ffb5d8e61f4, 0x7ffb3138592e, 0x7ffb313853b2, 0x2638e59e0a5}}, + params.ProcessParentID: {Name: params.ProcessParentID, Type: params.PID, Value: (uint32(os.Getpid()))}, + params.Callstack: {Name: params.Callstack, Type: params.Slice, Value: []va.Address{0x7ffb5c1d0396, 0x7ffb5d8e61f4, 0x7ffb3138592e, 0x7ffb313853b2, 0x2638e59e0a5}}, }, PS: proc, } @@ -454,7 +456,7 @@ func TestProcessCallstackProcsTTL(t *testing.T) { e := &event.Event{ Type: event.CreateProcess, Tid: 2484, - PID: uint32(os.Getpid()), + PID: 1232, CPU: 1, Seq: 2, Name: "CreatedProcess", @@ -462,7 +464,8 @@ func TestProcessCallstackProcsTTL(t *testing.T) { Category: event.Process, Host: "archrabbit", Params: event.Params{ - params.Callstack: {Name: params.Callstack, Type: params.Slice, Value: []va.Address{0x7ffb5c1d0396, 0x7ffb5d8e61f4, 0x7ffb3138592e, 0x7ffb313853b2, 0x2638e59e0a5}}, + params.ProcessParentID: {Name: params.ProcessParentID, Type: params.PID, Value: (uint32(os.Getpid()))}, + params.Callstack: {Name: params.Callstack, Type: params.Slice, Value: []va.Address{0x7ffb5c1d0396, 0x7ffb5d8e61f4, 0x7ffb3138592e, 0x7ffb313853b2, 0x2638e59e0a5}}, }, } _, _ = s.ProcessEvent(e) diff --git a/rules/credential_access_credential_access_from_backups_via_rundll32.yml b/rules/credential_access_credential_access_from_backups_via_rundll32.yml index 3e64b0293..2021cc850 100644 --- a/rules/credential_access_credential_access_from_backups_via_rundll32.yml +++ b/rules/credential_access_credential_access_from_backups_via_rundll32.yml @@ -1,6 +1,6 @@ name: Credentials access from backups via Rundll32 id: ff43852c-486c-4870-a318-ce976d2231a5 -version: 1.0.2 +version: 1.0.3 description: | Detects an attempt to obtain credentials from credential backups. labels: @@ -16,7 +16,7 @@ labels: condition: > spawn_process and - (ps.child.name ~= 'rundll32.exe' or ps.child.pe.file.name ~= 'rundll32.exe') and - (ps.child.args iin ('keymgr.dll') and ps.child.args iin ('KRShowKeyMgr')) + (ps.name ~= 'rundll32.exe' or pe.file.name ~= 'rundll32.exe') and + (ps.args iin ('keymgr.dll') and ps.args iin ('KRShowKeyMgr')) min-engine-version: 3.0.0 diff --git a/rules/credential_access_credential_discovery_via_vaultcmd.yml b/rules/credential_access_credential_discovery_via_vaultcmd.yml index 03f4be7e2..bfd8eb210 100644 --- a/rules/credential_access_credential_discovery_via_vaultcmd.yml +++ b/rules/credential_access_credential_discovery_via_vaultcmd.yml @@ -1,6 +1,6 @@ name: Credential discovery via VaultCmd tool id: 2ce607d3-5a14-4628-be8a-22bcde97dab5 -version: 1.1.2 +version: 1.1.3 description: | Detects the usage of the VaultCmd tool to list Windows Credentials. VaultCmd creates, displays and deletes stored credentials. An adversary may abuse this to list or dump @@ -18,8 +18,8 @@ labels: condition: > spawn_process and - (ps.child.name ~= 'VaultCmd.exe' or ps.child.pe.file.name ~= 'vaultcmd.exe') and - ps.child.cmdline imatches '*/list*' + (ps.name ~= 'VaultCmd.exe' or pe.file.name ~= 'vaultcmd.exe') and + ps.cmdline imatches '*/list*' severity: medium diff --git a/rules/credential_access_lsass_memory_dump_via_wer.yml b/rules/credential_access_lsass_memory_dump_via_wer.yml index 447b79aa0..ae38b8021 100644 --- a/rules/credential_access_lsass_memory_dump_via_wer.yml +++ b/rules/credential_access_lsass_memory_dump_via_wer.yml @@ -1,6 +1,6 @@ name: LSASS memory dump via Windows Error Reporting id: 7b4a74e2-c7a7-4c1f-b2ce-0e0273c3add7 -version: 1.0.3 +version: 1.0.5 description: | Adversaries may abuse Windows Error Reporting service to dump LSASS memory. The ALPC protocol can send a message to report an exception on LSASS and @@ -21,7 +21,8 @@ references: condition: > sequence maxspan 2m - |spawn_process and ps.child.name iin ('WerFault.exe', 'WerFaultSecure.exe')| by ps.child.uuid - |create_file and file.path icontains 'lsass'| by ps.uuid + by ps.uuid + |spawn_process and ps.name iin ('WerFault.exe', 'WerFaultSecure.exe')| + |create_file and file.path icontains 'lsass'| min-engine-version: 3.0.0 diff --git a/rules/credential_access_lsass_process_clone_creation_via_reflection.yml b/rules/credential_access_lsass_process_clone_creation_via_reflection.yml index 65e7a0b92..26ebc8b57 100644 --- a/rules/credential_access_lsass_process_clone_creation_via_reflection.yml +++ b/rules/credential_access_lsass_process_clone_creation_via_reflection.yml @@ -1,6 +1,6 @@ name: LSASS process clone creation via reflection id: cdf3810a-4832-446a-ac9d-d108cf2e313c -version: 1.0.2 +version: 1.0.3 description: | Identifies the creation of an LSASS clone process via RtlCreateProcessReflection API function. Adversaries can use this technique to dump credentials material from the LSASS fork and evade @@ -21,7 +21,7 @@ references: condition: > spawn_process and - ps.name ~= 'lsass.exe' and ps.child.name ~= 'lsass.exe' and + ps.name ~= 'lsass.exe' and ps.parent.name ~= 'lsass.exe' and thread.callstack.symbols imatches ('ntdll.dll!RtlCloneUserProcess', 'ntdll.dll!RtlCreateProcessReflection') action: - name: kill diff --git a/rules/credential_access_potential_sam_hive_dumping.yml b/rules/credential_access_potential_sam_hive_dumping.yml index 3a09d23c5..4adfe20a5 100644 --- a/rules/credential_access_potential_sam_hive_dumping.yml +++ b/rules/credential_access_potential_sam_hive_dumping.yml @@ -1,6 +1,6 @@ name: Potential SAM hive dumping id: 2f326557-0291-4eb1-a87a-7a17b7d941cb -version: 1.0.6 +version: 1.0.7 description: Identifies access to the Security Account Manager registry hives. labels: @@ -19,16 +19,17 @@ references: condition: > sequence maxspan 10m + by ps.uuid |spawn_process and - not (ps.exe imatches + not (ps.parent.exe imatches ( '?:\\Program Files\\*.exe', '?:\\Program Files (x86)\\*.exe', '?:\\Windows\\System32\\svchost.exe' ) or - (ps.child.cmdline imatches '"?:\\Windows\\Microsoft.NET\\Framework\\*\\ngen.exe" ExecuteQueuedItems /LegacyServiceBehavior') + (ps.cmdline imatches '"?:\\Windows\\Microsoft.NET\\Framework\\*\\ngen.exe" ExecuteQueuedItems /LegacyServiceBehavior') ) - | by ps.child.uuid + | |open_registry and registry.path imatches 'HKEY_LOCAL_MACHINE\\SAM\\SAM\\Domains\\Account\\*' and registry.path not imatches @@ -65,6 +66,6 @@ condition: > '?:\\Windows\\System32\\wbem\\WMIADAP.exe', '?:\\Windows\\System32\\cleanmgr.exe' ) - | by ps.uuid + | min-engine-version: 3.0.0 diff --git a/rules/credential_access_suspicious_vault_client_dll_load.yml b/rules/credential_access_suspicious_vault_client_dll_load.yml index deb0da9ba..cd35be615 100644 --- a/rules/credential_access_suspicious_vault_client_dll_load.yml +++ b/rules/credential_access_suspicious_vault_client_dll_load.yml @@ -1,6 +1,6 @@ name: Suspicious Vault client DLL load id: 64af2e2e-2309-4079-9c0f-985f1dd930f5 -version: 1.0.3 +version: 1.0.4 description: | Identifies loading of the Vault client DLL by an unusual process. Adversaries can abuse the functions provided by the Credential Vault Client Library to enumerate or harvest saved credentials. @@ -21,11 +21,12 @@ references: condition: > sequence maxspan 2m + by ps.uuid |spawn_process and ps.exe != '' and not ( - ps.child.exe imatches + ps.exe imatches ( '?:\\Windows\\System32\\MDMAppInstaller.exe', '?:\\Windows\\uus\\*\\MoUsoCoreWorker.exe', @@ -35,17 +36,17 @@ condition: > '?:\\Program Files (x86)\\*.exe', '?:\\Windows\\winsxs\\*\\TiWorker.exe' ) or - (ps.child.exe imatches '?:\\WINDOWS\\System32\\taskhostw.exe' and ps.args intersects ('-k', 'netsvcs', '-p', '-s', 'Schedule')) or - (ps.child.exe imatches '?:\\Windows\\System32\\RuntimeBroker.exe') or + (ps.exe imatches '?:\\WINDOWS\\System32\\taskhostw.exe' and ps.parent.args intersects ('-k', 'netsvcs', '-p', '-s', 'Schedule')) or + (ps.exe imatches '?:\\Windows\\System32\\RuntimeBroker.exe') or (ps.exe imatches ('?:\\Program Files\\WindowsApps\\Microsoft.*.exe', '?:\\Windows\\Microsoft.NET\\Framework*\\NGenTask.exe')) or - (ps.child.exe imatches '?:\\WINDOWS\\system32\\BackgroundTaskHost.exe' and ps.child.args imatches ('-ServerName:*')) or - (ps.child.exe imatches '?:\\Windows\\System32\\SecurityHealth\\*\\SecurityHealthHost.exe') or - (ps.child.exe imatches '?:\\WINDOWS\\uus\\*\\MoUsoCoreWorker.exe') or - (ps.exe imatches '?:\\Windows\\System32\\services.exe') or - (ps.exe imatches '?:\\Program Files\\Microsoft OneDrive\\OneDriveStandaloneUpdater.exe') + (ps.exe imatches '?:\\WINDOWS\\system32\\BackgroundTaskHost.exe' and ps.args imatches ('-ServerName:*')) or + (ps.exe imatches '?:\\Windows\\System32\\SecurityHealth\\*\\SecurityHealthHost.exe') or + (ps.exe imatches '?:\\WINDOWS\\uus\\*\\MoUsoCoreWorker.exe') or + (ps.parent.exe imatches '?:\\Windows\\System32\\services.exe') or + (ps.parent.exe imatches '?:\\Program Files\\Microsoft OneDrive\\OneDriveStandaloneUpdater.exe') ) - | by ps.child.uuid - |load_dll and image.name ~= 'vaultcli.dll'| by ps.uuid + | + |load_dll and image.name ~= 'vaultcli.dll'| output: > Suspicious process %2.ps.exe loaded the Credential Vault Client DLL for potential credentials harvesting diff --git a/rules/defense_evasion_dll_loaded_via_callback_function.yml b/rules/defense_evasion_dll_loaded_via_callback_function.yml index 2f0941a22..1ab3e25ad 100644 --- a/rules/defense_evasion_dll_loaded_via_callback_function.yml +++ b/rules/defense_evasion_dll_loaded_via_callback_function.yml @@ -1,6 +1,6 @@ name: DLL loaded via a callback function id: c7f46d0a-10b2-421a-b33c-f4df79599f2e -version: 1.0.3 +version: 1.0.5 description: | Identifies module proxying as a method to conceal suspicious callstacks. Adversaries use module proxying the hide the origin of the LoadLibrary call from the callstack by loading the library from the callback @@ -19,7 +19,8 @@ tags: condition: > sequence maxspan 2m - |spawn_process| by ps.child.uuid + by ps.uuid + |spawn_process| |load_dll and image.name iin ( 'winhttp.dll', 'clr.dll', 'bcrypt.dll', 'bcryptprimitives.dll', @@ -32,7 +33,7 @@ condition: > 'ntdll.dll|kernelbase.dll|ntdll.dll|kernel32.dll|ntdll.dll', 'ntdll.dll|wow64.dll|wow64cpu.dll|wow64.dll|ntdll.dll|kernelbase.dll|ntdll.dll|kernel32.dll|ntdll.dll' ) - | by ps.uuid + | output: > %2.image.path loaded from callback function by process %ps.exe diff --git a/rules/defense_evasion_potential_injection_via_dotnet_debugging.yml b/rules/defense_evasion_potential_injection_via_dotnet_debugging.yml index 1cf68a50b..78cd835da 100644 --- a/rules/defense_evasion_potential_injection_via_dotnet_debugging.yml +++ b/rules/defense_evasion_potential_injection_via_dotnet_debugging.yml @@ -1,6 +1,6 @@ name: Potential injection via .NET debugging id: 193ebf2f-e365-4f57-a639-275b7cdf0319 -version: 1.0.4 +version: 1.0.5 description: | Identifies creation of a process on behalf of the CLR debugging facility which may be indicative of code injection. The CLR interface utilizes the OpenVirtualProcess @@ -18,16 +18,16 @@ references: condition: > spawn_process and - ps.exe != '' and thread.callstack.symbols imatches ('mscordbi.dll!OpenVirtualProcess') and - ps.child.exe not imatches - ( - '?:\\Visual Studio\\Common?\\IDE\\devenv.exe', - '?:\\Program Files\\Microsoft Visual Studio\\*.exe', - '?:\\Program Files (x86)\\Microsoft Visual Studio\\*.exe', - '?:\\Program Files\\IIS Express\\iisexpress.exe', - '?:\\Program Files (x86)\\IIS Express\\iisexpress.exe' - ) and - ps.exe not imatches '?:\\Program Files (x86)\\Microsoft Visual Studio\\*.exe' + ps.parent.exe != '' and thread.callstack.symbols imatches ('mscordbi.dll!OpenVirtualProcess') and + ps.exe not imatches + ( + '?:\\Visual Studio\\Common?\\IDE\\devenv.exe', + '?:\\Program Files\\Microsoft Visual Studio\\*.exe', + '?:\\Program Files (x86)\\Microsoft Visual Studio\\*.exe', + '?:\\Program Files\\IIS Express\\iisexpress.exe', + '?:\\Program Files (x86)\\IIS Express\\iisexpress.exe' + ) and + ps.parent.exe not imatches '?:\\Program Files (x86)\\Microsoft Visual Studio\\*.exe' output: > Process %ps.exe attached the .NET debugger to process %ps.child.exe for potential code injection diff --git a/rules/defense_evasion_potential_process_doppelganging_injection.yml b/rules/defense_evasion_potential_process_doppelganging_injection.yml index aeff0f8f0..da2a1f306 100644 --- a/rules/defense_evasion_potential_process_doppelganging_injection.yml +++ b/rules/defense_evasion_potential_process_doppelganging_injection.yml @@ -1,6 +1,6 @@ name: Potential Process Doppelganging id: eb34cf6e-ccc3-4bce-bbcf-013720640a28 -version: 1.0.1 +version: 1.0.2 description: | Adversaries may inject malicious code into process via process doppelganging in order to evade process-based defenses as well as possibly elevate privileges. @@ -50,9 +50,8 @@ references: condition: > sequence maxspan 2m - by ps.uuid - |create_file and thread.callstack.symbols imatches ('kernel32.dll!CreateFileTransacted*', 'ntdll.dll!RtlSetCurrentTransaction')| - |spawn_process| + |create_file and thread.callstack.symbols imatches ('kernel32.dll!CreateFileTransacted*', 'ntdll.dll!RtlSetCurrentTransaction')| by ps.uuid + |spawn_process| by ps.parent.uuid action: - name: kill diff --git a/rules/defense_evasion_potential_process_hollowing_injection.yml b/rules/defense_evasion_potential_process_hollowing_injection.yml index 52244abc4..d8e67ad6e 100644 --- a/rules/defense_evasion_potential_process_hollowing_injection.yml +++ b/rules/defense_evasion_potential_process_hollowing_injection.yml @@ -1,6 +1,6 @@ name: Potential Process Hollowing id: 2a3fbae8-5e8c-4b71-b9da-56c3958c0d53 -version: 1.1.6 +version: 1.1.7 description: | Adversaries may inject malicious code into suspended and hollowed processes in order to evade process-based defenses. Process hollowing is a method of executing arbitrary code @@ -29,19 +29,20 @@ references: condition: > sequence maxspan 2m + by ps.uuid |spawn_process and - ps.sid not in ('S-1-5-18', 'S-1-5-19', 'S-1-5-20') and - ps.exe not imatches + ps.parent.sid not in ('S-1-5-18', 'S-1-5-19', 'S-1-5-20') and + ps.parent.exe not imatches ( '?:\\Program Files\\*.exe', '?:\\Program Files (x86)\\*.exe', '?:\\Users\\*\\AppData\\Local\\Programs\\Common\\OneDriveCloud\\taskhostw.exe' ) - | by ps.child.uuid + | |unmap_view_of_section and file.view.size > 20000 and file.view.protection != 'READONLY' and (length(file.name) = 0 or not ext(file.name) = '.dll') - | by ps.uuid - |load_executable| by ps.uuid + | + |load_executable| action: - name: kill diff --git a/rules/defense_evasion_regsvr32_scriptlet_execution.yml b/rules/defense_evasion_regsvr32_scriptlet_execution.yml index 2a93b5dd1..4bd20a3e8 100644 --- a/rules/defense_evasion_regsvr32_scriptlet_execution.yml +++ b/rules/defense_evasion_regsvr32_scriptlet_execution.yml @@ -1,6 +1,6 @@ name: Regsvr32 scriptlet execution id: 128f5254-67c9-43ac-b901-18b3731b1d0b -version: 1.0.3 +version: 1.0.4 description: | Identifies the execution of a scriptlet file by regsvr32.exe process. regsvr32.exe allows attackers to run arbitrary scripts to proxy execution of malicious code. @@ -17,42 +17,42 @@ labels: condition: > spawn_process and - (ps.child.name ~= 'regsvr32.exe' or ps.child.pe.file.name ~= 'regsvr32.exe') and + (ps.name ~= 'regsvr32.exe' or pe.file.name ~= 'regsvr32.exe') and ( - (ps.child.cmdline imatches '*scrobj*' and - ps.child.cmdline imatches - ( - '*/i:*', - '*-i:*', - '*.sct*' - ) + (ps.cmdline imatches '*scrobj*' and + ps.cmdline imatches + ( + '*/i:*', + '*-i:*', + '*.sct*' + ) ) or - (ps.child.cmdline imatches '* /u*' and - ps.child.cmdline imatches - ( - '* -i:*http*', - '* /i:*http*', - '* -i:*ftp*', - '* /i:*ftp*', - '* -i:C:\\*', - '* /i:\"C:\\*', - '* /i:C:\\*', - '* -i:\"C:\\*' - ) + (ps.cmdline imatches '* /u*' and + ps.cmdline imatches + ( + '* -i:*http*', + '* /i:*http*', + '* -i:*ftp*', + '* /i:*ftp*', + '* -i:C:\\*', + '* /i:\"C:\\*', + '* /i:C:\\*', + '* -i:\"C:\\*' + ) ) or - (ps.child.cmdline imatches - ( - '* /i:*', - '* -i:*' - ) and - ps.child.cmdline not imatches - ( - '* /n*', - '* -n*' - ) + (ps.cmdline imatches + ( + '* /i:*', + '* -i:*' + ) and + ps.cmdline not imatches + ( + '* /n*', + '* -n*' + ) ) ) and - ps.child.exe not imatches + ps.exe not imatches ( '?:\\Program Files\\*.exe', '?:\\Program Files (x86)\\*.exe' diff --git a/rules/defense_evasion_suspicious_access_to_the_hosts_file.yml b/rules/defense_evasion_suspicious_access_to_the_hosts_file.yml index 57f844c7e..965b5824e 100644 --- a/rules/defense_evasion_suspicious_access_to_the_hosts_file.yml +++ b/rules/defense_evasion_suspicious_access_to_the_hosts_file.yml @@ -1,6 +1,6 @@ name: Suspicious access to the hosts file id: f7b2c9d3-99e7-41d5-bb4a-6ea1a5f7f9e2 -version: 1.0.4 +version: 1.0.5 description: > Identifies suspicious process accessing the Windows hosts file for potential tampering. Adversaries can hijack the hosts files to block traffic to download/update servers or redirect the @@ -18,7 +18,8 @@ references: condition: > sequence maxspan 5m - |spawn_process and ps.child.exe not imatches + by ps.uuid + |spawn_process and ps.exe not imatches ( '?:\\Windows\\servicing\\TrustedInstaller.exe', '?:\\Windows\\System32\\svchost.exe', @@ -27,8 +28,8 @@ condition: > '?:\\Program Files\\Mozilla Firefox\\firefox.exe', '?:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe' ) - | by ps.child.uuid - |open_file and file.path imatches '?:\\Windows\\System32\\drivers\\etc\\hosts'| by ps.uuid + | + |open_file and file.path imatches '?:\\Windows\\System32\\drivers\\etc\\hosts'| action: - name: kill diff --git a/rules/defense_evasion_suspicious_html_application_script_execution.yml b/rules/defense_evasion_suspicious_html_application_script_execution.yml index 9714aa2e4..296bd0339 100644 --- a/rules/defense_evasion_suspicious_html_application_script_execution.yml +++ b/rules/defense_evasion_suspicious_html_application_script_execution.yml @@ -1,6 +1,6 @@ name: Suspicious HTML Application script execution id: 4ec64ac2-851d-41b4-b7d2-910c21de334d -version: 1.0.3 +version: 1.0.5 description: | Identifies the execution of scripts via Microsoft HTML Application Host interpreter. Adversaries can proxy the execution of arbitrary script code through a trusted, signed utility to evade defenses. @@ -20,38 +20,36 @@ references: condition: > spawn_process and - (ps.child.name ~= 'mshta.exe' or ps.child.pe.file.name ~= 'mshta.exe') and - ps.child.cmdline imatches - ( - '*WScript.Shell*', - '*mshtml*RunHTMLApplication*', - '*http*', - '*https*', - '*hXXps', - '*ftp*', - '*.run*', - '*window.close*', - '*mshta*', - '*mshtml*', - '*).Exec()*', - '*script*eval(*', - '*script*GetObject*', - '*vbscript*', - '*\\Users\\*\\Downloads\\*.hta*', - '*.rtf*', - '*.bat*', - '*.dll*', - '*.zip*', - '*.jpg*', - '*.png*', - '*.lnk*', - '*.doc*', - '*.xls*' - ) and - ps.exe not imatches - ( - '?:\\Program Files (x86)\\Microsoft Office\\Office*\\MSACCESS.EXE' - ) + (ps.name ~= 'mshta.exe' or pe.file.name ~= 'mshta.exe') and + ps.cmdline imatches + ( + '*WScript.Shell*', + '*mshtml*RunHTMLApplication*', + '*http*', + '*https*', + '*hXXps', + '*ftp*', + '*.run*', + '*window.close*', + '*mshta*', + '*mshtml*', + '*).Exec()*', + '*script*eval(*', + '*script*GetObject*', + '*vbscript*', + '*\\Users\\*\\Downloads\\*.hta*', + '*.rtf*', + '*.bat*', + '*.dll*', + '*.zip*', + '*.jpg*', + '*.png*', + '*.lnk*', + '*.doc*', + '*.xls*' + ) and + ps.parent.exe not imatches '?:\\Program Files (x86)\\Microsoft Office\\Office*\\MSACCESS.EXE' + action: - name: kill diff --git a/rules/defense_evasion_suspicious_xsl_script_execution.yml b/rules/defense_evasion_suspicious_xsl_script_execution.yml index a9cd2a350..2d5da5660 100644 --- a/rules/defense_evasion_suspicious_xsl_script_execution.yml +++ b/rules/defense_evasion_suspicious_xsl_script_execution.yml @@ -1,6 +1,6 @@ name: Suspicious XSL script execution id: 65136b30-14ae-46dd-b8e5-9dfa99690d74 -version: 1.0.3 +version: 1.0.4 description: | Identifies a suspicious execution of XSL script via Windows Management Instrumentation command line tool or XSL transformation utility. Adversaries may bypass application control and obscure the execution of code by embedding @@ -19,26 +19,27 @@ references: condition: > sequence maxspan 3m + by ps.uuid |spawn_process and - (((ps.child.name ~= 'wmic.exe' or ps.child.pe.file.name ~= 'wmic.exe') and - ps.child.cmdline imatches ('* format*:*', '*/format*:*', '*-format*:*') and - ps.child.cmdline not imatches - ( - '*format:list*', - '*format:htable*', - '*format:hform*', - '*format:table*', - '*format:mof*', - '*format:value*', - '*format:rawxml*', - '*format:xml*', - '*format:csv*' - ) + (((ps.name ~= 'wmic.exe' or pe.file.name ~= 'wmic.exe') and + ps.cmdline imatches ('* format*:*', '*/format*:*', '*-format*:*') and + ps.cmdline not imatches + ( + '*format:list*', + '*format:htable*', + '*format:hform*', + '*format:table*', + '*format:mof*', + '*format:value*', + '*format:rawxml*', + '*format:xml*', + '*format:csv*' + ) ) or - ps.child.name ~= 'msxsl.exe' or ps.child.pe.file.name ~= 'msxsl.exe' + ps.name ~= 'msxsl.exe' or pe.file.name ~= 'msxsl.exe' ) - | by ps.child.uuid - |load_dll and image.name iin ('scrobj.dll', 'vbscript.dll', 'jscript.dll', 'jscript9.dll')| by ps.uuid + | + |load_dll and image.name iin ('scrobj.dll', 'vbscript.dll', 'jscript.dll', 'jscript9.dll')| output: > Suspicious XSL script executed by process %1.ps.child.name with command line arguments %1.ps.child.args diff --git a/rules/defense_evasion_system_binary_proxy_execution_via_rundll32.yml b/rules/defense_evasion_system_binary_proxy_execution_via_rundll32.yml index 8943e3f34..fb9537488 100644 --- a/rules/defense_evasion_system_binary_proxy_execution_via_rundll32.yml +++ b/rules/defense_evasion_system_binary_proxy_execution_via_rundll32.yml @@ -1,6 +1,6 @@ name: System Binary Proxy Execution via Rundll32 id: 43d76718-cc46-485e-8f47-996eb7a9f83b -version: 1.0.3 +version: 1.0.4 description: | Detects the execution of rundll32.exe process with suspicious command line followed by the creation of a possibly malicious child process. @@ -25,38 +25,38 @@ condition: > sequence maxspan 1m |spawn_process and - (ps.child.name ~= 'rundll32.exe' or ps.child.pe.file.name ~= 'rundll32.exe') and + (ps.name ~= 'rundll32.exe' or pe.file.name ~= 'rundll32.exe') and ( - ps.child.cmdline imatches - ( - '*javascript:*', - '*vbscript:*', - '*shell32.dll*ShellExec_RunDLL*', - '*shell32*WaitForExplorerRestart*', - '*-sta*', - '*ActiveXObject*', - '*WScript.Shell*', - '*RunHTMLApplication*', - '*advpack*#12*', - '*advpack*RegisterOCX*', - '*advpack*LaunchINFSection*', - '*url.dll*FileProtocolHandler*file://*', - '*url.dll*FileProtocolHandler*.exe*', - '*zipfldr*RouteTheCall*', - '*pcwutl*LaunchApplication*', - '*pcwutl*#1*', - '*desk*InstallScreenSaver*', - '*PointFunctionCall*' - ) or - regex(ps.child.cmdline, '(?i)[A-Z]:\\\\.+:.+$') + ps.cmdline imatches + ( + '*javascript:*', + '*vbscript:*', + '*shell32.dll*ShellExec_RunDLL*', + '*shell32*WaitForExplorerRestart*', + '*-sta*', + '*ActiveXObject*', + '*WScript.Shell*', + '*RunHTMLApplication*', + '*advpack*#12*', + '*advpack*RegisterOCX*', + '*advpack*LaunchINFSection*', + '*url.dll*FileProtocolHandler*file://*', + '*url.dll*FileProtocolHandler*.exe*', + '*zipfldr*RouteTheCall*', + '*pcwutl*LaunchApplication*', + '*pcwutl*#1*', + '*desk*InstallScreenSaver*', + '*PointFunctionCall*' + ) or + regex(ps.cmdline, '(?i)[A-Z]:\\\\.+:.+$') ) - | by ps.child.uuid - |spawn_process and ps.child.exe not imatches - ( - '?:\\Program Files\\*.exe', - '?:\\Program Files (x86)\\*.exe' - ) | by ps.uuid + |spawn_process and ps.exe not imatches + ( + '?:\\Program Files\\*.exe', + '?:\\Program Files (x86)\\*.exe' + ) + | by ps.parent.uuid action: - name: kill diff --git a/rules/initial_access_execution_via_microsoft_office_process.yml b/rules/initial_access_execution_via_microsoft_office_process.yml index e8c30ba99..2a7ece313 100644 --- a/rules/initial_access_execution_via_microsoft_office_process.yml +++ b/rules/initial_access_execution_via_microsoft_office_process.yml @@ -1,6 +1,6 @@ name: Execution via Microsoft Office process id: a10ebe66-1b55-4005-a374-840f1e2933a3 -version: 1.0.2 +version: 1.0.3 description: Identifies the execution of the file dropped by Microsoft Office process. labels: @@ -18,6 +18,6 @@ condition: > sequence maxspan 1h |create_file and (file.extension iin executable_extensions or file.is_exec) and ps.name iin msoffice_binaries| by file.path - |spawn_process and ps.name iin msoffice_binaries| by ps.child.exe + |spawn_process and ps.parent.name iin msoffice_binaries| by ps.exe min-engine-version: 3.0.0 diff --git a/rules/initial_access_macro_execution_via_script_interpreter.yml b/rules/initial_access_macro_execution_via_script_interpreter.yml index 7bb810604..9f6e65ebf 100644 --- a/rules/initial_access_macro_execution_via_script_interpreter.yml +++ b/rules/initial_access_macro_execution_via_script_interpreter.yml @@ -1,6 +1,6 @@ name: Macro execution via script interpreter id: 845404de-df6f-472f-bd74-72148a7f5166 -version: 1.0.5 +version: 1.0.6 description: | Identifies the execution of the Windows scripting interpreter spawning a Microsoft Office process to execute suspicious Visual Basic macro. @@ -18,10 +18,11 @@ labels: condition: > sequence maxspan 5m - |spawn_process and ps.name iin script_interpreters and ps.child.name iin msoffice_binaries| by ps.child.uuid + by ps.uuid + |spawn_process and ps.parent.name iin script_interpreters and ps.name iin msoffice_binaries| |ps.name iin msoffice_binaries and thread.callstack.modules imatches '*vbe?.dll' and (spawn_process or (create_remote_thread) or (modify_registry) or (create_file) or (load_module and image.path not imatches ('?:\\Program Files\\*', '?:\\Program Files (x86)\\*'))) - | by ps.uuid + | min-engine-version: 3.0.0 diff --git a/rules/initial_access_microsoft_office_file_execution_via_script_interpreter.yml b/rules/initial_access_microsoft_office_file_execution_via_script_interpreter.yml index 30ff38a87..be9762b53 100644 --- a/rules/initial_access_microsoft_office_file_execution_via_script_interpreter.yml +++ b/rules/initial_access_microsoft_office_file_execution_via_script_interpreter.yml @@ -1,6 +1,6 @@ name: Microsoft Office file execution via script interpreter id: bf3ea547-1470-4bcc-9945-3b495d962c2c -version: 1.0.2 +version: 1.0.3 description: | Identifies the execution via Windows script interpreter of the executable file written by the Microsoft Office process. @@ -22,13 +22,13 @@ condition: > ps.name iin msoffice_binaries and (file.extension iin ('.exe', '.com', '.scr', '.pif', '.bat') or file.is_exec = true) | by file.path |spawn_process and - ps.name iin script_interpreters and - ps.child.exe not imatches + ps.parent.name iin script_interpreters and + ps.exe not imatches ( '?:\\Program Files\\*.exe', '?:\\Program Files (x86)\\*.exe' ) - | by ps.child.exe + | by ps.exe action: - name: kill diff --git a/rules/initial_access_microsoft_office_file_execution_via_wmi.yml b/rules/initial_access_microsoft_office_file_execution_via_wmi.yml index 772ecf976..da4812181 100644 --- a/rules/initial_access_microsoft_office_file_execution_via_wmi.yml +++ b/rules/initial_access_microsoft_office_file_execution_via_wmi.yml @@ -1,6 +1,6 @@ name: Microsoft Office file execution via WMI id: 50f6efa2-4d7b-4fb7-b1a9-65c3a24d9152 -version: 1.0.2 +version: 1.0.3 description: | Identifies the execution via Windows Management Instrumentation (WMI) of the binary file written by the Microsoft Office process. Attackers can exploit WMI to silently execute malicious code. @@ -23,7 +23,7 @@ condition: > |create_file and ps.name iin msoffice_binaries and (file.extension iin ('.exe', '.com') or file.is_exec = true) | by file.path - |spawn_process and ps.name ~= 'wmiprvse.exe'| by ps.child.exe + |spawn_process and ps.parent.name ~= 'wmiprvse.exe'| by ps.exe action: - name: kill diff --git a/rules/initial_access_potential_clickfix_infection_chain_via_run_window.yml b/rules/initial_access_potential_clickfix_infection_chain_via_run_window.yml index c1f10c180..e96922272 100644 --- a/rules/initial_access_potential_clickfix_infection_chain_via_run_window.yml +++ b/rules/initial_access_potential_clickfix_infection_chain_via_run_window.yml @@ -1,6 +1,6 @@ name: Potential ClickFix infection chain via Run window id: ffe1fc54-2893-4760-ab50-51a83bd71d13 -version: 1.0.3 +version: 1.0.4 description: | Identifies the execution of the process via the Run command dialog box followed by spawning of the potential infostealer process. @@ -22,7 +22,7 @@ condition: > sequence maxspan 2m |spawn_process and - ps.name ~= 'explorer.exe' and length(ps.child.args) >= 2 and + ps.parent.name ~= 'explorer.exe' and length(ps.args) >= 2 and (thread.callstack.summary imatches ( 'ntdll.dll|KernelBase.dll|kernel32.dll|windows.storage.dll|shell32.dll|user32.dll|shell32.dll|explorer.exe|SHCore.dll|*', @@ -30,14 +30,14 @@ condition: > ) or (thread.callstack.summary imatches '*shell32.dll|explorer.exe|*' and thread.callstack.symbols imatches ('*shell32.dll!GetFileNameFromBrowse*')) ) - | by ps.child.uuid - |spawn_process and ps.child.exe not imatches + | by ps.uuid + |spawn_process and ps.exe not imatches ( '?:\\Program Files\\*.exe', '?:\\Program Files (x86)\\*.exe', '?:\\Windows\\System32\\*.exe' ) - | by ps.uuid + | by ps.parent.uuid action: - name: kill diff --git a/rules/initial_access_process_spawned_from_macro_enabled_microsoft_office_document.yml b/rules/initial_access_process_spawned_from_macro_enabled_microsoft_office_document.yml index a4b863e9f..24057358c 100644 --- a/rules/initial_access_process_spawned_from_macro_enabled_microsoft_office_document.yml +++ b/rules/initial_access_process_spawned_from_macro_enabled_microsoft_office_document.yml @@ -1,6 +1,6 @@ name: Process spawned from macro-enabled Microsoft Office document id: 47521206-e19d-4608-9dbc-dc3a1df99db5 -version: 1.0.4 +version: 1.0.5 description: | Identifies the execution of the child process spawned by Microsoft Office parent process where the call stack contains the Visual Basic @@ -19,7 +19,7 @@ labels: condition: > spawn_process and - ps.name iin msoffice_binaries and + ps.parent.name iin msoffice_binaries and ( thread.callstack.modules imatches ('*vbe?.dll') or thread.callstack.symbols imatches @@ -31,7 +31,7 @@ condition: > 'shell32.dll!ShellExecute*' ) ) and - ps.child.exe not imatches + ps.exe not imatches ( '?:\\Windows\\explorer.exe', '?:\\Windows\\hh.exe', diff --git a/rules/initial_access_suspicious_execution_via_wmi_from_microsoft_office_process.yml b/rules/initial_access_suspicious_execution_via_wmi_from_microsoft_office_process.yml index ef510993d..dcbecfd39 100644 --- a/rules/initial_access_suspicious_execution_via_wmi_from_microsoft_office_process.yml +++ b/rules/initial_access_suspicious_execution_via_wmi_from_microsoft_office_process.yml @@ -1,6 +1,6 @@ name: Suspicious execution via WMI from a Microsoft Office process id: cc3f0bbe-ec53-40a7-9eed-f0a8a3f7d7fa -version: 1.0.2 +version: 1.0.3 description: | Identifies a suspicious process execution via Windows Management Instrumentation (WMI) originated from the Microsoft Office process loading an unusual WMI DLL. This technique @@ -23,66 +23,67 @@ references: condition: > sequence maxspan 1m + by ps.sid |load_dll and image.name iin ('wmiclnt.dll', 'wbemcomn.dll', 'wmiprov.dll', 'wbemprox.dll', 'wmutils.dll', 'fastprox.dll', 'WMINet_Utils.dll') and (ps.name iin msoffice_binaries or thread.callstack.modules imatches ('*vbe?.dll')) - | by ps.sid + | |spawn_process and - ps.name iin ('wmiprvse.exe', 'wmiapsrv.exe') and (ps.child.exe imatches ('?:\\Users\\*.exe', '?:\\ProgramData\\*.exe') or - ps.child.name iin - ( - 'rundll32.exe', - 'regsvr32.exe', - 'hh.exe', - 'cmd.exe', - 'pwsh.exe', - 'powershell.exe', - 'mshta.exe', - 'certutil.exe', - 'bitsadmin.exe', - 'cscript.exe', - 'wscript.exe', - 'jsc.exe', - 'vssadmin.exe', - 'curl.exe', - 'wget.exe', - 'sc.exe', - 'reg.exe', - 'schtasks.exe', - 'msxsl.exe', - 'msbuild.exe', - 'regasm.exe', - 'regsvcs.exe', - 'wmic.exe', - 'msiexec.exe' - ) or - ps.child.pe.file.name iin - ( - 'rundll32.exe', - 'regsvr32.exe', - 'hh.exe', - 'cmd.exe', - 'pwsh.exe', - 'powershell.exe', - 'mshta.exe', - 'certutil.exe', - 'bitsadmin.exe', - 'cscript.exe', - 'wscript.exe', - 'jsc.exe', - 'vssadmin.exe', - 'curl.exe', - 'wget.exe', - 'sc.exe', - 'reg.exe', - 'schtasks.exe', - 'msxsl.exe', - 'msbuild.exe', - 'regasm.exe', - 'regsvcs.exe', - 'wmic.exe', - 'msiexec.exe' - ))| by ps.child.sid + ps.parent.name iin ('wmiprvse.exe', 'wmiapsrv.exe') and (ps.exe imatches ('?:\\Users\\*.exe', '?:\\ProgramData\\*.exe') or + ps.name iin + ( + 'rundll32.exe', + 'regsvr32.exe', + 'hh.exe', + 'cmd.exe', + 'pwsh.exe', + 'powershell.exe', + 'mshta.exe', + 'certutil.exe', + 'bitsadmin.exe', + 'cscript.exe', + 'wscript.exe', + 'jsc.exe', + 'vssadmin.exe', + 'curl.exe', + 'wget.exe', + 'sc.exe', + 'reg.exe', + 'schtasks.exe', + 'msxsl.exe', + 'msbuild.exe', + 'regasm.exe', + 'regsvcs.exe', + 'wmic.exe', + 'msiexec.exe' + ) or + pe.file.name iin + ( + 'rundll32.exe', + 'regsvr32.exe', + 'hh.exe', + 'cmd.exe', + 'pwsh.exe', + 'powershell.exe', + 'mshta.exe', + 'certutil.exe', + 'bitsadmin.exe', + 'cscript.exe', + 'wscript.exe', + 'jsc.exe', + 'vssadmin.exe', + 'curl.exe', + 'wget.exe', + 'sc.exe', + 'reg.exe', + 'schtasks.exe', + 'msxsl.exe', + 'msbuild.exe', + 'regasm.exe', + 'regsvcs.exe', + 'wmic.exe', + 'msiexec.exe' + ))| output: > Suspicious process %2.ps.child.exe launched via WMI from Microsoft Office process %1.ps.cmdline diff --git a/rules/persistence_suspicious_netsh_helper_dll_execution.yml b/rules/persistence_suspicious_netsh_helper_dll_execution.yml index 882cd9e11..a484b2bcc 100644 --- a/rules/persistence_suspicious_netsh_helper_dll_execution.yml +++ b/rules/persistence_suspicious_netsh_helper_dll_execution.yml @@ -1,6 +1,6 @@ name: Suspicious Netsh Helper DLL execution id: bd17781d-38ca-4b9a-a12a-f807a1eb45e0 -version: 1.0.2 +version: 1.0.3 description: | Identifies the execution of a suspicious Netsh Helper DLL. Adversaries may establish persistence by executing malicious content triggered by Netsh Helper DLLs. Netsh.exe is a command-line scripting @@ -23,12 +23,13 @@ references: condition: > sequence maxspan 1m + by ps.uuid |spawn_process and - (ps.child.name ~= 'netsh.exe' or ps.child.pe.file.name ~= 'netsh.exe') - | by ps.child.uuid + (ps.name ~= 'netsh.exe' or pe.file.name ~= 'netsh.exe') + | |create_thread and foreach(thread._callstack, $frame, $frame.symbol imatches '*!InitHelperDll' and ($frame.module.signature.is_signed = false or $frame.module.signature.is_trusted = false)) - | by ps.uuid + | output: > Suspicious Netsh Helper DLL %2.thread.start_address.module executed