diff --git a/api/artifacts.go b/api/artifacts.go
index 2c233b3833a..042be57fa75 100644
--- a/api/artifacts.go
+++ b/api/artifacts.go
@@ -44,6 +44,7 @@ import (
"www.velocidex.com/golang/velociraptor/logging"
"www.velocidex.com/golang/velociraptor/paths"
"www.velocidex.com/golang/velociraptor/result_sets"
+ "www.velocidex.com/golang/velociraptor/services"
users "www.velocidex.com/golang/velociraptor/users"
"www.velocidex.com/golang/velociraptor/utils"
)
@@ -115,60 +116,30 @@ func setArtifactFile(config_obj *config_proto.Config,
required_prefix string) (
*artifacts_proto.Artifact, error) {
- // First ensure that the artifact is correct.
- tmp_repository := artifacts.NewRepository()
- artifact_definition, err := tmp_repository.LoadYaml(
- in.Artifact, true /* validate */)
- if err != nil {
- return nil, err
- }
-
- if !strings.HasPrefix(artifact_definition.Name, required_prefix) {
- return nil, errors.New(
- "Modified or custom artifacts must start with '" +
- required_prefix + "'")
- }
-
- file_store_factory := file_store.GetFileStore(config_obj)
- vfs_path := paths.GetArtifactDefintionPath(artifact_definition.Name)
-
- // Load the new artifact into the global repo so it is
- // immediately available.
- global_repository, err := artifacts.GetGlobalRepository(config_obj)
- if err != nil {
- return nil, err
- }
+ repository_manager := services.GetRepositoryManager()
switch in.Op {
-
case api_proto.SetArtifactRequest_DELETE:
- global_repository.Del(artifact_definition.Name)
- err = file_store_factory.Delete(vfs_path)
- return artifact_definition, err
- case api_proto.SetArtifactRequest_SET:
- // Now write it into the filestore.
- fd, err := file_store_factory.WriteFile(vfs_path)
+ // First ensure that the artifact is correct.
+ tmp_repository := artifacts.NewRepository()
+ artifact_definition, err := tmp_repository.LoadYaml(
+ in.Artifact, true /* validate */)
if err != nil {
return nil, err
}
- defer fd.Close()
- // We want to completely replace the content of the file.
- err = fd.Truncate()
- if err != nil {
- return nil, err
+ if !strings.HasPrefix(artifact_definition.Name, required_prefix) {
+ return nil, errors.New(
+ "Modified or custom artifacts must start with '" +
+ required_prefix + "'")
}
- _, err = fd.Write([]byte(in.Artifact))
- if err != nil {
- return nil, err
- }
+ return artifact_definition, repository_manager.DeleteArtifactFile(
+ artifact_definition.Name)
- // Load the artifact into the currently running repository.
- // Artifact is already valid - no need to revalidate it again.
- _, err = global_repository.LoadYaml(in.Artifact, false /* validate */)
- return artifact_definition, err
+ case api_proto.SetArtifactRequest_SET:
+ return repository_manager.SetArtifactFile(in.Artifact, required_prefix)
}
return nil, errors.New("Unknown op")
diff --git a/artifacts/definitions/Generic/Client/Stats.yaml b/artifacts/definitions/Generic/Client/Stats.yaml
index ca186271bdf..d90b0a7def8 100644
--- a/artifacts/definitions/Generic/Client/Stats.yaml
+++ b/artifacts/definitions/Generic/Client/Stats.yaml
@@ -20,7 +20,7 @@ sources:
FROM pslist(pid=getpid())
})
- - precondition: SELECT OS From info() where OS = 'linux'
+ - precondition: SELECT OS From info() where OS != 'windows'
queries:
- SELECT * from foreach(
row={
diff --git a/artifacts/definitions/Server/Internal/ArtifactModification.yaml b/artifacts/definitions/Server/Internal/ArtifactModification.yaml
new file mode 100644
index 00000000000..57fdcf90e9e
--- /dev/null
+++ b/artifacts/definitions/Server/Internal/ArtifactModification.yaml
@@ -0,0 +1,10 @@
+name: Server.Internal.ArtifactModification
+description: |
+ This event artifact is an internal event stream over which
+ notifications of artifact modifications are sent. Interested parties
+ can watch for new artifact modification events and rebuild caches
+ etc.
+
+ Note: This is an automated system artifact. You do not need to start it.
+
+type: SERVER_EVENT
diff --git a/artifacts/definitions/Windows/EventLogs/PowershellModule.yaml b/artifacts/definitions/Windows/EventLogs/PowershellModule.yaml
index 686183e8461..be5db51950e 100644
--- a/artifacts/definitions/Windows/EventLogs/PowershellModule.yaml
+++ b/artifacts/definitions/Windows/EventLogs/PowershellModule.yaml
@@ -1,18 +1,18 @@
name: Windows.EventLogs.PowershellModule
description: |
- This Artifact will search and extract Module events (Event ID 4103) from
+ This Artifact will search and extract Module events (Event ID 4103) from
Powershell-Operational Event Logs.
-
- Powershell is commonly used by attackers accross all stages of the attack
+
+ Powershell is commonly used by attackers accross all stages of the attack
lifecycle. Although quite noisy Module logging can provide valuable insight.
-
- There are several parameter's availible for search leveraging regex.
- - DateAfter enables search for events after this date.
- - DateBefore enables search for events before this date.
- - ContextRegex enables regex search over ContextInfo text field.
- - PayloadRegex enables a regex search over Payload text field.
+
+ There are several parameter's availible for search leveraging regex.
+ - DateAfter enables search for events after this date.
+ - DateBefore enables search for events before this date.
+ - ContextRegex enables regex search over ContextInfo text field.
+ - PayloadRegex enables a regex search over Payload text field.
- SearchVSS enables VSS search
-
+
author: Matt Green - @mgreen27
@@ -35,14 +35,21 @@ parameters:
description: "regex search over Payload text field."
- name: SearchVSS
description: "Add VSS into query."
- type: bool
-
+ type: bool
+ - name: LogLevelMap
+ type: hidden
+ default: |
+ Choice,Regex
+ All,"."
+ Warning,"3"
+ Verbose,"5"
+
sources:
- query: |
-- Build time bounds
- LET DateAfterTime <= if(condition=DateAfter,
+ LET DateAfterTime <= if(condition=DateAfter,
then=timestamp(epoch=DateAfter), else=timestamp(epoch="1600-01-01"))
- LET DateBeforeTime <= if(condition=DateBefore,
+ LET DateBeforeTime <= if(condition=DateBefore,
then=timestamp(epoch=DateBefore), else=timestamp(epoch="2200-01-01"))
-- Parse Log level dropdown selection
@@ -67,7 +74,7 @@ sources:
FROM foreach(
row=files,
query={
- SELECT
+ SELECT
timestamp(epoch=System.TimeCreated.SystemTime) As EventTime,
System.EventID.Value as EventID,
System.Computer as Computer,
@@ -90,14 +97,14 @@ sources:
then=ContextInfo=~PayloadRegex,else=TRUE)
})
ORDER BY Source DESC
-
+
-- Group results for deduplication
LET grouped = SELECT *
FROM hits
GROUP BY EventRecordID
-- Output results
- SELECT
+ SELECT
EventTime,
EventID,
Computer,
@@ -110,4 +117,4 @@ sources:
Opcode,
Task,
Source
- FROM grouped
\ No newline at end of file
+ FROM grouped
diff --git a/artifacts/definitions/Windows/EventLogs/PowershellScriptblock.yaml b/artifacts/definitions/Windows/EventLogs/PowershellScriptblock.yaml
index d54f04e9bc1..89daa8207fd 100644
--- a/artifacts/definitions/Windows/EventLogs/PowershellScriptblock.yaml
+++ b/artifacts/definitions/Windows/EventLogs/PowershellScriptblock.yaml
@@ -1,23 +1,23 @@
name: Windows.EventLogs.PowershellScriptblock
description: |
- This Artifact will search and extract ScriptBlock events (Event ID 4104) from
+ This Artifact will search and extract ScriptBlock events (Event ID 4104) from
Powershell-Operational Event Logs.
-
- Powershell is commonly used by attackers accross all stages of the attack
- lifecycle. A valuable hunt is to search Scriptblock logs for signs of
+
+ Powershell is commonly used by attackers accross all stages of the attack
+ lifecycle. A valuable hunt is to search Scriptblock logs for signs of
malicious content.
-
- There are several parameter's availible for search leveraging regex.
- - DateAfter enables search for events after this date.
- - DateBefore enables search for events before this date.
- - SearchStrings enables regex search over scriptblock text field.
- - StringWhiteList enables a regex whitelist for scriptblock text field.
- - PathWhitelist enables a regex whitelist for path of scriptblock.
- - LogLevel enables searching on type of log. Default is Warning level
- which is logged even if ScriptBlock logging is turned off when
- suspicious keywords detected in Powershell interpreter. See second
+
+ There are several parameter's availible for search leveraging regex.
+ - DateAfter enables search for events after this date.
+ - DateBefore enables search for events before this date.
+ - SearchStrings enables regex search over scriptblock text field.
+ - StringWhiteList enables a regex whitelist for scriptblock text field.
+ - PathWhitelist enables a regex whitelist for path of scriptblock.
+ - LogLevel enables searching on type of log. Default is Warning level
+ which is logged even if ScriptBlock logging is turned off when
+ suspicious keywords detected in Powershell interpreter. See second
reference for list of keywords.
- - SearchVSS enables VSS search
+ - SearchVSS enables VSS search
author: Matt Green - @mgreen27
@@ -40,7 +40,7 @@ parameters:
description: "Regex of string to witelist"
- name: PathWhitelist
description: "Regex of path to whitelist."
-
+
- name: LogLevel
description: "Log level. Warning is Powershell default bad keyword list."
type: choices
@@ -58,14 +58,14 @@ parameters:
Verbose,"5"
- name: SearchVSS
description: "Add VSS into query."
- type: bool
-
+ type: bool
+
sources:
- query: |
-- Build time bounds
- LET DateAfterTime <= if(condition=DateAfter,
+ LET DateAfterTime <= if(condition=DateAfter,
then=timestamp(epoch=DateAfter), else=timestamp(epoch="1600-01-01"))
- LET DateBeforeTime <= if(condition=DateBefore,
+ LET DateBeforeTime <= if(condition=DateBefore,
then=timestamp(epoch=DateBefore), else=timestamp(epoch="2200-01-01"))
-- Parse Log level dropdown selection
@@ -81,7 +81,8 @@ sources:
FROM Artifact.Windows.Search.VSS(SearchFilesGlob=EventLog)
},
else= {
- SELECT * FROM glob(globs=EventLog)
+ SELECT *, FullPath AS Source
+ FROM glob(globs=EventLog)
})
-- Main query
@@ -101,19 +102,19 @@ sources:
System.Level as Level,
System.Opcode as Opcode,
System.Task as Task,
- if(condition=Source, then=Source, else=FullPath) as Source
+ Source
FROM parse_evtx(filename=FullPath)
WHERE System.EventID.Value = 4104
AND EventTime < DateBeforeTime
- AND EventTime > DateAfterTime
+ AND EventTime > DateAfterTime
AND format(format="%d", args=System.Level) =~ LogLevelRegex.value[0]
- AND if(condition=SearchStrings,
+ AND if(condition=SearchStrings,
then=ScriptBlockText =~ SearchStrings,
- else=TRUE)
- AND if(condition=StringWhitelist,
+ else=TRUE)
+ AND if(condition=StringWhitelist,
then= NOT ScriptBlockText =~ StringWhitelist,
- else=TRUE)
- AND if(condition=PathWhitelist,
+ else=TRUE)
+ AND if(condition=PathWhitelist,
then= NOT Path =~ PathWhitelist,
else=TRUE)
})
@@ -139,4 +140,4 @@ sources:
Opcode,
Task,
Source
- FROM grouped
\ No newline at end of file
+ FROM grouped
diff --git a/bin/query.go b/bin/query.go
index dbb86b4376d..6156ae84fe8 100644
--- a/bin/query.go
+++ b/bin/query.go
@@ -39,6 +39,7 @@ import (
"www.velocidex.com/golang/velociraptor/services/journal"
"www.velocidex.com/golang/velociraptor/services/labels"
"www.velocidex.com/golang/velociraptor/services/launcher"
+ "www.velocidex.com/golang/velociraptor/services/repository"
"www.velocidex.com/golang/velociraptor/uploads"
"www.velocidex.com/golang/velociraptor/utils"
vql_subsystem "www.velocidex.com/golang/velociraptor/vql"
@@ -199,6 +200,11 @@ func startEssentialServices(config_obj *config_proto.Config, sm *services.Servic
return err
}
+ err = sm.Start(repository.StartRepositoryManager)
+ if err != nil {
+ return err
+ }
+
if config_obj.Datastore != nil {
err = sm.Start(journal.StartJournalService)
if err != nil {
diff --git a/constants/constants.go b/constants/constants.go
index e2f2a3688e9..e5d8b2d877e 100644
--- a/constants/constants.go
+++ b/constants/constants.go
@@ -20,7 +20,7 @@ package constants
import "regexp"
const (
- VERSION = "0.4.7"
+ VERSION = "0.4.8"
ENROLLMENT_WELL_KNOWN_FLOW = "E:Enrol"
MONITORING_WELL_KNOWN_FLOW = FLOW_PREFIX + "Monitoring"
diff --git a/go.mod b/go.mod
index d83c9769a0e..31ceceddfc0 100644
--- a/go.mod
+++ b/go.mod
@@ -41,7 +41,7 @@ require (
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0
github.com/golang/snappy v0.0.1
github.com/google/rpmpack v0.0.0-20200615183209-0c831d19bd44
- github.com/google/uuid v1.1.1 // indirect
+ github.com/google/uuid v1.1.1
github.com/gookit/color v1.2.7
github.com/gorilla/csrf v1.6.2
github.com/gorilla/schema v1.1.0
diff --git a/gui/static/angular-components/artifact/client-event-directive.js b/gui/static/angular-components/artifact/client-event-directive.js
index 86021032496..8cff293441d 100644
--- a/gui/static/angular-components/artifact/client-event-directive.js
+++ b/gui/static/angular-components/artifact/client-event-directive.js
@@ -142,6 +142,29 @@ ClientEventController.prototype.showHelp = function() {
return false;
};
+ClientEventController.prototype.showClientMonitoringTables = function() {
+ var self = this;
+ var url = 'v1/GetClientMonitoringState';
+
+ this.error = "";
+ this.grrApiService_.get(url).then(function(response) {
+ self.state = response['data'];
+
+ var modalScope = self.scope_.$new();
+ var modalInstance = self.uibModal_.open({
+ template: '',
+ scope: modalScope,
+ windowClass: 'wide-modal high-modal',
+ size: 'lg'
+ });
+
+ modalScope["json"] = JSON.stringify(self.state, null, 2);
+ modalScope["resolve"] = modalInstance.close;
+ });
+
+ return false;
+};
// When the label changes, we need to switch the current table.
ClientEventController.prototype.onLabelChange = function() {
diff --git a/gui/static/angular-components/artifact/client-event.html b/gui/static/angular-components/artifact/client-event.html
index 92dab8f5c64..a25583acfa6 100644
--- a/gui/static/angular-components/artifact/client-event.html
+++ b/gui/static/angular-components/artifact/client-event.html
@@ -21,6 +21,13 @@
+
+