3 Commits

Author SHA1 Message Date
Evan Hosinski 7cdee4b62c Enhance directory detection to handle prefix matching and eliminate duplicates
Helps to discover directories with a prefix instead of just by exact match
2025-10-10 16:57:12 -04:00
Evan Hosinski 9512022a73 Standardize and fix README code block formatting for improved readability 2025-10-10 16:48:43 -04:00
Evan Hosinski 967b0c1de1 Fix README formatting for consistent code block styling and improve clarity in usage examples 2025-10-10 16:47:27 -04:00
2 changed files with 76 additions and 16 deletions
+12 -13
View File
@@ -63,14 +63,12 @@ The HTML report includes:
### Binary Download
Download the latest compiled binary from the releases page:
```
powershell
```powershell
Download rmm-hunter.exe
Run with administrator privileges```
Run with administrator privileges
```
### Building from Source
```
The Scurvy Library is not publicly accessible making building this tool from source impossible at the moment.
@@ -79,20 +77,22 @@ The Scurvy Library is not publicly accessible making building this tool from sou
### Hunt Mode
Execute a comprehensive system scan:
```
powershell .\rmm-hunter.exe hunt```
```powershell
powershell .\rmm-hunter.exe hunt
```
With custom output file:
```
powershell .\rmm-hunter.exe hunt --output custom-report.json```
```powershell
powershell .\rmm-hunter.exe hunt --output custom-report.json
```
Exclude specific RMM tools from detection:
```powershell
powershell .\rmm-hunter.exe hunt --exclude TeamViewer,AnyDesk
```
powershell .\rmm-hunter.exe hunt --exclude TeamViewer,AnyDesk```
### Eliminate Mode
**Status: Under Construction**
@@ -122,7 +122,6 @@ The modular architecture allows for extensible detection capabilities while main
## Output Formats
### JSON Report
```
json { "processes": [...], "services": [...], "binaries": [...], "autoRuns": [...], "scheduledTasks": [...], "outboundConnections": [...], "directories": [...] }```
@@ -168,7 +167,7 @@ If you use RMM-Hunter in your project or research, please provide attribution by
- Credit to **KrakenTech LLC** (https://krakensec.tech)
Example attribution:
```
```txt
This project uses RMM-Hunter by KrakenTech LLC
https://github.com/KrakenTech/RMM-Hunter
```
@@ -12,14 +12,33 @@ var appData = os.Getenv("APPDATA")
func Detect() []string {
var suspiciousDirectories []string
seen := make(map[string]bool) // Prevent duplicates
fmt.Printf("[*] Enumerating Suspicious Directories \n")
// Check for common directories
for _, dir := range common.CommonDirectories {
dir = replaceAppData(dir)
if _, err := os.Stat(dir); err == nil {
fmt.Printf(" [?] Found %s\n", dir)
suspiciousDirectories = append(suspiciousDirectories, dir)
// Check if this is a prefix pattern (ends with incomplete path such as Screen Connect "C:\Program Files (x86)\ScreenConnect Client (")
if isPrefix(dir) {
// Find all directories matching this prefix
matches := findPrefixMatches(dir)
for _, match := range matches {
if !seen[match] {
fmt.Printf(" [?] Found %s\n", match)
suspiciousDirectories = append(suspiciousDirectories, match)
seen[match] = true
}
}
} else {
// Exact match
if _, err := os.Stat(dir); err == nil {
if !seen[dir] {
fmt.Printf(" [?] Found %s\n", dir)
suspiciousDirectories = append(suspiciousDirectories, dir)
seen[dir] = true
}
}
}
}
fmt.Printf("[+] Found %d Suspicious Directories\n", len(suspiciousDirectories))
@@ -27,6 +46,7 @@ func Detect() []string {
return suspiciousDirectories
}
// replaceAppData replaces {{APPDATA}} with the actual APPDATA path
func replaceAppData(path string) string {
if strings.Contains(path, "{{APPDATA}}") {
p := strings.Replace(path, "{{APPDATA}}", "", -1)
@@ -34,3 +54,44 @@ func replaceAppData(path string) string {
}
return path
}
// isPrefix checks if a path is a prefix pattern (incomplete path for matching)
func isPrefix(path string) bool {
// If path ends with "(" or other incomplete patterns, it's a prefix
return strings.HasSuffix(path, "(") || strings.HasSuffix(path, "\\")
}
// findPrefixMatches finds all directories that start with the given prefix
func findPrefixMatches(prefix string) []string {
var matches []string
// Get the parent directory to search in
parentDir := filepath.Dir(prefix)
// Check if parent directory exists
if _, err := os.Stat(parentDir); os.IsNotExist(err) {
return matches
}
// Read all entries in the parent directory
entries, err := os.ReadDir(parentDir)
if err != nil {
return matches
}
// Get the base name prefix
basePrefix := filepath.Base(prefix)
// Check each entry
for _, entry := range entries {
if entry.IsDir() {
// Check if this directory name starts with our prefix
if strings.HasPrefix(entry.Name(), basePrefix) {
fullPath := filepath.Join(parentDir, entry.Name())
matches = append(matches, fullPath)
}
}
}
return matches
}