From 346f26538749341116a63108345a066a3f2257c1 Mon Sep 17 00:00:00 2001 From: Scott Gress Date: Wed, 8 Jan 2025 11:55:24 -0600 Subject: [PATCH 1/5] update live query to support yara queries --- cmd/osquery-perf/agent.go | 76 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/cmd/osquery-perf/agent.go b/cmd/osquery-perf/agent.go index 0671766fc2ed..0ccaf7da5a9e 100644 --- a/cmd/osquery-perf/agent.go +++ b/cmd/osquery-perf/agent.go @@ -18,6 +18,7 @@ import ( "net/http" _ "net/http/pprof" "os" + "regexp" "sort" "strconv" "strings" @@ -2090,10 +2091,83 @@ func (a *agent) runLiveQuery(query string) (results []map[string]string, status ss := fleet.OsqueryStatus(1) return []map[string]string{}, &ss, ptr.String("live query failed with error foobar"), nil } - ss := fleet.OsqueryStatus(0) if a.liveQueryNoResultsProb > 0.0 && rand.Float64() <= a.liveQueryNoResultsProb { + ss := fleet.OsqueryStatus(0) return []map[string]string{}, &ss, nil, nil } + + // Switch based on contents of the query. + lcQuery := strings.ToLower(query) + switch { + case strings.Contains(lcQuery, "from yara") && strings.Contains(lcQuery, "sigurl"): + return a.RunLiveYaraQuery(query) + default: + return a.RunLiveMockQuery(query) + } +} + +func (a *agent) RunLiveYaraQuery(query string) (results []map[string]string, status *fleet.OsqueryStatus, message *string, stats *fleet.Stats) { + // Get the URL of the YARA rule to request (i.e. the sigurl). + urlRegex := regexp.MustCompile(`sigurl=(["'])([^"']*)\1`) + matches := urlRegex.FindStringSubmatch(query) + var url string + if len(matches) > 2 { + url = matches[2] + } else { + ss := fleet.OsqueryStatus(1) + return []map[string]string{}, &ss, ptr.String("live yara query failed because a valid sigurl could not be found"), nil + } + + // Osquery validates that the sigurl is one of a configured set, so that it's not + // sending requests to just anywhere. We'll check that it's at least the same host + // as the Fleet server. + if !strings.HasPrefix(url, a.serverAddress) { + ss := fleet.OsqueryStatus(1) + return []map[string]string{}, &ss, ptr.String("live yara query failed because sigurl host did not match server address"), nil + } + + // Make the request. + body := []byte(`{"node_key": "` + a.nodeKey + `"}`) + request, err := http.NewRequest("POST", url, bytes.NewReader(body)) + if err != nil { + ss := fleet.OsqueryStatus(1) + return []map[string]string{}, &ss, ptr.String("live yara query failed due to error creating request"), nil + } + request.Header.Add("Content-type", "application/json") + + // Make the request. For load testing purposes we don't actually care about the response. + response, err := http.DefaultClient.Do(request) + if err != nil { + ss := fleet.OsqueryStatus(1) + return []map[string]string{}, &ss, ptr.String(fmt.Sprintf("yara request failed to run: %v", err)), nil + } + defer response.Body.Close() + + // Return a response indicating that the file is clean. + ss := fleet.OsqueryStatus(0) + return []map[string]string{ + { + "count": "0", + "matches": "", + "strings": "", + "tags": "", + "sig_group": "", + "sigfile": "", + "sigrule": "", + "sigurl": url, + // Could pull this from the query, but not necessary for load testing. + "path": "/some/path", + }, + }, &ss, nil, &fleet.Stats{ + WallTimeMs: uint64(rand.Intn(1000) * 1000), + UserTime: uint64(rand.Intn(1000)), + SystemTime: uint64(rand.Intn(1000)), + Memory: uint64(rand.Intn(1000)), + } +} + +func (a *agent) RunLiveMockQuery(query string) (results []map[string]string, status *fleet.OsqueryStatus, message *string, stats *fleet.Stats) { + ss := fleet.OsqueryStatus(0) return []map[string]string{ { "admindir": "/var/lib/dpkg", From 02c28534e20fc353b8251edf6e43c3d1b8def49c Mon Sep 17 00:00:00 2001 From: Scott Gress Date: Wed, 8 Jan 2025 15:47:24 -0600 Subject: [PATCH 2/5] fix regex --- cmd/osquery-perf/agent.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/osquery-perf/agent.go b/cmd/osquery-perf/agent.go index 0ccaf7da5a9e..95aee4cbfd0a 100644 --- a/cmd/osquery-perf/agent.go +++ b/cmd/osquery-perf/agent.go @@ -2108,7 +2108,7 @@ func (a *agent) runLiveQuery(query string) (results []map[string]string, status func (a *agent) RunLiveYaraQuery(query string) (results []map[string]string, status *fleet.OsqueryStatus, message *string, stats *fleet.Stats) { // Get the URL of the YARA rule to request (i.e. the sigurl). - urlRegex := regexp.MustCompile(`sigurl=(["'])([^"']*)\1`) + urlRegex := regexp.MustCompile(`sigurl=(["'])([^"']*)["']`) matches := urlRegex.FindStringSubmatch(query) var url string if len(matches) > 2 { From 85932dc5f45e33ef761f9437a0be8b8d9822446e Mon Sep 17 00:00:00 2001 From: Scott Gress Date: Wed, 8 Jan 2025 16:01:07 -0600 Subject: [PATCH 3/5] make methods private --- cmd/osquery-perf/agent.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/osquery-perf/agent.go b/cmd/osquery-perf/agent.go index 95aee4cbfd0a..37d087ba7c8d 100644 --- a/cmd/osquery-perf/agent.go +++ b/cmd/osquery-perf/agent.go @@ -2100,13 +2100,13 @@ func (a *agent) runLiveQuery(query string) (results []map[string]string, status lcQuery := strings.ToLower(query) switch { case strings.Contains(lcQuery, "from yara") && strings.Contains(lcQuery, "sigurl"): - return a.RunLiveYaraQuery(query) + return a.runLiveYaraQuery(query) default: - return a.RunLiveMockQuery(query) + return a.runLiveMockQuery(query) } } -func (a *agent) RunLiveYaraQuery(query string) (results []map[string]string, status *fleet.OsqueryStatus, message *string, stats *fleet.Stats) { +func (a *agent) runLiveYaraQuery(query string) (results []map[string]string, status *fleet.OsqueryStatus, message *string, stats *fleet.Stats) { // Get the URL of the YARA rule to request (i.e. the sigurl). urlRegex := regexp.MustCompile(`sigurl=(["'])([^"']*)["']`) matches := urlRegex.FindStringSubmatch(query) @@ -2166,7 +2166,7 @@ func (a *agent) RunLiveYaraQuery(query string) (results []map[string]string, sta } } -func (a *agent) RunLiveMockQuery(query string) (results []map[string]string, status *fleet.OsqueryStatus, message *string, stats *fleet.Stats) { +func (a *agent) runLiveMockQuery(query string) (results []map[string]string, status *fleet.OsqueryStatus, message *string, stats *fleet.Stats) { ss := fleet.OsqueryStatus(0) return []map[string]string{ { From 5cc6717b2c501e6fc164a98a23d6f3d95c34ea98 Mon Sep 17 00:00:00 2001 From: Scott Gress Date: Wed, 8 Jan 2025 17:12:42 -0600 Subject: [PATCH 4/5] read response body --- cmd/osquery-perf/agent.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/osquery-perf/agent.go b/cmd/osquery-perf/agent.go index 37d087ba7c8d..25e16c13ded8 100644 --- a/cmd/osquery-perf/agent.go +++ b/cmd/osquery-perf/agent.go @@ -2135,12 +2135,17 @@ func (a *agent) runLiveYaraQuery(query string) (results []map[string]string, sta } request.Header.Add("Content-type", "application/json") - // Make the request. For load testing purposes we don't actually care about the response. + // Make the request. response, err := http.DefaultClient.Do(request) if err != nil { ss := fleet.OsqueryStatus(1) return []map[string]string{}, &ss, ptr.String(fmt.Sprintf("yara request failed to run: %v", err)), nil } + // For load testing purposes we don't actually care about the response, but check that we at least got one. + if _, err := io.Copy(io.Discard, response.Body); err != nil { + ss := fleet.OsqueryStatus(1) + return []map[string]string{}, &ss, ptr.String(fmt.Sprintf("error reading response from yara API: %v", err)), nil + } defer response.Body.Close() // Return a response indicating that the file is clean. From f08a80285e1441b4277864a13eaeb01ff07a4711 Mon Sep 17 00:00:00 2001 From: Scott Gress Date: Thu, 9 Jan 2025 09:29:29 -0600 Subject: [PATCH 5/5] move defer --- cmd/osquery-perf/agent.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/osquery-perf/agent.go b/cmd/osquery-perf/agent.go index 25e16c13ded8..388f08537a83 100644 --- a/cmd/osquery-perf/agent.go +++ b/cmd/osquery-perf/agent.go @@ -2141,12 +2141,13 @@ func (a *agent) runLiveYaraQuery(query string) (results []map[string]string, sta ss := fleet.OsqueryStatus(1) return []map[string]string{}, &ss, ptr.String(fmt.Sprintf("yara request failed to run: %v", err)), nil } + defer response.Body.Close() + // For load testing purposes we don't actually care about the response, but check that we at least got one. if _, err := io.Copy(io.Discard, response.Body); err != nil { ss := fleet.OsqueryStatus(1) return []map[string]string{}, &ss, ptr.String(fmt.Sprintf("error reading response from yara API: %v", err)), nil } - defer response.Body.Close() // Return a response indicating that the file is clean. ss := fleet.OsqueryStatus(0)