diff --git a/actions/client_info.go b/actions/client_info.go index 85d9c384e12..28735f8b195 100644 --- a/actions/client_info.go +++ b/actions/client_info.go @@ -38,5 +38,10 @@ func GetClientInfo( result.BuildTime = config_obj.Version.BuildTime result.InstallTime = config_obj.Version.InstallTime } + + if config_obj.Client != nil { + result.Labels = config_obj.Client.Labels + } + return result } diff --git a/artifacts/definitions/Windows/Forensics/SAM.yaml b/artifacts/definitions/Windows/Forensics/SAM.yaml index a333ca690af..627e01e9e55 100644 --- a/artifacts/definitions/Windows/Forensics/SAM.yaml +++ b/artifacts/definitions/Windows/Forensics/SAM.yaml @@ -113,10 +113,11 @@ export: | ] ''' -sources: - - precondition: - SELECT OS From info() where OS = 'windows' +precondition: + SELECT OS From info() where OS = 'windows' +sources: + - name: Parsed query: | SELECT Key.OSPath.Path AS Key, Key.OSPath.DelegatePath AS Hive, @@ -133,6 +134,15 @@ sources: accessor="raw_reg") WHERE _F AND _V + - name: CreateTimes + description: "Show the modified times of the \\SAM\\Domains\\Account\\Users\\Names keys" + query: | + SELECT Name AS Username, Mtime AS CreatedTime + FROM glob(globs='SAM\\Domains\\Account\\Users\\Names\\*', + root=pathspec(DelegatePath=SAMPath), + accessor="raw_reg") + WHERE Data.type =~ "Key" + column_types: - name: F type: hex diff --git a/artifacts/definitions/Windows/Network/NetstatEnriched.yaml b/artifacts/definitions/Windows/Network/NetstatEnriched.yaml index 8b3a417b41c..de0b385b5e8 100644 --- a/artifacts/definitions/Windows/Network/NetstatEnriched.yaml +++ b/artifacts/definitions/Windows/Network/NetstatEnriched.yaml @@ -6,17 +6,17 @@ description: | Examples include: Process name and path, authenticode information or network connection details. - - WARNING: - KillProcess - attempts to use Taskill to kill the processes returned. - DumpProcess - dumps the process as a sparse file for post processing. - - Please only use these switches after scoping as there are no guardrails on + + WARNING: + KillProcess - attempts to use Taskill to kill the processes returned. + DumpProcess - dumps the process as a sparse file for post processing. + + Please only use these switches after scoping as there are no guardrails on shooting yourself in the foot. required_permissions: - EXECVE - + precondition: SELECT OS From info() where OS = 'windows' parameters: @@ -80,7 +80,7 @@ parameters: - name: ProcessNameRegex description: "regex search over source process name" - default: ^malware\.exe$ + default: ^(malware\.exe|.*)$ type: regex - name: ProcessPathRegex description: "regex search over source process path" @@ -129,7 +129,7 @@ parameters: - name: KillProcess description: "WARNING: If selected will attempt to kill process from all results." type: bool - + sources: - name: Netstat query: | @@ -170,13 +170,13 @@ sources: FamilyString as Family, TypeString as Type, Status, - Laddr.IP as SrcIP, + Laddr.IP as SrcIP, Laddr.Port as SrcPort, - Raddr.IP as DestIP, + Raddr.IP as DestIP, Raddr.Port as DestPort, Timestamp FROM netstat() - WHERE + WHERE Name =~ ProcessNameRegex AND Path =~ ProcessPathRegex and CommandLine =~ CommandLineRegex @@ -195,7 +195,7 @@ sources: or format(format="%v", args=DestIP) =~ IPRegex ) and ( format(format="%v", args=SrcPort) =~ PortRegex or format(format="%v", args=DestPort) =~ PortRegex ) - + LET Regions(Pid) = SELECT dict(Offset=Address, Length=Size) AS Sparse FROM vad(pid=Pid) WHERE Protection =~ "r" @@ -207,19 +207,19 @@ sources: DelegatePath=format(format="/%d", args=Pid)), name=pathspec(Path=format(format="%d.dd", args=Pid))) AS ProcessMemory FROM results - LET kill = SELECT *, pskill(pid=Pid) AS KillProcess + LET kill = SELECT *, pskill(pid=Pid) AS KillProcess FROM results - LET dumpandkill = SELECT *, pskill(pid=Pid) AS KillProcess + LET dumpandkill = SELECT *, pskill(pid=Pid) AS KillProcess FROM dump - + SELECT * FROM switch( - a = { + a = { SELECT *, if(condition= KillProcess=Null,then='Success',else=KillProcess) AS KillProcess FROM if(condition= DumpProcess AND KillProcess, then= dumpandkill )}, b = { SELECT * FROM if(condition= DumpProcess, then= dump )}, - c = { + c = { SELECT *, if(condition= KillProcess=Null,then='Success',else=KillProcess) AS KillProcess - FROM if(condition= KillProcess, then= kill) + FROM if(condition= KillProcess, then= kill) }, catch = results ) diff --git a/artifacts/testdata/server/testcases/raw_registry.in.yaml b/artifacts/testdata/server/testcases/raw_registry.in.yaml index 560c53c0dad..78372d22814 100644 --- a/artifacts/testdata/server/testcases/raw_registry.in.yaml +++ b/artifacts/testdata/server/testcases/raw_registry.in.yaml @@ -18,7 +18,10 @@ Queries: # Test the SAM parsers - SELECT ParsedF, ParsedV FROM Artifact.Windows.Forensics.SAM( - SAMPath=srcDir+"/artifacts/testdata/files/SAM") + SAMPath=srcDir+"/artifacts/testdata/files/SAM", source="Parsed") + + - SELECT * FROM Artifact.Windows.Forensics.SAM( + SAMPath=srcDir+"/artifacts/testdata/files/SAM", source="CreateTimes") # Check raw reg can read values as files - REG_SZ. - SELECT utf16(string=read_file(filename=pathspec( diff --git a/artifacts/testdata/server/testcases/raw_registry.out.yaml b/artifacts/testdata/server/testcases/raw_registry.out.yaml index 2ac0386bdd5..562c0663f2c 100644 --- a/artifacts/testdata/server/testcases/raw_registry.out.yaml +++ b/artifacts/testdata/server/testcases/raw_registry.out.yaml @@ -319,7 +319,7 @@ SELECT mock(plugin='info', results=[dict(OS='windows'), dict(OS='windows')] ) FR } } } -]SELECT ParsedF, ParsedV FROM Artifact.Windows.Forensics.SAM( SAMPath=srcDir+"/artifacts/testdata/files/SAM")[ +]SELECT ParsedF, ParsedV FROM Artifact.Windows.Forensics.SAM( SAMPath=srcDir+"/artifacts/testdata/files/SAM", source="Parsed")[ { "ParsedF": { "LastLoginDate": "1601-01-01T00:00:00Z", @@ -456,6 +456,32 @@ SELECT mock(plugin='info', results=[dict(OS='windows'), dict(OS='windows')] ) FR "ntpwd_hash": "\u0002\u0002\u0010" } } +]SELECT * FROM Artifact.Windows.Forensics.SAM( SAMPath=srcDir+"/artifacts/testdata/files/SAM", source="CreateTimes")[ + { + "Username": "Administrator", + "CreatedTime": "2021-10-08T00:54:13Z", + "_Source": "Windows.Forensics.SAM/CreateTimes" + }, + { + "Username": "DefaultAccount", + "CreatedTime": "2021-10-08T00:54:13Z", + "_Source": "Windows.Forensics.SAM/CreateTimes" + }, + { + "Username": "Guest", + "CreatedTime": "2021-10-08T00:54:13Z", + "_Source": "Windows.Forensics.SAM/CreateTimes" + }, + { + "Username": "WDAGUtilityAccount", + "CreatedTime": "2021-10-08T00:54:13Z", + "_Source": "Windows.Forensics.SAM/CreateTimes" + }, + { + "Username": "test", + "CreatedTime": "2021-10-07T08:07:32Z", + "_Source": "Windows.Forensics.SAM/CreateTimes" + } ]SELECT utf16(string=read_file(filename=pathspec( Path="\\Root\\DeviceCensus\\Processor\\ProcessorIdentifier", DelegatePath=srcDir+"/artifacts/testdata/files/Amcache.hve", DelegateAccessor='file'), accessor="raw_reg")) FROM scope()[ { "utf16(string=read_file(filename=pathspec(Path=\"\\\\Root\\\\DeviceCensus\\\\Processor\\\\ProcessorIdentifier\", DelegatePath=srcDir + \"/artifacts/testdata/files/Amcache.hve\", DelegateAccessor='file'), accessor=\"raw_reg\"))": "Intel64 Family 6 Model 62 Stepping 4\u0000" diff --git a/constants/constants.go b/constants/constants.go index 9a98313da1d..cae3207924a 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -23,7 +23,7 @@ import ( ) var ( - VERSION = "0.7.0" + VERSION = "0.7.0-2" ) const ( diff --git a/flows/client_info.go b/flows/client_info.go index ced0b748381..bb78a4638ef 100644 --- a/flows/client_info.go +++ b/flows/client_info.go @@ -12,13 +12,12 @@ import ( // minions rathen than getting sent to event moniroting. func (self *ClientFlowRunner) maybeProcessClientInfo( ctx context.Context, client_id string, response *actions_proto.VQLResponse) error { - if response.Query == nil || response.Query.Name != "Server.Internal.ClientInfo" { return nil } - client_info := services.ClientInfo{} + client_info := &services.ClientInfo{} err := json.Unmarshal([]byte(response.JSONLResponse), &client_info.ClientInfo) if err != nil { return err @@ -30,33 +29,50 @@ func (self *ClientFlowRunner) maybeProcessClientInfo( return err } - old_client_info, err := client_info_manager.Get(ctx, client_id) + err = client_info_manager.Modify(ctx, client_id, + func(old_client_info *services.ClientInfo) (*services.ClientInfo, error) { + if old_client_info == nil { + return client_info, nil + } + + dirty := false + update := func(old, new *string) { + if *old != *new { + *old = *new + dirty = true + } + } + + // Now merge the new record with the old + old_client_info.ClientId = client_id + update(&old_client_info.Hostname, &client_info.Hostname) + update(&old_client_info.System, &client_info.System) + update(&old_client_info.Release, &client_info.Release) + update(&old_client_info.Architecture, &client_info.Architecture) + update(&old_client_info.Fqdn, &client_info.Fqdn) + update(&old_client_info.ClientName, &client_info.ClientName) + update(&old_client_info.ClientVersion, &client_info.ClientVersion) + update(&old_client_info.BuildUrl, &client_info.BuildUrl) + update(&old_client_info.BuildTime, &client_info.BuildTime) + + // Nothing to do ignore the update. + if !dirty { + return nil, nil + } + return old_client_info, nil + }) if err != nil { - return client_info_manager.Set(ctx, &client_info) + return err } - dirty := false - update := func(old, new *string) { - if *old != *new { - *old = *new - dirty = true + // Now update any labels baked into the client. + if len(client_info.Labels) > 0 { + labeler := services.GetLabeler(self.config_obj) + + for _, label := range client_info.Labels { + labeler.SetClientLabel(ctx, self.config_obj, client_id, label) } } - // Now merge the new record with the old - old_client_info.ClientId = client_id - update(&old_client_info.Hostname, &client_info.Hostname) - update(&old_client_info.System, &client_info.System) - update(&old_client_info.Release, &client_info.Release) - update(&old_client_info.Architecture, &client_info.Architecture) - update(&old_client_info.Fqdn, &client_info.Fqdn) - update(&old_client_info.ClientName, &client_info.ClientName) - update(&old_client_info.ClientVersion, &client_info.ClientVersion) - update(&old_client_info.BuildUrl, &client_info.BuildUrl) - update(&old_client_info.BuildTime, &client_info.BuildTime) - - if dirty { - return client_info_manager.Set(ctx, old_client_info) - } return nil } diff --git a/vql/psutils/process_darwin.go b/vql/psutils/process_darwin.go index b024ab1fba8..3415c9b089a 100644 --- a/vql/psutils/process_darwin.go +++ b/vql/psutils/process_darwin.go @@ -13,7 +13,6 @@ import ( "golang.org/x/sys/unix" "github.com/Velocidex/ordereddict" - "www.velocidex.com/golang/velociraptor/utils" ) func GetProcess(ctx context.Context, pid int32) (*ordereddict.Dict, error) { @@ -33,9 +32,6 @@ func ListProcesses(ctx context.Context) ([]*ordereddict.Dict, error) { } for _, item := range processes { - if false { - utils.Debug(item) - } result = append(result, getProcessData(ctx, &item)) }