Skip to content

Commit

Permalink
Send hard coded labels in periodic client info updates (#2935)
Browse files Browse the repository at this point in the history
It is possible to hard code labels into the config file. These labels
will be automatically added when an interrogation process occurs

This PR also includes them in periodic client info updates
  • Loading branch information
scudette authored Sep 8, 2023
1 parent 7e6fdfe commit 4eaf165
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 53 deletions.
5 changes: 5 additions & 0 deletions actions/client_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
16 changes: 13 additions & 3 deletions artifacts/definitions/Windows/Forensics/SAM.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down
38 changes: 19 additions & 19 deletions artifacts/definitions/Windows/Network/NetstatEnriched.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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: |
Expand Down Expand Up @@ -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
Expand All @@ -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"
Expand All @@ -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
)
5 changes: 4 additions & 1 deletion artifacts/testdata/server/testcases/raw_registry.in.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
28 changes: 27 additions & 1 deletion artifacts/testdata/server/testcases/raw_registry.out.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
)

var (
VERSION = "0.7.0"
VERSION = "0.7.0-2"
)

const (
Expand Down
64 changes: 40 additions & 24 deletions flows/client_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
4 changes: 0 additions & 4 deletions vql/psutils/process_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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))
}

Expand Down

0 comments on commit 4eaf165

Please sign in to comment.