diff --git a/go.mod b/go.mod index 85db1d0..656c390 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module rmm-hunter go 1.24.7 require ( - github.com/Kraken-OffSec/Scurvy v0.0.0-20251010192328-967933276439 + github.com/Kraken-OffSec/Scurvy v0.0.0-20251011184544-e9265efd21c6 github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/lipgloss v1.1.0 @@ -12,26 +12,44 @@ require ( ) require ( + github.com/Binject/debug v0.0.0-20230508195519-26db73212a7a // indirect + github.com/alwindoss/morse v1.0.1 // indirect github.com/atotto/clipboard v0.1.4 // indirect + github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/botherder/go-savetime v1.5.0 // indirect + github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect github.com/charmbracelet/x/ansi v0.10.1 // indirect github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/ecies/go/v2 v2.0.10 // indirect + github.com/elastic/go-sysinfo v1.15.1 // indirect + github.com/elastic/go-windows v1.0.2 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/ethereum/go-ethereum v1.14.12 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mattn/go-shellwords v1.0.12 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.16.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rickb777/date v1.21.1 // indirect + github.com/rickb777/plural v1.4.2 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/ryanuber/columnize v2.1.2+incompatible // indirect github.com/sahilm/fuzzy v0.1.1 // indirect github.com/spf13/pflag v1.0.9 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect golang.org/x/text v0.21.0 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + howett.net/plist v1.0.1 // indirect ) diff --git a/go.sum b/go.sum index 826d5ff..0f42f3b 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,23 @@ +github.com/Binject/debug v0.0.0-20230508195519-26db73212a7a h1:4c0nc0krv8eh7gD809n+swLaCuFyHpxdrxwx0ZmHvBw= +github.com/Binject/debug v0.0.0-20230508195519-26db73212a7a/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ= github.com/Kraken-OffSec/Scurvy v0.0.0-20251010192328-967933276439 h1:n/B4+1K6vpKX34iISUKHzEKEND53PmxePHrtsy693Jo= github.com/Kraken-OffSec/Scurvy v0.0.0-20251010192328-967933276439/go.mod h1:hljxQLLV5S60GVVG51+u3r1agCjZ45x8jd2WiJxy0wQ= +github.com/Kraken-OffSec/Scurvy v0.0.0-20251011184544-e9265efd21c6 h1:CRH0t964ocRHXspOo8cB0DPcSfEtsGh8FenjML252HI= +github.com/Kraken-OffSec/Scurvy v0.0.0-20251011184544-e9265efd21c6/go.mod h1:0pPwYHy+r8KGzXZ8vBgyYd6qy3vX+AMRo9XLiGc8WGE= +github.com/alwindoss/morse v1.0.1 h1:PkUh5m1UHMcZ1Upvl7CmSIBMxdEBejWoQ4rQQtgJsCQ= +github.com/alwindoss/morse v1.0.1/go.mod h1:qAqJOep3jEpIpiLgqSGgLk5Zh4BZKsyzMQHuAwVPMXc= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 h1:cIAK2NNf2yafdgpFRNJrgZMwvy61BEVpGoHc2n4/yWs= +github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4/go.mod h1:SalMPBCab3yuID8nIhLfzwoBV+lBRyaC7NhuN8qL8xE= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/botherder/go-savetime v1.5.0 h1:i4vt4d4IcXgFXnIK5FBuSCUUZSV8E+s4S8TLm+9tYdM= +github.com/botherder/go-savetime v1.5.0/go.mod h1:w8rKlqwexRgSmekdFAZVfenmaZKhXBIew2tDvuox2sI= +github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 h1:BjkPE3785EwPhhyuFkbINB+2a1xATwk8SNDWnJiD41g= +github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5/go.mod h1:jtAfVaU/2cu1+wdSRPWE2c1N2qeAA3K4RH9pYgqwets= github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= @@ -23,12 +35,23 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/ecies/go/v2 v2.0.10 h1:AaLxGio0MLLbvWur4rKnLzw+K9zI+wMScIDAtqCqOtU= +github.com/ecies/go/v2 v2.0.10/go.mod h1:N73OyuR6tuKznit2LhXjrZ0XAQ234uKbzYz8pEPYzlI= +github.com/elastic/go-sysinfo v1.15.1 h1:zBmTnFEXxIQ3iwcQuk7MzaUotmKRp3OabbbWM8TdzIQ= +github.com/elastic/go-sysinfo v1.15.1/go.mod h1:jPSuTgXG+dhhh0GKIyI2Cso+w5lPJ5PvVqKlL8LV/Hk= +github.com/elastic/go-windows v1.0.2 h1:yoLLsAsV5cfg9FLhZ9EXZ2n2sQFKeDYrHenkcivY4vI= +github.com/elastic/go-windows v1.0.2/go.mod h1:bGcDpBzXgYSqM0Gx3DM4+UxFj300SZLixie9u9ixLM8= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/ethereum/go-ethereum v1.14.12 h1:8hl57x77HSUo+cXExrURjU/w1VhL+ShCTJrTwcCQSe4= +github.com/ethereum/go-ethereum v1.14.12/go.mod h1:RAC2gVMWJ6FkxSPESfbshrcKpIokgQKsVKmAuqdekDY= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= @@ -39,16 +62,26 @@ github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2J github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rickb777/date v1.21.1 h1:tUcQS8riIRoYK5kUAv5aevllFEYUEk2x8OYDyoldOn4= +github.com/rickb777/date v1.21.1/go.mod h1:gnDexsbXViZr2fCKMrY3m6IfAF5U2vSkEaiGJcNFaLQ= +github.com/rickb777/plural v1.4.2 h1:Kl/syFGLFZ5EbuV8c9SVud8s5HI2HpCCtOMw2U1kS+A= +github.com/rickb777/plural v1.4.2/go.mod h1:kdmXUpmKBJTS0FtG/TFumd//VBWsNTD7zOw7x4umxNw= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk= +github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= @@ -57,6 +90,8 @@ github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -66,5 +101,10 @@ golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= +howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= diff --git a/internal/pkg/hunt/detect/autorun/autorun.go b/internal/pkg/hunt/detect/autorun/autorun.go index bd448b0..0c065b3 100644 --- a/internal/pkg/hunt/detect/autorun/autorun.go +++ b/internal/pkg/hunt/detect/autorun/autorun.go @@ -6,7 +6,7 @@ import ( . "rmm-hunter/internal/suspicious" "strings" - "golang.org/x/sys/windows/registry" + "github.com/Kraken-OffSec/Scurvy/core/autoruns" ) func Detect() []AutoRun { @@ -14,158 +14,79 @@ func Detect() []AutoRun { fmt.Printf("[*] Enumerating AutoRun Applications\n") - // Check common autorun registry locations - autorunKeys := []string{ - `SOFTWARE\Microsoft\Windows\CurrentVersion\Run`, - `SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce`, - `SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run`, - `SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\RunOnce`, - `SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices`, - `SOFTWARE\Microsoft\Windows\CurrentVersion\RunServicesOnce`, - `SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run`, - `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit`, - `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell`, - `SOFTWARE\Microsoft\Active Setup\Installed Components`, - } + // Use Scurvy to enumerate autoruns from multiple sources + autoRuns := autoruns.GetAllAutoruns() + fmt.Printf(" [>] Dispositioning %d AutoRun Entries\n", len(autoRuns)) - // Check both HKLM and HKCU - roots := []registry.Key{registry.LOCAL_MACHINE, registry.CURRENT_USER} - rootNames := []string{"HKLM", "HKCU"} + for _, ar := range autoRuns { + // Map Scurvy autorun to our Suspicious.AutoRun struct + sar := AutoRun{ + Type: ar.Type, + Location: ar.Location, + ImagePath: ar.ImagePath, + ImageName: ar.ImageName, + Arguments: ar.Arguments, + MD5: ar.MD5, + SHA1: ar.SHA1, + SHA256: ar.SHA256, + Entry: ar.Entry, + LaunchString: ar.LaunchString, + } - fmt.Printf(" [>] Dispositioning %d AutoRun Keys\n", len(autorunKeys)*len(roots)) - - totalEntries := 0 - for i, root := range roots { - for _, keyPath := range autorunKeys { - entries := checkAutoRunKey(root, keyPath, rootNames[i]) - totalEntries += len(entries) - suspiciousAutoRuns = append(suspiciousAutoRuns, entries...) + if isSuspiciousAutoRunEntry(sar) { + fmt.Printf(" [?] Found %s | %s | %s\n", sar.Location, sar.Entry, sar.ImagePath) + suspiciousAutoRuns = append(suspiciousAutoRuns, sar) } } fmt.Printf("[+] Found %d Suspicious AutoRun Applications\n", len(suspiciousAutoRuns)) - return suspiciousAutoRuns } -func checkAutoRunKey(root registry.Key, keyPath, rootName string) []AutoRun { - var autoRuns []AutoRun - - key, err := registry.OpenKey(root, keyPath, registry.QUERY_VALUE) - if err != nil { - return autoRuns - } - defer key.Close() - - valueNames, err := key.ReadValueNames(-1) - if err != nil { - return autoRuns +// isSuspiciousAutoRunEntry determines if an autorun looks like an RMM by +// checking image path/name, location, entry and launch string against +// common RMM indicators and suspicious image suffixes. It also flags +// suspicious installation paths. +func isSuspiciousAutoRunEntry(ar AutoRun) bool { + // Prepare lowercase fields for matching + fields := []string{ + strings.ToLower(ar.ImageName), + strings.ToLower(ar.ImagePath), + strings.ToLower(ar.Location), + strings.ToLower(ar.LaunchString), + strings.ToLower(ar.Entry), } - for _, valueName := range valueNames { - value, _, err := key.GetStringValue(valueName) - if err != nil { - continue - } - - // Check if this autorun entry matches any known Suspicious patterns - if isSuspiciousAutoRun(valueName, value) { - // Analyze the executable path for additional suspicious indicators - isPathSuspicious, pathReason := analyzeExecutablePath(value) - description := extractDescription(value) - if isPathSuspicious { - description += fmt.Sprintf(" [%s]", pathReason) - } - - fmt.Printf(" [?] Found %s\\%s: %s = %s\n", rootName, keyPath, valueName, value) - autoRuns = append(autoRuns, AutoRun{ - Name: valueName, - Command: value, - Location: fmt.Sprintf("%s\\%s", rootName, keyPath), - Enabled: true, - Description: description, - }) - } - } - - return autoRuns -} - -func isSuspiciousAutoRun(name, command string) bool { - // Convert to lowercase for case-insensitive comparison - nameLower := strings.ToLower(name) - commandLower := strings.ToLower(command) - - // Check against known Suspicious names + // Match against known RMM names/keywords for _, rmm := range common.CommonRMMs { - rmmLower := strings.ToLower(rmm) - if strings.Contains(nameLower, rmmLower) || strings.Contains(commandLower, rmmLower) { + r := strings.ToLower(rmm) + for _, f := range fields { + if strings.Contains(f, r) { + return true + } + } + } + + // Match against common suspicious image suffix/patterns (path or name) + imgPathLower := strings.ToLower(ar.ImagePath) + imgNameLower := strings.ToLower(ar.ImageName) + for _, suf := range common.CommonImageSuffixes { + s := strings.ToLower(suf) + if strings.Contains(imgPathLower, s) || strings.Contains(imgNameLower, s) { return true } } - // Check against common Suspicious executable patterns - for _, imageEnd := range common.CommonImageSuffixes { - imageEndLower := strings.ToLower(imageEnd) - if strings.Contains(commandLower, imageEndLower) { - return true - } + // Suspicious installation paths + if suspicious, _ := common.AnalyzeExecutablePath(ar.ImagePath); suspicious { + return true } - - // Additional suspicious patterns - suspiciousPatterns := []string{ - "remote", "control", "assist", "support", "vnc", "rdp", "teamview", - "anydesk", "logmein", "screenconnect", "splashtop", "ultravnc", - } - - for _, pattern := range suspiciousPatterns { - if strings.Contains(nameLower, pattern) || strings.Contains(commandLower, pattern) { + // Consider launch string as a command line too + if ar.LaunchString != "" { + if suspicious, _ := common.AnalyzeExecutablePath(ar.LaunchString); suspicious { return true } } return false } - -func extractDescription(command string) string { - // Extract just the executable name from the command - parts := strings.Fields(command) - if len(parts) > 0 { - return parts[0] - } - return command -} - -func analyzeExecutablePath(command string) (bool, string) { - // Extract executable path from command - var execPath string - if strings.HasPrefix(command, "\"") { - // Handle quoted paths - endQuote := strings.Index(command[1:], "\"") - if endQuote != -1 { - execPath = command[1 : endQuote+1] - } - } else { - // Handle unquoted paths - parts := strings.Fields(command) - if len(parts) > 0 { - execPath = parts[0] - } - } - - // Check for suspicious installation paths - suspiciousPaths := []string{ - "\\temp\\", "\\tmp\\", "\\appdata\\local\\temp\\", - "\\users\\public\\", "\\programdata\\", - "\\windows\\temp\\", "\\%temp%\\", - } - - execPathLower := strings.ToLower(execPath) - for _, suspPath := range suspiciousPaths { - if strings.Contains(execPathLower, suspPath) { - return true, fmt.Sprintf("Suspicious installation path: %s", suspPath) - } - } - - return false, "" -} diff --git a/internal/pkg/hunt/detect/autorun/autorun_test.go b/internal/pkg/hunt/detect/autorun/autorun_test.go index dfd2e8c..3c64c19 100644 --- a/internal/pkg/hunt/detect/autorun/autorun_test.go +++ b/internal/pkg/hunt/detect/autorun/autorun_test.go @@ -6,11 +6,14 @@ func TestAutoRun(t *testing.T) { autoruns := Detect() for _, ar := range autoruns { t.Logf("-----") - t.Logf("Name: %s", ar.Name) - t.Logf("Command: %s", ar.Command) + t.Logf("Type: %s", ar.Type) + t.Logf("Entry: %s", ar.Entry) t.Logf("Location: %s", ar.Location) - t.Logf("Enabled: %t", ar.Enabled) - t.Logf("Description: %s", ar.Description) + t.Logf("Image: %s", ar.ImagePath) + t.Logf("Args: %s", ar.Arguments) + t.Logf("MD5: %s", ar.MD5) + t.Logf("SHA1: %s", ar.SHA1) + t.Logf("SHA256: %s", ar.SHA256) t.Logf("-----") } } diff --git a/internal/pkg/writer/htmlTemplate.go b/internal/pkg/writer/htmlTemplate.go index 9a2f58f..4b47c34 100644 --- a/internal/pkg/writer/htmlTemplate.go +++ b/internal/pkg/writer/htmlTemplate.go @@ -434,11 +434,14 @@ const htmlTemplate = ` {{if .Findings.AutoRuns}} {{range .Findings.AutoRuns}}
-
{{.Name}}
-
Command: {{.Command}}
+
{{.ImageName}}
+
Entry: {{.Entry}}
+
Type: {{.Type}}
Location: {{.Location}}
-
Enabled: {{.Enabled}}
- {{if .Description}}
Description: {{.Description}}
{{end}} +
Image: {{.ImagePath}}
+ {{if .Arguments}}
Arguments: {{.Arguments}}
{{end}} + {{if .LaunchString}}
Launch: {{.LaunchString}}
{{end}} +
Hashes: MD5={{.MD5}} SHA1={{.SHA1}} SHA256={{.SHA256}}
{{end}} {{else}} diff --git a/internal/suspicious/rmm.go b/internal/suspicious/rmm.go index 0c6c8cc..6e3c610 100644 --- a/internal/suspicious/rmm.go +++ b/internal/suspicious/rmm.go @@ -50,11 +50,22 @@ AutoRun The object used to resemble the auto run methods used by the Suspicious software. */ type AutoRun struct { - Name string `json:"name"` - Command string `json:"command"` - Location string `json:"location"` - Enabled bool `json:"enabled"` - Description string `json:"description"` + //Name string `json:"name"` + //Command string `json:"command"` + //Location string `json:"location"` + //Enabled bool `json:"enabled"` + //Description string `json:"description"` + + Type string `json:"type"` + Location string `json:"location"` + ImagePath string `json:"image_path"` + ImageName string `json:"image_name"` + Arguments string `json:"arguments"` + MD5 string `json:"md5"` + SHA1 string `json:"sha1"` + SHA256 string `json:"sha256"` + Entry string `json:"entry"` + LaunchString string `json:"launch_string"` } /* diff --git a/internal/tui/detailview.go b/internal/tui/detailview.go index 2f4f7c8..68e4d55 100644 --- a/internal/tui/detailview.go +++ b/internal/tui/detailview.go @@ -78,7 +78,7 @@ func (m DetailViewModel) renderDetails() string { switch m.typeKey { case "autoruns": ar := m.data.AutoRuns[m.index] - return fmt.Sprintf("Name: %s\nCommand: %s\nLocation: %s\nEnabled: %v\nDescription: %s", ar.Name, ar.Command, ar.Location, ar.Enabled, ar.Description) + return fmt.Sprintf("Type: %s\nEntry: %s\nLaunch: %s\nLocation: %s\nImage: %s\nArgs: %s\nMD5: %s\nSHA1: %s\nSHA256: %s", ar.Type, ar.Entry, ar.LaunchString, ar.Location, ar.ImagePath, ar.Arguments, ar.MD5, ar.SHA1, ar.SHA256) case "binaries": b := m.data.Binaries[m.index] return fmt.Sprintf("Binary: %s\nAction: delete file", b) diff --git a/internal/tui/listview.go b/internal/tui/listview.go index 9ab02ab..bf80453 100644 --- a/internal/tui/listview.go +++ b/internal/tui/listview.go @@ -46,8 +46,11 @@ func NewListView(typeKey string, sus suspicious.Suspicious, width, height int) L case "autoruns": header = "Suspicious AutoRuns" for _, ar := range sus.AutoRuns { - title := ar.Name - desc := fmt.Sprintf("%s (%s)", ar.Command, ar.Location) + title := ar.ImageName + if title == "" { + title = ar.Entry + } + desc := fmt.Sprintf("%s (%s)", ar.ImagePath, ar.Location) items = append(items, listItem{title: title, desc: desc}) } case "binaries":