Skip to content

Commit 7c94017

Browse files
Add js expression checks on the wappalyzer scanner
1 parent 833234f commit 7c94017

File tree

3 files changed

+132
-3
lines changed

3 files changed

+132
-3
lines changed

go.mod

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module oott
22

3-
go 1.22.1
3+
go 1.23
4+
5+
toolchain go1.23.8
46

57
require (
68
4d63.com/gocheckcompilerdirectives v1.2.1 // indirect
@@ -37,6 +39,9 @@ require (
3739
github.com/cespare/xxhash/v2 v2.1.2 // indirect
3840
github.com/charithe/durationcheck v0.0.10 // indirect
3941
github.com/chavacava/garif v0.1.0 // indirect
42+
github.com/chromedp/cdproto v0.0.0-20250403032234-65de8f5d025b // indirect
43+
github.com/chromedp/chromedp v0.13.6 // indirect
44+
github.com/chromedp/sysutil v1.1.0 // indirect
4045
github.com/ckaznocha/intrange v0.2.0 // indirect
4146
github.com/curioswitch/go-reassign v0.2.0 // indirect
4247
github.com/daixiang0/gci v0.13.5 // indirect
@@ -52,6 +57,7 @@ require (
5257
github.com/fzipp/gocyclo v0.6.0 // indirect
5358
github.com/ghostiam/protogetter v0.3.6 // indirect
5459
github.com/go-critic/go-critic v0.11.4 // indirect
60+
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535 // indirect
5561
github.com/go-toolsmith/astcast v1.1.0 // indirect
5662
github.com/go-toolsmith/astcopy v1.1.0 // indirect
5763
github.com/go-toolsmith/astequal v1.2.0 // indirect
@@ -62,6 +68,9 @@ require (
6268
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
6369
github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
6470
github.com/gobwas/glob v0.2.3 // indirect
71+
github.com/gobwas/httphead v0.1.0 // indirect
72+
github.com/gobwas/pool v0.2.1 // indirect
73+
github.com/gobwas/ws v1.4.0 // indirect
6574
github.com/gofrs/flock v0.12.1 // indirect
6675
github.com/golang/protobuf v1.5.3 // indirect
6776
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect
@@ -180,7 +189,7 @@ require (
180189
golang.org/x/mod v0.21.0 // indirect
181190
golang.org/x/net v0.28.0 // indirect
182191
golang.org/x/sync v0.8.0 // indirect
183-
golang.org/x/sys v0.25.0 // indirect
192+
golang.org/x/sys v0.29.0 // indirect
184193
golang.org/x/text v0.18.0 // indirect
185194
golang.org/x/time v0.6.0 // indirect
186195
golang.org/x/tools v0.24.0 // indirect

go.sum

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iy
110110
github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ=
111111
github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
112112
github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
113+
github.com/chromedp/cdproto v0.0.0-20250403032234-65de8f5d025b h1:jJmiCljLNTaq/O1ju9Bzz2MPpFlmiTn0F7LwCoeDZVw=
114+
github.com/chromedp/cdproto v0.0.0-20250403032234-65de8f5d025b/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
115+
github.com/chromedp/chromedp v0.13.6 h1:xlNunMyzS5bu3r/QKrb3fzX6ow3WBQ6oao+J65PGZxk=
116+
github.com/chromedp/chromedp v0.13.6/go.mod h1:h8GPP6ZtLMLsU8zFbTcb7ZDGCvCy8j/vRoFmRltQx9A=
117+
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
118+
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
113119
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
114120
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
115121
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -156,6 +162,8 @@ github.com/go-critic/go-critic v0.11.4/go.mod h1:2QAdo4iuLik5S9YG0rT4wcZ8QxwHYkr
156162
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
157163
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
158164
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
165+
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535 h1:yE7argOs92u+sSCRgqqe6eF+cDaVhSPlioy1UkA0p/w=
166+
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535/go.mod h1:BWmvoE1Xia34f3l/ibJweyhrT+aROb/FQ6d+37F0e2s=
159167
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
160168
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
161169
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
@@ -186,6 +194,12 @@ github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80
186194
github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
187195
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
188196
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
197+
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
198+
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
199+
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
200+
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
201+
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
202+
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
189203
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
190204
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
191205
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -734,6 +748,8 @@ golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
734748
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
735749
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
736750
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
751+
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
752+
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
737753
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
738754
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
739755
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=

webscans/wappalyzer.go

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package webscans
22

33
import (
44
"bytes"
5+
"context"
56
"encoding/json"
67
"fmt"
78
"io"
@@ -20,6 +21,7 @@ import (
2021
"oott/lib"
2122

2223
"github.com/PuerkitoBio/goquery"
24+
"github.com/chromedp/chromedp"
2325
)
2426

2527
type Technology struct {
@@ -358,6 +360,26 @@ func (wp *Wappalyzer) scanWappalyzerScanByUrl(domain string, urlStr string, tech
358360
if err != nil {
359361
return result, err
360362
}
363+
364+
// Check if any technologies have JavaScript patterns to evaluate
365+
jsNeeded := false
366+
for _, tech := range technologies {
367+
if tech.Js != nil && len(tech.Js) > 0 {
368+
jsNeeded = true
369+
break
370+
}
371+
}
372+
373+
// Run JavaScript scanning if any technology has Js patterns
374+
if jsNeeded {
375+
helper.InfoPrintf("[Wappalyzer] JavaScript scanning needed for %s\n", domain)
376+
jsTechs, jsErr := wp.scanJsExpressions(domain, urlStr, technologies)
377+
if jsErr == nil && len(jsTechs) > 0 {
378+
result.Technologies = wp.appendDistinct(result.Technologies, jsTechs)
379+
} else if jsErr != nil {
380+
helper.ErrorPrintf("[!] Error during JavaScript scanning: %v\n", jsErr)
381+
}
382+
}
361383

362384
// Assign status code to result
363385
result.StatusCode = strconv.Itoa(resp.StatusCode)
@@ -677,7 +699,7 @@ func (wp *Wappalyzer) ScanWebsites(domains []string) ([]WebsiteDetails, error) {
677699

678700
var data []byte
679701
var err error
680-
if !skipDownload {
702+
if (!skipDownload) {
681703
data, err = wp.downloadJSON(url, lib.Config.Tmpfolder+fileName)
682704
if err != nil {
683705
helper.ErrorPrintf("[!] Error downloading JSON file %s. Read the default files... Error: %v\n", fileName, err)
@@ -782,3 +804,85 @@ func (wp *Wappalyzer) ScanWebsites(domains []string) ([]WebsiteDetails, error) {
782804

783805
return websiteDetails, nil
784806
}
807+
808+
func (wp *Wappalyzer) scanJsExpressions(domain string, urlStr string, technologies map[string]Technology) ([]WebsiteDetailTechnology, error) {
809+
helper.InfoPrintln("[Wappalyzer] Starting JavaScript scanning for:", urlStr)
810+
811+
// Create a lightweight headless browser using chromedp
812+
opts := append(chromedp.DefaultExecAllocatorOptions[:],
813+
chromedp.Flag("headless", true),
814+
chromedp.Flag("disable-gpu", true),
815+
chromedp.Flag("no-sandbox", true),
816+
chromedp.Flag("disable-dev-shm-usage", true),
817+
)
818+
819+
ctx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
820+
defer cancel()
821+
822+
ctx, cancel = chromedp.NewContext(ctx)
823+
defer cancel()
824+
825+
// Set a reasonable timeout
826+
ctx, cancel = context.WithTimeout(ctx, 15*time.Second)
827+
defer cancel()
828+
829+
var detectedTechs []WebsiteDetailTechnology
830+
831+
// Navigate to the URL
832+
if err := chromedp.Run(ctx, chromedp.Navigate(urlStr)); err != nil {
833+
return nil, fmt.Errorf("failed to navigate to %s: %v", urlStr, err)
834+
}
835+
836+
// Process technologies with JS patterns
837+
for name, tech := range technologies {
838+
if tech.Js == nil || len(tech.Js) == 0 {
839+
continue
840+
}
841+
842+
for jsPath, jsPattern := range tech.Js {
843+
var jsResult string
844+
845+
// Build the script to evaluate the JS variable
846+
script := fmt.Sprintf(`
847+
function getVersion() {
848+
try {
849+
const jsVar = %s;
850+
if (jsVar !== undefined) {
851+
return String(jsVar);
852+
}
853+
return null;
854+
} catch (e) {
855+
return null;
856+
}
857+
}
858+
getVersion();
859+
`, jsPath)
860+
861+
// Execute the script
862+
err := chromedp.Run(ctx, chromedp.Evaluate(script, &jsResult))
863+
if err != nil {
864+
helper.VerbosePrintf("[Wappalyzer] Error evaluating JS path %s: %v\n", jsPath, err)
865+
continue
866+
}
867+
868+
// If we got a result, check if it matches the pattern
869+
if jsResult != "" && jsResult != "null" {
870+
helper.VerbosePrintln("[-] JS Scan result:",jsPath, "->", jsResult)
871+
matches, err := wp.matchingWithModification(jsPattern, jsResult)
872+
if err != nil {
873+
helper.ErrorPrintf("[!] Error matching JS pattern for technology %s: %v\n", name, err)
874+
continue
875+
}
876+
877+
if len(matches) > 0 {
878+
helper.InfoPrintf("[Wappalyzer] Domain [%s] JS matched for technology: %s\n", domain, name)
879+
helper.VerbosePrintln("[-] JS Match result:",jsPattern, "->", matches)
880+
881+
detectedTechs = wp.appendToTechnology(detectedTechs, name, matches)
882+
}
883+
}
884+
}
885+
}
886+
887+
return detectedTechs, nil
888+
}

0 commit comments

Comments
 (0)