Merge pull request #5 from FourCoreLabs/refactor-check-errors

refactor: pass errors via multierror, pass context in CheckRegistry
This commit is contained in:
achilles4828
2021-10-13 18:27:46 +05:30
committed by GitHub
10 changed files with 111 additions and 88 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
[![goreleaser](https://github.com/FourCoreLabs/EDRHunt/actions/workflows/goreleaser.yml/badge.svg)](https://github.com/FourCoreLabs/EDRHunt/actions/workflows/goreleaser.yml)
EDRHunt scans Windows services, drivers, processes, registry for installed EDRs. Read more about EDRHunt [here](https://www.fourcore.vision/blogs/Red-Team-Adventure:-Digging-into-Windows-Endpoints-for-EDRs-and-profit-cUf).
EDRHunt scans Windows services, drivers, processes, registry for installed EDRs (Endpoint Detection And Response). Read more about EDRHunt [here](https://www.fourcore.vision/blogs/Red-Team-Adventure:-Digging-into-Windows-Endpoints-for-EDRs-and-profit-cUf).
[![asciicast](https://asciinema.org/a/P8i99w9mI497qUPTNbdwYWcwQ.svg)](https://asciinema.org/a/P8i99w9mI497qUPTNbdwYWcwQ)
+3 -2
View File
@@ -1,6 +1,7 @@
package main
import (
"context"
"fmt"
"os"
@@ -66,7 +67,7 @@ func edrCommand(cmd *cobra.Command, args []string) {
}
if registry {
fmt.Println("[REGISTRY]")
summary, _ := edrRecon.CheckRegistry()
summary, _ := edrRecon.CheckRegistry(context.Background())
printRegistry(summary)
fmt.Println()
}
@@ -78,7 +79,7 @@ func versionCommand(cmd *cobra.Command, args []string) {
func scanEDRCommand(cmd *cobra.Command, args []string) {
fmt.Println("[EDR]")
systemData, _ := edrRecon.GetSystemData()
systemData, _ := edrRecon.GetSystemData(context.Background())
for _, scanner := range edrRecon.Scanners {
_, ok := scanner.Detect(systemData)
+2
View File
@@ -10,6 +10,8 @@ require (
require (
github.com/go-ole/go-ole v1.2.5 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
+3
View File
@@ -144,11 +144,14 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+39 -37
View File
@@ -7,6 +7,8 @@ import (
"syscall"
"unicode/utf16"
"unsafe"
"github.com/hashicorp/go-multierror"
)
var (
@@ -163,43 +165,43 @@ func GetDriverBaseName(driverAddrs uintptr) (string, error) {
return syscall.UTF16ToString(data), nil
}
func IterateOverDrivers(numberOfDrivers uint, driverAddrs []uintptr) ([]DriverMetaData, []string) {
func IterateOverDrivers(numberOfDrivers uint, driverAddrs []uintptr) ([]DriverMetaData, error) {
var (
// counter uint
errArray []string
multiErr error
summary []DriverMetaData = make([]DriverMetaData, 0)
)
for _, addr := range driverAddrs {
driverFileName, err := GetDriverFileName(addr)
if err != nil {
errArray = append(errArray, fmt.Sprintf("%v", err))
multiErr = multierror.Append(multiErr, err)
continue
}
driverBaseName, err := GetDriverBaseName(addr)
if err != nil {
errArray = append(errArray, fmt.Sprintf("%v", err))
continue
}
output, err := AnalyzeDriver(driverFileName, driverBaseName)
if err != nil {
errArray = append(errArray, fmt.Sprintf("%v", err))
}
if output.DriverBaseName == "" {
continue
}
summary = append(summary, output)
}
// for counter = 0; counter < numberOfDrivers; counter++ {
// }
return summary, errArray
driverBaseName, err := GetDriverBaseName(addr)
if err != nil {
multiErr = multierror.Append(multiErr, err)
continue
}
if driverBaseName == "" {
continue
}
output, err := AnalyzeDriver(driverFileName, driverBaseName)
if err != nil {
multiErr = multierror.Append(multiErr, err)
}
if len(output.ScanMatch) > 0 {
summary = append(summary, output)
}
}
return summary, multiErr
}
func AnalyzeDriver(driverFileName string, driverBaseName string) (DriverMetaData, error) {
var err error
fixedDriverPath := strings.ToLower(driverFileName)
fixedDriverPath = strings.Replace(fixedDriverPath, `\systemroot\`, `c:\windows\`, -1)
if strings.HasPrefix(fixedDriverPath, `\windows\`) {
@@ -207,13 +209,14 @@ func AnalyzeDriver(driverFileName string, driverBaseName string) (DriverMetaData
} else if strings.HasPrefix(fixedDriverPath, `\??\`) {
fixedDriverPath = strings.Replace(fixedDriverPath, `\??\`, ``, -1)
}
analysis := DriverMetaData{
DriverBaseName: driverBaseName,
DriverFilePath: fixedDriverPath,
ScanMatch: make([]string, 0),
}
analysis.DriverSysMetaData, err = GetFileMetaData(fixedDriverPath)
analysis.DriverSysMetaData, _ = GetFileMetaData(fixedDriverPath)
for _, edr := range EdrList {
// regexp as alternate but saving another import. No bully.
@@ -224,33 +227,32 @@ func AnalyzeDriver(driverFileName string, driverBaseName string) (DriverMetaData
}
}
if len(analysis.ScanMatch) > 0 {
return analysis, err
}
return DriverMetaData{ScanMatch: make([]string, 0)}, err
return analysis, nil
}
// CheckDrivers return a list of drivers matching any suspicious driver names present in edrdata.go.
func CheckDrivers() ([]DriverMetaData, error) {
var drivers []DriverMetaData = make([]DriverMetaData, 0)
sizeOfDriverArrayInBytes, err := GetSizeOfDriversArray()
if err != nil {
return []DriverMetaData{}, err
return drivers, err
}
sizeOfOneDriverAddress := uint(unsafe.Sizeof(uintptr(0)))
numberOfDrivers = uint(sizeOfDriverArrayInBytes) / sizeOfOneDriverAddress
driverAddrs = make([]uintptr, numberOfDrivers)
success := EnumDeviceDrivers(driverAddrs, DWORD(sizeOfDriverArrayInBytes), &sizeOfDriverArrayInBytes)
if !success {
return []DriverMetaData{}, fmt.Errorf("failed to enumerate device drivers, error code: %d", syscall.GetLastError())
return drivers, fmt.Errorf("failed to enumerate device drivers, error code: %w", syscall.GetLastError())
}
// filterErrArray optional || Don't bully.
summary, filterErrArray := IterateOverDrivers(numberOfDrivers, driverAddrs)
if strings.TrimSpace(strings.Join(filterErrArray, "")) != "" {
return summary, fmt.Errorf("%s", filterErrArray)
drivers, err = IterateOverDrivers(numberOfDrivers, driverAddrs)
if err != nil {
return drivers, err
}
return summary, nil
return drivers, nil
}
+2 -1
View File
@@ -1,6 +1,7 @@
package edrRecon
import (
"context"
"encoding/json"
"fmt"
"testing"
@@ -19,7 +20,7 @@ func TestCheckDrivers(t *testing.T) {
}
func TestCheckRegistry(t *testing.T) {
summary, err := CheckRegistry()
summary, err := CheckRegistry(context.TODO())
fmt.Println("Scanning registry: ")
for _, match := range summary.ScanMatch {
fmt.Printf("\t%s\n", match)
+26 -22
View File
@@ -5,6 +5,7 @@ import (
"strings"
"github.com/StackExchange/wmi"
"github.com/hashicorp/go-multierror"
)
type Win32_Process struct {
@@ -24,30 +25,36 @@ type Win32_Process struct {
func CheckProcesses() ([]ProcessMetaData, error) {
var (
processList []Win32_Process
errArray []string
summary []ProcessMetaData
multiErr error
summary []ProcessMetaData = make([]ProcessMetaData, 0)
)
query := wmi.CreateQuery(&processList, "")
err := wmi.Query(query, &processList)
if err != nil {
return []ProcessMetaData{}, err
}
for _, process := range processList {
output, err := AnalyzeProcess(process)
if err != nil {
errArray = append(errArray, fmt.Sprintf("%v", err))
}
if output.ProcessName == "" {
continue
}
summary = append(summary, output)
if err := wmi.Query(query, &processList); err != nil {
return summary, err
}
return summary, fmt.Errorf("%v", errArray)
for _, process := range processList {
if process.Name == "" {
continue
}
output, err := AnalyzeProcess(process)
if err != nil {
multiErr = multierror.Append(multiErr, err)
continue
}
if len(output.ScanMatch) > 0 {
summary = append(summary, output)
}
}
return summary, multiErr
}
func AnalyzeProcess(process Win32_Process) (ProcessMetaData, error) {
var err error
analysis := ProcessMetaData{
ProcessName: process.Name,
ProcessPath: process.ExecutablePath,
@@ -59,7 +66,7 @@ func AnalyzeProcess(process Win32_Process) (ProcessMetaData, error) {
}
if analysis.ProcessPath != "" {
analysis.ProcessExeMetaData, err = GetFileMetaData(analysis.ProcessPath)
analysis.ProcessExeMetaData, _ = GetFileMetaData(analysis.ProcessPath)
}
for _, edr := range EdrList {
@@ -70,8 +77,5 @@ func AnalyzeProcess(process Win32_Process) (ProcessMetaData, error) {
}
}
if len(analysis.ScanMatch) > 0 {
return analysis, err
}
return ProcessMetaData{}, err
return analysis, nil
}
+14 -9
View File
@@ -39,32 +39,35 @@ func makeCmd(ctx context.Context, param ...string) (*exec.Cmd, error) {
return cmd, nil
}
func EnumRegistry() []string {
ctx := context.Background()
func EnumRegistry(ctx context.Context) []string {
for _, x := range RegistrySearchList {
paramWg.Add(1)
go func(args string) {
defer paramWg.Done()
stdout, err := runCMDCommand(ctx, args)
if err != nil {
return
}
outputLock.Lock()
defer outputLock.Unlock()
if len(stdout) != 0 {
output = append(output, string(stdout))
}
}(x)
}
paramWg.Wait()
return output
}
func CheckRegistry() (RegistryMetaData, error) {
func CheckRegistry(ctx context.Context) (RegistryMetaData, error) {
var analysis RegistryMetaData = RegistryMetaData{ScanMatch: make([]string, 0)}
output := strings.Join(EnumRegistry(), " ")
var analysis RegistryMetaData
// matches = append(matches, "Scanning Registry:")
output := strings.Join(EnumRegistry(ctx), " ")
if output != "" {
processedOutput := strings.ToLower(output)
for _, match := range RegistryReconList {
@@ -75,8 +78,10 @@ func CheckRegistry() (RegistryMetaData, error) {
}
}
}
if len(analysis.ScanMatch) > 0 {
if len(analysis.ScanMatch) == 0 {
return analysis, fmt.Errorf("nothing found in registry")
}
return analysis, nil
}
return RegistryMetaData{}, fmt.Errorf("nothing found in registry")
}
+4 -2
View File
@@ -1,5 +1,7 @@
package edrRecon
import "context"
var (
Scanners = []EDRDetection{
&CarbonBlackDetection{},
@@ -64,7 +66,7 @@ func (s *SystemData) CountMatchesAll(keywords ...[]string) (int, bool) {
}
// GetSystemData collects the parsed list of processes, services, drivers and registry keys to be used for EDR heuristics.
func GetSystemData() (SystemData, error) {
func GetSystemData(ctx context.Context) (SystemData, error) {
var systemData SystemData
systemData.Processes, err = CheckProcesses()
@@ -77,7 +79,7 @@ func GetSystemData() (SystemData, error) {
return systemData, err
}
systemData.Registry, err = CheckRegistry()
systemData.Registry, err = CheckRegistry(ctx)
if err != nil {
return systemData, err
}
+24 -21
View File
@@ -5,6 +5,7 @@ import (
"strings"
"github.com/StackExchange/wmi"
"github.com/hashicorp/go-multierror"
)
type Win32_Service struct {
@@ -23,32 +24,35 @@ type Win32_Service struct {
func CheckServices() ([]ServiceMetaData, error) {
var (
serviceList []Win32_Service
errArray []string
multiErr error
summary []ServiceMetaData
)
query := wmi.CreateQuery(&serviceList, "")
err := wmi.Query(query, &serviceList)
if err != nil {
return []ServiceMetaData{}, err
}
for _, service := range serviceList {
output, err := AnalyzeService(service)
if err != nil {
errArray = append(errArray, fmt.Sprintf("%v", err))
}
if output.ServiceName == "" {
continue
}
summary = append(summary, output)
if err := wmi.Query(query, &serviceList); err != nil {
return summary, err
}
return summary, fmt.Errorf("%v", errArray)
for _, service := range serviceList {
if service.Name == "" {
continue
}
output, err := AnalyzeService(service)
if err != nil {
multiErr = multierror.Append(multiErr, err)
}
if len(output.ScanMatch) > 0 {
summary = append(summary, output)
}
}
return summary, multiErr
}
func AnalyzeService(service Win32_Service) (ServiceMetaData, error) {
var err error
analysis := ServiceMetaData{
ServiceName: service.Name,
ServiceDisplayName: service.DisplayName,
@@ -58,11 +62,12 @@ func AnalyzeService(service Win32_Service) (ServiceMetaData, error) {
ServiceState: service.State,
ServiceProcessId: fmt.Sprint(service.ProcessId),
}
if analysis.ServicePathName != "" {
trim := strings.Index(analysis.ServicePathName, ".exe")
if trim > 0 {
servicePath := analysis.ServicePathName[:trim] + ".exe"
analysis.ServiceExeMetaData, err = GetFileMetaData(servicePath)
analysis.ServiceExeMetaData, _ = GetFileMetaData(servicePath)
}
}
@@ -73,8 +78,6 @@ func AnalyzeService(service Win32_Service) (ServiceMetaData, error) {
analysis.ScanMatch = append(analysis.ScanMatch, edr)
}
}
if len(analysis.ScanMatch) > 0 {
return analysis, err
}
return ServiceMetaData{}, err
}