Skip to content

Commit

Permalink
- windows specific Sysmon tests
Browse files Browse the repository at this point in the history
- API changed in sysmon package
- removed unused utilities
  • Loading branch information
qjerome committed Jun 28, 2022
1 parent 6b6a327 commit 74169e8
Show file tree
Hide file tree
Showing 12 changed files with 479 additions and 346 deletions.
2 changes: 1 addition & 1 deletion .github/coverage/badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions .github/coverage/coverage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ github.com/0xrawsec/whids/api/manager.go:411: GetCommand 66.7%
github.com/0xrawsec/whids/api/manager.go:425: Endpoint 100.0%
github.com/0xrawsec/whids/api/manager.go:438: Endpoints 85.7%
github.com/0xrawsec/whids/api/manager.go:454: ImportRules 0.0%
github.com/0xrawsec/whids/api/manager.go:479: CreateNewAdminAPIUser 83.3%
github.com/0xrawsec/whids/api/manager.go:479: CreateNewAdminAPIUser 50.0%
github.com/0xrawsec/whids/api/manager.go:492: AddEndpoint 100.0%
github.com/0xrawsec/whids/api/manager.go:497: UpdateReducer 100.0%
github.com/0xrawsec/whids/api/manager.go:512: logAPIErrorf 100.0%
Expand Down Expand Up @@ -261,4 +261,4 @@ github.com/0xrawsec/whids/sysmon/config.go:195: Sha256 80.0%
github.com/0xrawsec/whids/sysmon/config.go:210: MarshalXML 100.0%
github.com/0xrawsec/whids/sysmon/config.go:214: UnmarshalXML 80.0%
github.com/0xrawsec/whids/sysmon/default.go:64: AgnosticConfig 0.0%
total: (statements) 69.6%
total: (statements) 69.5%
318 changes: 176 additions & 142 deletions api/openapi_def.go

Large diffs are not rendered by default.

318 changes: 176 additions & 142 deletions doc/admin.openapi.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion hids/hids.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,8 +543,12 @@ of the two versions is installed.
*/
func (h *HIDS) updateSysmon() (err error) {
var version string
var si *sysmon.Info

if si, err = sysmon.NewSysmonInfo(); err != nil {
return
}

si := sysmon.NewSysmonInfo()
sysmonPath := filepath.Join(toolsDir, tools.WithExecExt(tools.ToolSysmon))

if !fsutil.IsFile(sysmonPath) {
Expand Down
2 changes: 2 additions & 0 deletions hids/sysinfo/sysinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,6 @@ type SystemInfo struct {
} `json:"cpu"`

Sysmon *sysmon.Info `json:"sysmon"`

Err error `json:"error"`
}
7 changes: 6 additions & 1 deletion hids/sysinfo/windows_sysinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ var (
)

func NewSystemInfo() (info *SystemInfo) {
var err error

info = &SystemInfo{
Edr: edrInfo,
}
Expand All @@ -54,6 +56,9 @@ func NewSystemInfo() (info *SystemInfo) {
procs, _ := advapi32.RegEnumKeys(pathProcInfo)
info.CPU.Count = len(procs)

info.Sysmon = sysmon.NewSysmonInfo()
if info.Sysmon, err = sysmon.NewSysmonInfo(); err != nil {
info.Err = err
}

return
}
1 change: 0 additions & 1 deletion sysmon/sysmon.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ var (
)

type Info struct {
Err error `json:"error,omitempty"`
Version string `json:"version"`

Service struct {
Expand Down
71 changes: 49 additions & 22 deletions sysmon/sysmon_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package sysmon

import (
"errors"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -31,6 +32,8 @@ var (
`(?s:System\sMonitor\s(?P<version>v\d+(\.\d+)?)\s-[.\s].*` +
`<manifest schemaversion="(?P<schemaversion>\d+(\.\d+)?)" binaryversion="(?P<binaryversion>\d+(\.\d+)?)">)`,
)

defaultTimeout = 5 * time.Second
)

func Versions(image string) (version, schema, binary string, err error) {
Expand All @@ -41,14 +44,14 @@ func Versions(image string) (version, schema, binary string, err error) {
if fsutil.IsFile(image) {

c := command.CommandTimeout(
time.Second*2,
defaultTimeout,
image,
"-s",
)
defer c.Terminate()

if out, err = c.CombinedOutput(); err != nil {
err = fmt.Errorf("failed to run sysmon command: %s", err)
err = fmt.Errorf("failed to run sysmon schema command: %s", err)
return
}

Expand Down Expand Up @@ -77,12 +80,12 @@ func InstallOrUpdate(image string) (err error) {

// we first uninstall existing installation
// if Sysmon is not installed yet we should not get any error
if err = Uninstall(); err != nil {
if err = Uninstall(); err != nil && !errors.Is(err, ErrSysmonNotInstalled) {
return
}

// we run install
c := command.CommandTimeout(30*time.Second, image, "-accepteula", "-i")
c := command.CommandTimeout(defaultTimeout, image, "-accepteula", "-i")
defer c.Terminate()
if err = c.Run(); err != nil {
return fmt.Errorf("failed to install sysmon: %w", err)
Expand All @@ -93,11 +96,19 @@ func InstallOrUpdate(image string) (err error) {

func Configure(r io.Reader) (err error) {
var tmp, config string
var i *Info

i := NewSysmonInfo()
// retrieve sysmon information
if i, err = NewSysmonInfo(); err != nil {
return
}

image := i.Service.Image

if !fsutil.IsFile(image) {
return ErrSysmonNotInstalled
}

if tmp, err = utils.HidsMkTmpDir(); err != nil {
return fmt.Errorf("failed to create tmp dir: %w", err)
}
Expand All @@ -109,25 +120,29 @@ func Configure(r io.Reader) (err error) {
return fmt.Errorf("failed to create config file: %w", err)
}

if !fsutil.IsFile(image) {
return ErrSysmonNotInstalled
}

c := command.CommandTimeout(5*time.Second, image, "-c", config)
c := command.CommandTimeout(defaultTimeout, image, "-c", config)
defer c.Terminate()
return c.Run()
if err = c.Run(); err != nil {
return fmt.Errorf("command to configure sysmon failed: %w", err)
}

return
}

func Uninstall() (err error) {
i := NewSysmonInfo()
var i *Info

// retrieve sysmon information
if i, err = NewSysmonInfo(); err != nil {
return
}

image := i.Service.Image

//means sysmon is already installed
if fsutil.IsFile(image) {
// we uninstall it
c := command.CommandTimeout(60*time.Second, image, "-u")
c := command.CommandTimeout(defaultTimeout, image, "-u")
defer c.Terminate()

if err = c.Run(); err != nil {
Expand Down Expand Up @@ -162,19 +177,28 @@ func findMatchingSysmonServiceKeys() (keys []string) {
return
}

func NewSysmonInfo() (i *Info) {
func NewSysmonInfo() (i *Info, err error) {
var sysmonRegPath string

i = &Info{}

// validate that we find only one entry in registry
if keys := findMatchingSysmonServiceKeys(); len(keys) != 1 {
i.Err = fmt.Errorf("more than one key looking like Sysmon: %v", keys)
keys := findMatchingSysmonServiceKeys()

// sysmon is not installed
if len(keys) == 0 {
err = ErrSysmonNotInstalled
return
} else {
i.Service.Name = keys[0]
}

// more than one key found -> not normal
if len(keys) > 1 {
err = fmt.Errorf("more than one key looking like Sysmon: %v", keys)
return
}

i.Service.Name = keys[0]

sysmonRegPath = utils.RegJoin(servicesPath, i.Service.Name)
i.Service.Image = utils.RegValueToString(sysmonRegPath, "ImagePath")
i.Service.Sha256, _ = file.Sha256(i.Service.Image)
Expand All @@ -186,7 +210,7 @@ func NewSysmonInfo() (i *Info) {
i.Config.Hash = i.ConfigHash()

// parse schema and populate version information
i.parseSchema()
err = i.parseSchema()

return
}
Expand Down Expand Up @@ -225,10 +249,13 @@ func (i *Info) ConfigHash() string {
return hash
}

func (i *Info) parseSchema() {
version, schema, binary, err := Versions(i.Service.Image)
func (i *Info) parseSchema() (err error) {
var version, schema, binary string

version, schema, binary, err = Versions(i.Service.Image)
i.Version = version
i.Config.Version.Schema = schema
i.Config.Version.Binary = binary
i.Err = err

return
}
59 changes: 59 additions & 0 deletions sysmon/sysmon_windows_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//go:build windows
// +build windows

package sysmon

import (
"bytes"
"encoding/xml"
"testing"
"time"

"github.com/0xrawsec/toast"
"github.com/0xrawsec/whids/utils"
)

func init() {
// testing on WSL makes Windows command
// very long to respond
defaultTimeout = 60 * time.Second
}

func TestSysmonInfo(t *testing.T) {
var c *Config

tt := toast.FromT(t)

// we run uninstall first
Uninstall()
defer Uninstall()

_, err := NewSysmonInfo()

tt.ExpectErr(err, ErrSysmonNotInstalled)

tt.CheckErr(InstallOrUpdate("./data/Sysmon64.exe"))

i, err := NewSysmonInfo()
t.Log(utils.PrettyJson(i))
tt.CheckErr(err)

// we deserialize config
tt.CheckErr(xml.Unmarshal([]byte(config), &c))
// we force the good schema version to be the one of sysmon installed
c.SchemaVersion = i.Config.Version.Schema
xmlConfig, err := c.XML()
tt.CheckErr(err)

// configuring Sysmon
tt.CheckErr(Configure(bytes.NewBuffer(xmlConfig)))

i, err = NewSysmonInfo()
tt.CheckErr(err)

sha256, err := c.Sha256()
tt.CheckErr(err)
tt.Assert(i.Config.Hash == sha256)

tt.CheckErr(Uninstall())
}
27 changes: 0 additions & 27 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package utils
import (
"bytes"
"crypto/sha256"
"encoding/csv"
"encoding/hex"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -170,32 +169,6 @@ func Round(f float64, precision int) float64 {
return float64(int64(f*pow)) / pow
}

// SvcFromPid returns the list of services hosted by a given PID
// interesting to know what service is hosted by svchost
func SvcFromPid(pid int32) string {
c := exec.Command("tasklist", "/SVC", "/FO", "CSV", "/NH", "/FI", fmt.Sprintf("PID eq %d", pid))

out, err := c.Output()
if err != nil {
log.Errorf("Failed to run tasklist: %s", err)
return "ERROR"
}

r := csv.NewReader(bytes.NewBuffer(out))
rec, err := r.Read()
if err != nil {
log.Errorf("Failed to read tasklist output: %s", err)
return "ERROR"
}

// Expect three fields
if len(rec) == 3 {
return rec[2]
}
log.Errorf("Unexpected tasklist output: %s", out)
return "ERROR"
}

// RegQuery issues a reg query command to dump registry
func RegQuery(key, value string) (string, error) {
c := exec.Command("reg", "query", key, "/v", value)
Expand Down
10 changes: 3 additions & 7 deletions utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,26 @@ var (
pid = 604
)

func TestSvcFromPid(t *testing.T) {
t.Logf("SVC from PID=%d: %s", pid, SvcFromPid(int32(pid)))
}

func TestRegQuery(t *testing.T) {
/*path := `HKLM\System\CurrentControlSet\Services\SysmonDrv\Parameters\HashingAlgorithm`
key, value := filepath.Split(path)
t.Logf("Sysmon hashing algorithm: %s", RegQuery(key, value))*/
}

var (
aclDirectories = []string{"C:\\"}
aclDirectories = []string{"C:\\Windows\\System32"}
)

func TestSetAuditACL(t *testing.T) {
if err := SetEDRAuditACL(aclDirectories...); err != nil {
t.Logf("Failed at setting Audit ACL: %s", err)
t.Logf("Failed at setting Audit ACL: %s", err)
t.FailNow()
}
t.Logf("Successfully set audit ACL")
}
func TestRemoveAuditACL(t *testing.T) {
if err := RemoveEDRAuditACL(aclDirectories...); err != nil {
t.Logf("Failed at setting Audit ACL: %s", err)
t.Logf("Failed at setting Audit ACL: %s", err)
t.FailNow()
}
t.Logf("Successfully set audit ACL")
Expand Down

0 comments on commit 74169e8

Please sign in to comment.