Skip to content

Commit c85f665

Browse files
committed
Added security example
1 parent bd44bca commit c85f665

File tree

7 files changed

+111
-26
lines changed

7 files changed

+111
-26
lines changed

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ ctx, err := context.New(data)
2828
if err != nil {
2929
log.Fatal(err)
3030
}
31-
result, err := expression.Evaluate(ctx, `@len( @select( $resources.config_maps, "$elt.firstName == 'John'" ) ) > 0`)
31+
result, err := expression.EvaluateContext(ctx, `@len( @select( $resources.config_maps, "$elt.firstName == 'John'" ) ) > 0`)
3232
if result {
3333
fmt.Println("found John!")
3434
}
@@ -43,6 +43,10 @@ func MyFunction(args []interface{})(interface{}, error) {
4343
}
4444

4545
ctx, _ := context.New(data, context.Func("@myfunc", MyFunction))
46-
result, _ := expression.Evaluate(ctx, `@myfunc($foo) == "hello"`)
46+
result, _ := expression.EvaluateContext(ctx, `@myfunc($foo) == "hello"`)
4747

48-
```
48+
```
49+
50+
## Examples
51+
Programs illustrating the usage of Analyze can be found in the examples directory. Also see the
52+
unit tests in the analyzer/expression package for more examples of expressions and how they are used.

context/builtin_functions.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ func _has(args []interface{})(interface{},error){
101101
return nil, errors.New(errors.TypeMismatch, "expected object for first argument of has function")
102102
}
103103

104+
// @match(string, regex-string) returns true if string matches regex-string of the form /regular expression/
104105
func _match(args []interface{})(interface{},error){
105106
if len(args) != 2 {
106107
return nil, errors.New(errors.SyntaxError, "match expects 2 arguments")

examples/security/README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Example Security Check
2+
3+
Attackers will sometimes leave a malicious process running but delete the original binary
4+
on disk. This example will use OSQuery to run a query that returns processes in which the
5+
original binary has been deleted from disk. The example converts the OSQuery results into something
6+
analyzer can consume and then uses analyzer to interpret the results.
7+
8+
The check is defined in the config.json file included with the example.
9+
```json
10+
{
11+
"collectors": [
12+
{
13+
"id": "process-check",
14+
"expression": "SELECT name, path, pid FROM processes WHERE on_disk = 0;"
15+
}
16+
],
17+
"checks": [
18+
{
19+
"collector-id": "process-check",
20+
"Description": "processes running without binary on disk",
21+
"conditions": [
22+
{
23+
"type": "fail",
24+
"predicate": "@len( $ ) > 2",
25+
"message": "lots of possible malicious processes exist"
26+
},
27+
{
28+
"type": "warning",
29+
"predicate": "@len( $ ) > 0 && @len( $ ) <= 2",
30+
"message": "possible malicious processes exist"
31+
},
32+
{
33+
"type": "pass",
34+
"predicate": "@len( $ ) == 0",
35+
"message": "no suspect processes found"
36+
}
37+
]
38+
}
39+
]
40+
}
41+
```

examples/security/config.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,13 @@
1111
"Description": "processes running without binary on disk",
1212
"conditions": [
1313
{
14-
"type": "warn",
15-
"predicate": "@len( $ ) > 0",
14+
"type": "fail",
15+
"predicate": "@len( $ ) > 2",
16+
"message": "lots of possible malicious processes exist"
17+
},
18+
{
19+
"type": "warning",
20+
"predicate": "@len( $ ) > 0 && @len( $ ) <= 2",
1621
"message": "possible malicious processes exist"
1722
},
1823
{

examples/security/go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@ module github.com/murphybytes/analyze/examples/security
22

33
go 1.16
44

5-
require github.com/osquery/osquery-go v0.0.0-20210622151333-99b4efa62ec5
5+
require (
6+
github.com/murphybytes/analyze v0.0.0-20211113025243-bd44bca5ce89
7+
github.com/osquery/osquery-go v0.0.0-20210622151333-99b4efa62ec5
8+
)

examples/security/go.sum

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
11
github.com/Microsoft/go-winio v0.4.9 h1:3RbgqgGVqmcpbOiwrjbVtDHLlJBGF6aE+yHmNtBNsFQ=
22
github.com/Microsoft/go-winio v0.4.9/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
3+
github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c=
4+
github.com/alecthomas/participle/v2 v2.0.0-alpha7/go.mod h1:NumScqsC42o9x+dGj8/YqsIfhrIQjFEOFovxotbBirA=
5+
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E=
6+
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
37
github.com/apache/thrift v0.13.1-0.20200603211036-eac4d0c79a5f h1:33BV5v3u8I6dA2dEoPuXWCsAaHHOJfPtdxZhAMQV4uo=
48
github.com/apache/thrift v0.13.1-0.20200603211036-eac4d0c79a5f/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
9+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
510
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
611
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
12+
github.com/murphybytes/analyze v0.0.0-20211113025243-bd44bca5ce89 h1:x+rVyFOg9WtPtMGB3jfSFpvWkyHW4iaIEARqir44hQw=
13+
github.com/murphybytes/analyze v0.0.0-20211113025243-bd44bca5ce89/go.mod h1:CeNT1Yh03Z8CipVMl8OEbZ3QN2bAcBwwrEYpl0oNi+I=
714
github.com/osquery/osquery-go v0.0.0-20210622151333-99b4efa62ec5 h1:E275nJIUAvIK/RSN8cq9MAcRLk23jaZq+s24B0I8bEw=
815
github.com/osquery/osquery-go v0.0.0-20210622151333-99b4efa62ec5/go.mod h1:JKR5QhjsYdnIPY7hakgas5sxf8qlA/9wQnLqaMfWdcg=
916
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
1017
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
1118
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1219
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
13-
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
20+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1421
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
22+
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
23+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
24+
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
25+
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
1526
golang.org/x/sys v0.0.0-20210603125802-9665404d3644 h1:CA1DEQ4NdKphKeL70tvsWNdT5oFh1lOjihRcEDROi0I=
1627
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
28+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
29+
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
30+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

examples/security/main.go

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,75 +16,92 @@ func main() {
1616
var osQuerySockPath, configPath string
1717

1818
flag.StringVar(&configPath, "config", "conf.json", "the path to the json configuration defining checks")
19-
flag.StringVar(&osQuerySockPath, "osquery", "/var/osquery/osquery.em", "the path to the osquery socket")
19+
flag.StringVar(&osQuerySockPath, "osquery", "/var/osquery/osquery.em", "the path to the osquery socket")
2020
flag.Parse()
2121

2222
client, err := osquery.NewClient(osQuerySockPath, 10*time.Second)
2323
if err != nil {
24-
log.Fatalf("could not create osquery client %q", err )
24+
log.Fatalf("could not create osquery client %q", err)
2525
}
2626
defer client.Close()
2727

2828
buff, err := os.ReadFile(configPath)
2929
if err != nil {
30-
log.Fatalf("could not read config file %q", err )
30+
log.Fatalf("could not read config file %q", err)
3131
}
3232
var config AnalysisConfig
3333
if err := json.NewDecoder(bytes.NewBuffer(buff)).Decode(&config); err != nil {
34-
log.Fatalf("could not decode config file %q", err )
34+
log.Fatalf("could not decode config file %q", err)
3535
}
3636

3737
datas, err := collect(client, config.Collectors)
3838
if err != nil {
39-
log.Fatalf("collection step failed %q", err )
39+
log.Fatalf("collection step failed %q", err)
4040
}
4141

4242
for _, check := range config.Checks {
43+
log.Printf("check %s", check.Description)
4344
data, ok := datas[check.CollectorID]
4445
if !ok {
45-
log.Fatalf("no collector %q for check %q", check.CollectorID, check.Description)
46+
log.Fatalf("no collector %q", check.CollectorID)
47+
}
48+
for _, condition := range check.Conditions {
49+
passed, err := expression.Evaluate(data, condition.Predicate)
50+
if err != nil {
51+
log.Fatalf("an error occured %q evaluating predicate %q", err, condition.Predicate)
52+
}
53+
if passed {
54+
log.Printf("%s: %s", condition.Type, condition.Message)
55+
}
4656
}
4757

4858
}
49-
5059
}
5160

5261
type Collector struct {
5362
ID string `json:"id"`
5463
// Expression retrieve data from OSQuery
5564
Expression string `json:"expression"`
56-
5765
}
5866

5967
type Condition struct {
60-
Type string `json:"type"`
68+
Type string `json:"type"`
6169
Predicate string `json:"predicate"`
62-
Message string `json:"message"`
70+
Message string `json:"message"`
6371
}
6472

6573
type Check struct {
66-
CollectorID string `json:"collector-id"`
67-
Description string `json:"description"`
68-
Conditions []Condition `json:"conditions"`
69-
74+
CollectorID string `json:"collector-id"`
75+
Description string `json:"description"`
76+
Conditions []Condition `json:"conditions"`
7077
}
7178

7279
type AnalysisConfig struct {
7380
Collectors []Collector `json:"collectors"`
74-
Checks []Check `json:"checks"`
75-
81+
Checks []Check `json:"checks"`
7682
}
7783

7884
type collectorMap map[string]interface{}
7985

80-
func collect(client *osquery.ExtensionManagerClient, collectors []Collector)(collectorMap, error ) {
86+
// convert []map[string]string to something analyze can handle []interface{} where interface{} is map[string]interface{}
87+
func collect(client *osquery.ExtensionManagerClient, collectors []Collector) (collectorMap, error) {
8188
results := make(collectorMap)
8289
for _, coll := range collectors {
83-
result, err := client.QueryRows(coll.Expression)
90+
rows, err := client.QueryRows(coll.Expression)
8491
if err != nil {
8592
return nil, err
8693
}
87-
results[coll.ID] = result
94+
var objects []interface{}
95+
for _, row := range rows {
96+
object := make(map[string]interface{})
97+
for k, v := range row {
98+
object[k] = v
99+
}
100+
objects = append(objects, object)
101+
102+
}
103+
results[coll.ID] = objects
104+
88105
}
89106
return results, nil
90107
}

0 commit comments

Comments
 (0)