mirror of
https://github.com/FourCoreLabs/EDRHunt
synced 2026-08-09 12:06:42 +00:00
Added WMI AV Support
This commit is contained in:
+3
-2
@@ -1,2 +1,3 @@
|
||||
dist
|
||||
EDRHunt.exe
|
||||
dist
|
||||
EDRHunt.exe
|
||||
.vscode
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
all: build
|
||||
|
||||
build:
|
||||
go build -ldflags="-w -s" -o EDRHunt.exe github.com/FourCoreLabs/EDRHunt/cmd/EDRHunt
|
||||
garble-build:
|
||||
garble -literals build -ldflags="-w -s" -o EDRHunt.exe github.com/FourCoreLabs/EDRHunt/cmd/EDRHunt
|
||||
local:
|
||||
go build -ldflags="-w -s" -o EDRHunt.exe github.com/FourCoreLabs/EDRHunt/cmd/EDRHunt
|
||||
run:
|
||||
go run -ldflags="-w -s" github.com/FourCoreLabs/EDRHunt/cmd/EDRHunt all
|
||||
drivers:
|
||||
go run -ldflags="-w -s" github.com/FourCoreLabs/EDRHunt/cmd/EDRHunt -d
|
||||
all: build
|
||||
|
||||
build:
|
||||
go build -ldflags="-w -s" -o EDRHunt.exe github.com/FourCoreLabs/EDRHunt/cmd/EDRHunt
|
||||
garble-build:
|
||||
garble -literals build -ldflags="-w -s" -o EDRHunt.exe github.com/FourCoreLabs/EDRHunt/cmd/EDRHunt
|
||||
local:
|
||||
go build -ldflags="-w -s" -o EDRHunt.exe github.com/FourCoreLabs/EDRHunt/cmd/EDRHunt
|
||||
run:
|
||||
go run -ldflags="-w -s" github.com/FourCoreLabs/EDRHunt/cmd/EDRHunt all
|
||||
drivers:
|
||||
go run -ldflags="-w -s" github.com/FourCoreLabs/EDRHunt/cmd/EDRHunt -d
|
||||
avwmi:
|
||||
go run -ldflags="-w -s" github.com/FourCoreLabs/EDRHunt/cmd/EDRHunt -w
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://github.com/FourCoreLabs/EDRHunt/actions/workflows/goreleaser.yml)
|
||||
|
||||
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-windows-endpoints-edr-edrhunt).
|
||||
EDRHunt scans Windows services, drivers, processes, registry, wmi for installed EDRs (Endpoint Detection And Response). Read more about EDRHunt [here](https://www.fourcore.vision/blogs/red-team-adventure-windows-endpoints-edr-edrhunt).
|
||||
|
||||
[](https://asciinema.org/a/P8i99w9mI497qUPTNbdwYWcwQ)
|
||||
|
||||
@@ -30,7 +30,7 @@ Detected EDR: Kaspersky Security
|
||||
```
|
||||
$ .\EDRHunt.exe all
|
||||
Running in user mode, escalate to admin for more details.
|
||||
Scanning processes, services, drivers, and registry...
|
||||
Scanning processes, services, drivers, wmi, and registry...
|
||||
[PROCESSES]
|
||||
|
||||
Suspicious Process Name: MsMpEng.exe
|
||||
@@ -131,6 +131,13 @@ $ .\EDRHunt.exe -d
|
||||
$ .\EDRHunt.exe -r
|
||||
```
|
||||
|
||||
|
||||
- Find WMI Repository keys matching EDR keywords
|
||||
|
||||
```
|
||||
$ .\EDRHunt.exe -w
|
||||
```
|
||||
|
||||
## Detections
|
||||
|
||||
EDR Detections Currently Available
|
||||
@@ -145,6 +152,9 @@ EDR Detections Currently Available
|
||||
- SentinelOne
|
||||
- FireEye
|
||||
- Elastic EDR
|
||||
- Qualys EDR
|
||||
- Trend Micro EDR
|
||||
- ESET EDR
|
||||
|
||||
More to be added soon.
|
||||
|
||||
|
||||
+26
-3
@@ -16,8 +16,9 @@ var (
|
||||
processes bool
|
||||
services bool
|
||||
registry bool
|
||||
avwmi bool
|
||||
all bool
|
||||
versionStr string = "1.3.1"
|
||||
versionStr string = "1.4.0"
|
||||
versionCheck bool
|
||||
)
|
||||
|
||||
@@ -46,7 +47,8 @@ func edrCommand(cmd *cobra.Command, args []string) {
|
||||
drivers = true
|
||||
services = true
|
||||
registry = true
|
||||
fmt.Println("Scanning processes, services, drivers, and registry...")
|
||||
avwmi = true
|
||||
fmt.Println("Scanning processes, services, drivers, wmi, and registry...")
|
||||
}
|
||||
|
||||
if processes {
|
||||
@@ -67,6 +69,12 @@ func edrCommand(cmd *cobra.Command, args []string) {
|
||||
printServices(summary)
|
||||
fmt.Println()
|
||||
}
|
||||
if avwmi {
|
||||
fmt.Println("[WMI-REPO]")
|
||||
summary, _ := edrRecon.CheckAVWmiRepo()
|
||||
printAVWmi(summary)
|
||||
fmt.Println()
|
||||
}
|
||||
if registry {
|
||||
fmt.Println("[REGISTRY]")
|
||||
summary, _ := edrRecon.CheckRegistry(context.Background())
|
||||
@@ -100,7 +108,7 @@ func allCommand(cmd *cobra.Command, args []string) {
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "EDRHunt",
|
||||
Short: "scans EDR/AV",
|
||||
Long: `EDRHunt scans and finds the installed EDR/AV by scanning services, processes, registry, and drivers.`,
|
||||
Long: `EDRHunt scans and finds the installed EDR/AV by scanning services, processes, registry, wmi, and drivers.`,
|
||||
Run: edrCommand,
|
||||
}
|
||||
|
||||
@@ -125,6 +133,20 @@ var allCmd = &cobra.Command{
|
||||
Run: allCommand,
|
||||
}
|
||||
|
||||
func printAVWmi(summary []resources.AVWmiMetaData) {
|
||||
for _, antivirus := range summary {
|
||||
fmt.Printf("Suspicious Product Name: %s\n", antivirus.ProductName)
|
||||
fmt.Printf("Suspicious Product GUID: %s\n", antivirus.ProductGUID)
|
||||
fmt.Printf("Path to Suspicious Product Exe: %s\n", antivirus.PathToProductExe)
|
||||
fmt.Printf("Suspicious Product Exe Metadata: \t%s\n", edrRecon.FileMetaDataParser(antivirus.ProductExeMetaData))
|
||||
fmt.Printf("Path to Suspicious Reporting Exe: %s\n", antivirus.PathToReportingExe)
|
||||
fmt.Printf("Suspicious Reporting Exe Metadata: \t%s\n", edrRecon.FileMetaDataParser(antivirus.ReportingExeMetaData))
|
||||
fmt.Printf("Suspicious Product State: %d\n", antivirus.ProductState)
|
||||
fmt.Printf("Matched Keyword: %s\n", antivirus.ScanMatch)
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func printProcess(summary []resources.ProcessMetaData) {
|
||||
for _, process := range summary {
|
||||
fmt.Printf("Suspicious Process Name: %s\n", process.ProcessName)
|
||||
@@ -184,6 +206,7 @@ func init() {
|
||||
rootCmd.PersistentFlags().BoolVarP(&processes, "processes", "p", processes, "Scan installed processes")
|
||||
rootCmd.PersistentFlags().BoolVarP(&services, "services", "s", services, "Scan installed services")
|
||||
rootCmd.PersistentFlags().BoolVarP(®istry, "registry", "r", registry, "Scan installed registry")
|
||||
rootCmd.PersistentFlags().BoolVarP(&avwmi, "avwmi", "w", avwmi, "Scan installed AntiVirus Providers")
|
||||
rootCmd.PersistentFlags().BoolVarP(&versionCheck, "version", "v", versionCheck, "Output version information and exit")
|
||||
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/FourCoreLabs/EDRHunt/pkg/resources"
|
||||
)
|
||||
|
||||
// GetSystemData collects the parsed list of processes, services, drivers and registry keys to be used for EDR heuristics.
|
||||
// GetSystemData collects the parsed list of processes, services, drivers, wmi and registry keys to be used for EDR heuristics.
|
||||
func GetSystemData(ctx context.Context) (resources.SystemData, error) {
|
||||
var err error
|
||||
var systemData resources.SystemData
|
||||
@@ -32,5 +32,9 @@ func GetSystemData(ctx context.Context) (resources.SystemData, error) {
|
||||
return systemData, fmt.Errorf("failed to check drivers: %w", err)
|
||||
}
|
||||
|
||||
systemData.AVProviders, err = CheckAVWmiRepo()
|
||||
if err != nil {
|
||||
return systemData, fmt.Errorf("failed to check wmi repo: %w", err)
|
||||
}
|
||||
return systemData, nil
|
||||
}
|
||||
|
||||
@@ -55,6 +55,16 @@ func TestCheckProcesses(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckAVWMI(t *testing.T) {
|
||||
summary, _ := CheckAVWmiRepo()
|
||||
for _, av := range summary {
|
||||
fmt.Println(av)
|
||||
}
|
||||
// if err.Error() != "" {
|
||||
// fmt.Println("error", err)
|
||||
// }
|
||||
}
|
||||
|
||||
func TestGetFileMetaData(t *testing.T) {
|
||||
fileMetaData, err := GetFileMetaData(`C:\Users\hardi\AnyDesk.exe`)
|
||||
if err != nil {
|
||||
|
||||
@@ -289,6 +289,15 @@ var EdrList = []string{
|
||||
"elastic-endpoint.exe",
|
||||
"elastic-endpoint-driver",
|
||||
"ElasticEndpoint",
|
||||
"ecmd.exe",
|
||||
"ekrn.exe",
|
||||
"ESET",
|
||||
"mcupdate.exe",
|
||||
"ProtectedModuleHost.exe",
|
||||
"dsa.exe",
|
||||
"Notifier.exe",
|
||||
"qualys",
|
||||
"qualysagent.exe",
|
||||
}
|
||||
|
||||
var ReconList = []string{
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package edrRecon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/FourCoreLabs/EDRHunt/pkg/resources"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
)
|
||||
|
||||
type AntiVirusProduct struct {
|
||||
DisplayName string
|
||||
InstanceGuid string
|
||||
PathToSignedProductExe string
|
||||
PathToSignedReportingExe string
|
||||
ProductState uint32
|
||||
}
|
||||
|
||||
type AvResult struct {
|
||||
AvProduct []AntiVirusProduct
|
||||
Err error
|
||||
}
|
||||
|
||||
const (
|
||||
namespace = "root\\SecurityCenter2"
|
||||
class = "AntiVirusProduct"
|
||||
wmiErr = "wmi query timed out"
|
||||
)
|
||||
|
||||
func CheckAVWmiRepo() ([]resources.AVWmiMetaData, error) {
|
||||
var (
|
||||
avList []AntiVirusProduct
|
||||
multiErr error
|
||||
summary []resources.AVWmiMetaData = make([]resources.AVWmiMetaData, 0)
|
||||
err error
|
||||
)
|
||||
|
||||
avList, err = GetAVwithWMI()
|
||||
if err != nil {
|
||||
return summary, err
|
||||
}
|
||||
for _, av := range avList {
|
||||
if av.DisplayName == "" {
|
||||
continue
|
||||
}
|
||||
output, err := AnalyzeAVProduct(av)
|
||||
if err != nil {
|
||||
multiErr = multierror.Append(multiErr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(output.ScanMatch) > 0 {
|
||||
summary = append(summary, output)
|
||||
}
|
||||
|
||||
}
|
||||
return summary, multiErr
|
||||
}
|
||||
|
||||
func AnalyzeAVProduct(av AntiVirusProduct) (resources.AVWmiMetaData, error) {
|
||||
analysis := resources.AVWmiMetaData{
|
||||
ProductName: av.DisplayName,
|
||||
ProductGUID: av.InstanceGuid,
|
||||
PathToProductExe: av.PathToSignedProductExe,
|
||||
PathToReportingExe: av.PathToSignedReportingExe,
|
||||
ProductState: av.ProductState,
|
||||
}
|
||||
|
||||
if analysis.PathToProductExe != "" {
|
||||
analysis.ProductExeMetaData, _ = GetFileMetaData(analysis.PathToProductExe)
|
||||
}
|
||||
|
||||
if analysis.PathToReportingExe != "" {
|
||||
analysis.ReportingExeMetaData, _ = GetFileMetaData(analysis.PathToReportingExe)
|
||||
}
|
||||
|
||||
for _, edr := range EdrList {
|
||||
if strings.Contains(
|
||||
strings.ToLower(fmt.Sprint(analysis)),
|
||||
strings.ToLower(edr)) {
|
||||
analysis.ScanMatch = append(analysis.ScanMatch, edr)
|
||||
}
|
||||
}
|
||||
|
||||
return analysis, nil
|
||||
}
|
||||
|
||||
func GetAVwithWMI() ([]AntiVirusProduct, error) {
|
||||
result := make(chan AvResult, 1)
|
||||
go func() {
|
||||
result <- WMIQuery()
|
||||
}()
|
||||
select {
|
||||
case <-time.After(6 * time.Second):
|
||||
return nil, errors.New(wmiErr)
|
||||
case result := <-result:
|
||||
return result.AvProduct, result.Err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func WMIQuery() AvResult {
|
||||
var avResults []AntiVirusProduct
|
||||
query := wmi.CreateQuery(&avResults, "", class)
|
||||
err := wmi.QueryNamespace(query, &avResults, namespace)
|
||||
return AvResult{
|
||||
AvProduct: avResults,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ type Recon interface {
|
||||
CheckServices() ([]ServiceMetaData, error)
|
||||
CheckDrivers() ([]DriverMetaData, error)
|
||||
CheckRegistry() (RegistryMetaData, error)
|
||||
CheckAVWmiRepo() ([]AVWmiMetaData, error)
|
||||
CheckDirectory() (string, error)
|
||||
}
|
||||
|
||||
@@ -32,6 +33,17 @@ type ServiceMetaData struct {
|
||||
ScanMatch []string
|
||||
}
|
||||
|
||||
type AVWmiMetaData struct {
|
||||
ProductName string
|
||||
ProductGUID string
|
||||
PathToProductExe string
|
||||
ProductExeMetaData FileMetaData
|
||||
PathToReportingExe string
|
||||
ReportingExeMetaData FileMetaData
|
||||
ProductState uint32
|
||||
ScanMatch []string
|
||||
}
|
||||
|
||||
type ProcessMetaData struct {
|
||||
ProcessName string
|
||||
ProcessPath string
|
||||
|
||||
@@ -19,4 +19,7 @@ var (
|
||||
SentinelOneEDR EDRType = "sentinel_one"
|
||||
FireEyeEDR EDRType = "fireeye"
|
||||
ElasticAgentEDR EDRType = "elastic_agent"
|
||||
QualysEDR EDRType = "qualys"
|
||||
TrendMicroEDR EDRType = "trend_micro"
|
||||
ESETEDR EDRType = "eset"
|
||||
)
|
||||
|
||||
@@ -5,10 +5,11 @@ import (
|
||||
)
|
||||
|
||||
type SystemData struct {
|
||||
Processes []ProcessMetaData
|
||||
Registry RegistryMetaData
|
||||
Services []ServiceMetaData
|
||||
Drivers []DriverMetaData
|
||||
Processes []ProcessMetaData
|
||||
Registry RegistryMetaData
|
||||
Services []ServiceMetaData
|
||||
Drivers []DriverMetaData
|
||||
AVProviders []AVWmiMetaData
|
||||
}
|
||||
|
||||
// CountMatchesAll collects all the scanned matches of suspicious names and checks for passed keywords in the matches.
|
||||
@@ -30,6 +31,10 @@ func (s *SystemData) CountMatchesAll(keywords ...[]string) (int, bool) {
|
||||
scanMatchList = append(scanMatchList, v.ScanMatch...)
|
||||
}
|
||||
|
||||
for _, v := range s.AVProviders {
|
||||
scanMatchList = append(scanMatchList, v.ScanMatch...)
|
||||
}
|
||||
|
||||
scanMatchList = append(scanMatchList, s.Registry.ScanMatch...)
|
||||
|
||||
var totalLen int
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package scanners
|
||||
|
||||
import "github.com/FourCoreLabs/EDRHunt/pkg/resources"
|
||||
|
||||
type ESETEDRDetection struct{}
|
||||
|
||||
func (w *ESETEDRDetection) Name() string {
|
||||
return "ESET Endpoint Security"
|
||||
}
|
||||
|
||||
func (w *ESETEDRDetection) Type() resources.EDRType {
|
||||
return resources.ESETEDR
|
||||
}
|
||||
|
||||
var ESETHeuristic = []string{
|
||||
"ESET",
|
||||
"ESET Endpoint Security",
|
||||
"ecmd",
|
||||
"ekrn",
|
||||
}
|
||||
|
||||
func (w *ESETEDRDetection) Detect(data resources.SystemData) (resources.EDRType, bool) {
|
||||
_, ok := data.CountMatchesAll(ESETHeuristic)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return resources.QualysEDR, true
|
||||
}
|
||||
@@ -14,6 +14,8 @@ func (w *McafeeDetection) Type() resources.EDRType {
|
||||
|
||||
var McafeeHeuristic = []string{
|
||||
"Mcafee\\",
|
||||
"mcupdate.exe",
|
||||
"ProtectedModuleHost.exe",
|
||||
"McAfeeAgent\\",
|
||||
"APPolicyName",
|
||||
"EPPolicyName",
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package scanners
|
||||
|
||||
import "github.com/FourCoreLabs/EDRHunt/pkg/resources"
|
||||
|
||||
type QualysDetection struct{}
|
||||
|
||||
func (w *QualysDetection) Name() string {
|
||||
return "Qualys Cloud Agent EDR"
|
||||
}
|
||||
|
||||
func (w *QualysDetection) Type() resources.EDRType {
|
||||
return resources.QualysEDR
|
||||
}
|
||||
|
||||
var QualysHeuristic = []string{
|
||||
"Qualys",
|
||||
"QualysAgent.exe",
|
||||
"qualysagent.exe",
|
||||
"qualys",
|
||||
}
|
||||
|
||||
func (w *QualysDetection) Detect(data resources.SystemData) (resources.EDRType, bool) {
|
||||
_, ok := data.CountMatchesAll(QualysHeuristic)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return resources.QualysEDR, true
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package scanners
|
||||
|
||||
import "github.com/FourCoreLabs/EDRHunt/pkg/resources"
|
||||
|
||||
type TrendMicroDetection struct{}
|
||||
|
||||
func (w *TrendMicroDetection) Name() string {
|
||||
return "Trend Micro Deep Security"
|
||||
}
|
||||
|
||||
func (w *TrendMicroDetection) Type() resources.EDRType {
|
||||
return resources.TrendMicroEDR
|
||||
}
|
||||
|
||||
var TrendMicroHeuristic = []string{
|
||||
"Deep Security Agent\\",
|
||||
"dsa",
|
||||
"Notifier",
|
||||
"Trend Micro",
|
||||
}
|
||||
|
||||
func (w *TrendMicroDetection) Detect(data resources.SystemData) (resources.EDRType, bool) {
|
||||
_, ok := data.CountMatchesAll(TrendMicroHeuristic)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return resources.QualysEDR, true
|
||||
}
|
||||
@@ -14,5 +14,8 @@ var (
|
||||
&SentinelOneDetection{},
|
||||
&WinDefenderDetection{},
|
||||
&ElasticAgentDetection{},
|
||||
&ESETEDRDetection{},
|
||||
&QualysDetection{},
|
||||
&TrendMicroDetection{},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
)
|
||||
|
||||
type AntiVirusProduct struct {
|
||||
DisplayName string
|
||||
InstanceGuid string
|
||||
PathToSignedProductExe string
|
||||
PathToSignedReportingExe string
|
||||
ProductState uint32
|
||||
}
|
||||
|
||||
type AvResult struct {
|
||||
AvProduct []AntiVirusProduct
|
||||
Err error
|
||||
}
|
||||
|
||||
func WMIQuery() AvResult {
|
||||
var avResults []AntiVirusProduct
|
||||
query := wmi.CreateQuery(&avResults, "", "AntiVirusProduct")
|
||||
err := wmi.QueryNamespace(query, &avResults, "root\\SecurityCenter2")
|
||||
return AvResult{
|
||||
AvProduct: avResults,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func GetAVwithWMI() ([]AntiVirusProduct, error) {
|
||||
result := make(chan AvResult, 1)
|
||||
go func() {
|
||||
result <- WMIQuery()
|
||||
}()
|
||||
select {
|
||||
case <-time.After(10 * time.Second):
|
||||
return nil, errors.New("wmi query timed out")
|
||||
case result := <-result:
|
||||
return result.AvProduct, result.Err
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
query, queryErr := GetAVwithWMI()
|
||||
if queryErr != nil {
|
||||
panic(queryErr)
|
||||
}
|
||||
for i, v := range query {
|
||||
println(i+1, v.DisplayName, v.InstanceGuid, v.PathToSignedProductExe, v.PathToSignedReportingExe, v.ProductState)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user