Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7cdee4b62c | |||
| 9512022a73 | |||
| 967b0c1de1 |
@@ -63,14 +63,12 @@ The HTML report includes:
|
|||||||
### Binary Download
|
### Binary Download
|
||||||
|
|
||||||
Download the latest compiled binary from the releases page:
|
Download the latest compiled binary from the releases page:
|
||||||
```
|
```powershell
|
||||||
|
|
||||||
powershell
|
|
||||||
Download rmm-hunter.exe
|
Download rmm-hunter.exe
|
||||||
Run with administrator privileges```
|
Run with administrator privileges
|
||||||
|
```
|
||||||
|
|
||||||
### Building from Source
|
### Building from Source
|
||||||
```
|
|
||||||
|
|
||||||
The Scurvy Library is not publicly accessible making building this tool from source impossible at the moment.
|
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
|
### Hunt Mode
|
||||||
|
|
||||||
Execute a comprehensive system scan:
|
Execute a comprehensive system scan:
|
||||||
```
|
|
||||||
|
|
||||||
powershell .\rmm-hunter.exe hunt```
|
```powershell
|
||||||
|
powershell .\rmm-hunter.exe hunt
|
||||||
|
```
|
||||||
|
|
||||||
With custom output file:
|
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:
|
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
|
### Eliminate Mode
|
||||||
|
|
||||||
**Status: Under Construction**
|
**Status: Under Construction**
|
||||||
@@ -122,7 +122,6 @@ The modular architecture allows for extensible detection capabilities while main
|
|||||||
## Output Formats
|
## Output Formats
|
||||||
|
|
||||||
### JSON Report
|
### JSON Report
|
||||||
```
|
|
||||||
|
|
||||||
json { "processes": [...], "services": [...], "binaries": [...], "autoRuns": [...], "scheduledTasks": [...], "outboundConnections": [...], "directories": [...] }```
|
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)
|
- Credit to **KrakenTech LLC** (https://krakensec.tech)
|
||||||
|
|
||||||
Example attribution:
|
Example attribution:
|
||||||
```
|
```txt
|
||||||
This project uses RMM-Hunter by KrakenTech LLC
|
This project uses RMM-Hunter by KrakenTech LLC
|
||||||
https://github.com/KrakenTech/RMM-Hunter
|
https://github.com/KrakenTech/RMM-Hunter
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -12,14 +12,33 @@ var appData = os.Getenv("APPDATA")
|
|||||||
|
|
||||||
func Detect() []string {
|
func Detect() []string {
|
||||||
var suspiciousDirectories []string
|
var suspiciousDirectories []string
|
||||||
|
seen := make(map[string]bool) // Prevent duplicates
|
||||||
|
|
||||||
fmt.Printf("[*] Enumerating Suspicious Directories \n")
|
fmt.Printf("[*] Enumerating Suspicious Directories \n")
|
||||||
// Check for common directories
|
// Check for common directories
|
||||||
for _, dir := range common.CommonDirectories {
|
for _, dir := range common.CommonDirectories {
|
||||||
dir = replaceAppData(dir)
|
dir = replaceAppData(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 _, err := os.Stat(dir); err == nil {
|
||||||
|
if !seen[dir] {
|
||||||
fmt.Printf(" [?] Found %s\n", dir)
|
fmt.Printf(" [?] Found %s\n", dir)
|
||||||
suspiciousDirectories = append(suspiciousDirectories, dir)
|
suspiciousDirectories = append(suspiciousDirectories, dir)
|
||||||
|
seen[dir] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Printf("[+] Found %d Suspicious Directories\n", len(suspiciousDirectories))
|
fmt.Printf("[+] Found %d Suspicious Directories\n", len(suspiciousDirectories))
|
||||||
@@ -27,6 +46,7 @@ func Detect() []string {
|
|||||||
return suspiciousDirectories
|
return suspiciousDirectories
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// replaceAppData replaces {{APPDATA}} with the actual APPDATA path
|
||||||
func replaceAppData(path string) string {
|
func replaceAppData(path string) string {
|
||||||
if strings.Contains(path, "{{APPDATA}}") {
|
if strings.Contains(path, "{{APPDATA}}") {
|
||||||
p := strings.Replace(path, "{{APPDATA}}", "", -1)
|
p := strings.Replace(path, "{{APPDATA}}", "", -1)
|
||||||
@@ -34,3 +54,44 @@ func replaceAppData(path string) string {
|
|||||||
}
|
}
|
||||||
return path
|
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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user