Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ go_library(
deps = [
"//tables/alt_system_info",
"//tables/authdb",
"//tables/crowdstrike_falcon",
"//tables/chromeuserprofiles",
"//tables/fileline",
"//tables/filevaultusers",
Expand Down
39 changes: 20 additions & 19 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.2.5
1.2.6
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/macadmins/osquery-extension/tables/alt_system_info"
"github.com/macadmins/osquery-extension/tables/chromeuserprofiles"
"github.com/macadmins/osquery-extension/tables/crowdstrike_falcon"
"github.com/macadmins/osquery-extension/tables/fileline"
"github.com/macadmins/osquery-extension/tables/filevaultusers"
macosprofiles "github.com/macadmins/osquery-extension/tables/macos_profiles"
Expand Down Expand Up @@ -78,6 +79,7 @@ func main() {

if runtime.GOOS == "darwin" {
darwinPlugins := []osquery.OsqueryPlugin{
table.NewPlugin("crowdstrike_falcon", crowdstrike_falcon.CrowdstrikeFalconColumns(), crowdstrike_falcon.CrowdstrikeFalconGenerate),
table.NewPlugin("filevault_users", filevaultusers.FileVaultUsersColumns(), filevaultusers.FileVaultUsersGenerate),
table.NewPlugin("macos_profiles", macosprofiles.MacOSProfilesColumns(), macosprofiles.MacOSProfilesGenerate),
table.NewPlugin("mdm", mdm.MDMInfoColumns(), mdm.MDMInfoGenerate),
Expand Down
24 changes: 24 additions & 0 deletions tables/crowdstrike_falcon/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "crowdstrike_falcon",
srcs = ["crowdstrike_falcon.go"],
importpath = "github.com/macadmins/osquery-extension/tables/crowdstrike_falcon",
visibility = ["//visibility:public"],
deps = [
"//pkg/utils",
"@com_github_groob_plist//:plist",
"@com_github_osquery_osquery_go//plugin/table",
"@com_github_pkg_errors//:errors",
],
)

go_test(
name = "crowdstrike_falcon_test",
srcs = ["crowdstrike_falcon_test.go"],
embed = [":crowdstrike_falcon"],
deps = [
"//pkg/utils",
"@com_github_stretchr_testify//assert",
],
)
113 changes: 113 additions & 0 deletions tables/crowdstrike_falcon/crowdstrike_falcon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package crowdstrike_falcon

import (
"context"
"fmt"
"os"
"regexp"
"runtime"
"strconv"
"strings"

"github.com/groob/plist"
"github.com/macadmins/osquery-extension/pkg/utils"
"github.com/osquery/osquery-go/plugin/table"
"github.com/pkg/errors"
)

var falconCtlPath = map[string]string{
"linux": "/opt/CrowdStrike/falconctl",
"darwin": "/Applications/Falcon.app/Contents/Resources/falconctl",
}

type CrowdStrikeOutput struct {
AgentID string `plist:"aid"`
CID string `plist:"cid"`
FalconVersion string `plist:"falcon_version"`
ReducedFunctionalityMode bool `plist:"rfm"`
SensorLoaded bool `plist:"sensor_loaded"`
}

func CrowdstrikeFalconColumns() []table.ColumnDefinition {
return []table.ColumnDefinition{
table.TextColumn("agent_id"),
table.TextColumn("cid"),
table.TextColumn("falcon_version"),
table.TextColumn("reduced_functionality_mode"),
table.TextColumn("sensor_loaded"),
}
}

func CrowdstrikeFalconGenerate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
var results []map[string]string
r := utils.NewRunner()
fs := utils.OSFileSystem{}
output, err := runCrowdstrikeFalcon(r, fs)
if err != nil {
fmt.Println(err)
return results, err
}

results = append(results, map[string]string{
"agent_id": output.AgentID,
"cid": output.CID,
"falcon_version": output.FalconVersion,
"reduced_functionality_mode": strconv.FormatBool(output.ReducedFunctionalityMode),
"sensor_loaded": strconv.FormatBool(output.SensorLoaded),
})

return results, nil
}

func runCrowdstrikeFalcon(r utils.Runner, fs utils.FileSystem) (CrowdStrikeOutput, error) {
var output CrowdStrikeOutput

_, err := fs.Stat(falconCtlPath[runtime.GOOS])
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return output, nil
}
return output, err
}
switch runtime.GOOS {
case "darwin":
out, err := r.Runner.RunCmd(falconCtlPath[runtime.GOOS], "info")
if err != nil {
return output, errors.Wrap(err, falconCtlPath[runtime.GOOS]+" info")
}
if err := plist.Unmarshal(out, &output); err != nil {
return output, errors.Wrap(err, "unmarshalling falconctl output")
}
case "linux":
out, err := r.Runner.RunCmd(falconCtlPath[runtime.GOOS], "-g", "--aid")
if err != nil {
return output, errors.Wrap(err, falconCtlPath[runtime.GOOS]+" -g --aid")
}
agentIdRegex := regexp.MustCompile(`[a-f0-9]{16}`)
output.AgentID = agentIdRegex.FindStringSubmatch(strings.ToLower(string(out)))[1]

out, err = r.Runner.RunCmd(falconCtlPath[runtime.GOOS], "-g", "--cid")
if err != nil {
return output, errors.Wrap(err, falconCtlPath[runtime.GOOS]+" -g --cid")
}
cidRegex := regexp.MustCompile(`[a-f0-9]{16}`)
output.CID = cidRegex.FindStringSubmatch(strings.ToLower(string(out)))[1]

out, err = r.Runner.RunCmd(falconCtlPath[runtime.GOOS], "-g", "--rfm-state")
if err != nil {
return output, errors.Wrap(err, falconCtlPath[runtime.GOOS]+" -g --rfm-state")
}
rfmState := strings.Split(strings.ToLower(string(out)), "=")[1]
output.ReducedFunctionalityMode = rfmState == "true"

out, err = r.Runner.RunCmd(falconCtlPath[runtime.GOOS], "-g", "--version")
if err != nil {
return output, errors.Wrap(err, falconCtlPath[runtime.GOOS]+" -g --version")
}
version := strings.TrimSpace(strings.Split(strings.ToLower(string(out)), "=")[1])
output.FalconVersion = version

}

return output, nil
}
95 changes: 95 additions & 0 deletions tables/crowdstrike_falcon/crowdstrike_falcon_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package crowdstrike_falcon

import (
"testing"

"github.com/macadmins/osquery-extension/pkg/utils"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)

func TestRunCrowdstrikeFalcon(t *testing.T) {
tests := []struct {
name string
mockCmd utils.MockCmdRunner
fileExist bool
wantErr bool
}{
{
name: "Binary not present",
mockCmd: utils.MockCmdRunner{
Output: "",
Err: nil,
},
fileExist: false,
wantErr: false,
},
{
name: "Command execution error",
mockCmd: utils.MockCmdRunner{
Output: "",
Err: errors.New("command error"),
},
fileExist: true,
wantErr: true,
},
{
name: "Successful execution",
mockCmd: utils.MockCmdRunner{
Output: `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aid</key>
<string>DEADBEEF</string>
<key>falcon_version</key>
<string>7.26.19707.0</string>
<key>rfm</key>
<false/>
<key>sensor_loaded</key>
<true/>
</dict>
</plist>`,
Err: nil,
},
fileExist: true,
wantErr: false,
},
{
name: "JSON unmarshal error",
mockCmd: utils.MockCmdRunner{
Output: `invalid json`,
Err: nil,
},
fileExist: true,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
runner := utils.Runner{Runner: tt.mockCmd}
fs := utils.MockFileSystem{FileExists: tt.fileExist}

output, err := runCrowdstrikeFalcon(runner, fs)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
if tt.fileExist {
assert.NotEmpty(t, output)
if tt.name == "Successful execution" {
expectedOutput := CrowdStrikeOutput{
AgentID: "DEADBEEF",
FalconVersion: "7.26.19707.0",
ReducedFunctionalityMode: false,
SensorLoaded: true}
assert.Equal(t, expectedOutput, output)
}
} else {
assert.Empty(t, output)
}
}
})
}
}