From 50f2fef2770bff7d5f287c4a8f3f08a78e27cb72 Mon Sep 17 00:00:00 2001
From: haccer <22013186+haccer@users.noreply.github.com>
Date: Sun, 15 Mar 2026 13:58:08 -0400
Subject: [PATCH 01/19] Modernize subjack: Go modules, embedded fingerprints,
bug fixes, and code cleanup
---
.appveyor.yml | 20 ---
.gitignore | 30 ++--
.travis.yml | 4 -
README.md | 103 ++++--------
fingerprints.json | 358 -----------------------------------------
go.mod | 26 +++
go.sum | 73 +++++++++
main.go | 28 ++--
subjack/dns.go | 98 +++++------
subjack/dot.go | 9 --
subjack/file.go | 126 ---------------
subjack/fingerprint.go | 126 ++++-----------
subjack/requests.go | 35 ----
subjack/subjack.go | 30 ++--
14 files changed, 246 insertions(+), 820 deletions(-)
delete mode 100644 .appveyor.yml
delete mode 100644 .travis.yml
delete mode 100644 fingerprints.json
create mode 100644 go.mod
create mode 100644 go.sum
delete mode 100644 subjack/dot.go
delete mode 100644 subjack/file.go
delete mode 100644 subjack/requests.go
diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644
index c45a2dd..0000000
--- a/.appveyor.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-version: "{build}"
-
-clone_folder: c:\gopath\src\github.com\haccer\subjack
-
-environment:
- GOPATH: c:\gopath
-
-install:
- - echo %PATH%
- - echo %GOPATH%
- - set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
- - go version
-
-build: false
-
-test_script:
- - go get github.com/haccer/available
- - go get github.com/miekg/dns
- - go get github.com/valyala/fasthttp
- - go build github.com/haccer/subjack
diff --git a/.gitignore b/.gitignore
index 91603bf..19426fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,21 +1,27 @@
-# common files
-*~
-*.log
-*.bak
-*.tmp
-*.swp
-*.lock
-
-# .gitignore Go Template
-# Binaries for programs and plugins
+# Binaries
+subjack
*.exe
*.exe~
*.dll
*.so
*.dylib
-# Test binary, build with `go test -c`
+# Test binary, built with `go test -c`
*.test
-# Output of the go coverage tool, specifically when used with LiteIDE
+# Output of the go coverage tool
*.out
+
+# Go vendor directory
+/vendor/
+
+# IDE and editor files
+.idea/
+.vscode/
+*.swp
+*~
+
+# Misc
+*.log
+*.bak
+*.tmp
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 0e402a4..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-language: go
-go: master
-sudo: false
-script: go test ./...
diff --git a/README.md b/README.md
index aae5fdb..8fb7f06 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# subjack
-[](https://goreportcard.com/report/github.com/haccer/subjack)
-[](http://godoc.org/github.com/haccer/subjack/subjack)
+[](https://goreportcard.com/report/github.com/haccer/subjack)
+[](http://godoc.org/github.com/haccer/subjack/subjack)
[](https://github.com/haccer/subjack/blob/master/LICENSE)
The page you are looking for doesn't exist or has been moved.
"
- ],
- "nxdomain": false
- },
- {
- "service": "wishpond",
- "cname": [
- "wishpond.com"
- ],
- "fingerprint": [
- "https://www.wishpond.com/404?campaign=true"
- ],
- "nxdomain": false
- },
- {
- "service": "aftership",
- "cname": [
- "aftership.com"
- ],
- "fingerprint": [
- "Oops.The page you're looking for doesn't exist."
- ],
- "nxdomain": false
- },
- {
- "service": "aha",
- "cname": [
- "ideas.aha.io"
- ],
- "fingerprint": [
- "There is no portal here ... sending you back to Aha!"
- ],
- "nxdomain": false
- },
- {
- "service": "tictail",
- "cname": [
- "domains.tictail.com"
- ],
- "fingerprint": [
- "to target URL: Error Code: 404
"
- ],
- "nxdomain": false
- },
- {
- "service": "bigcartel",
- "cname": [
- "bigcartel.com"
- ],
- "fingerprint": [
- "Learn more about Worksites.net"
- ],
- "nxdomain": false
- }
-]
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..94444b9
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,26 @@
+module github.com/haccer/subjack
+
+go 1.25.1
+
+require (
+ github.com/haccer/available v0.0.0-20240515180643-5940a1670ee3
+ github.com/miekg/dns v1.1.72
+ github.com/valyala/fasthttp v1.69.0
+)
+
+require (
+ github.com/PuerkitoBio/goquery v1.8.0 // indirect
+ github.com/andybalholm/brotli v1.2.0 // indirect
+ github.com/andybalholm/cascadia v1.3.1 // indirect
+ github.com/domainr/whois v0.1.0 // indirect
+ github.com/klauspost/compress v1.18.2 // indirect
+ github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
+ github.com/valyala/bytebufferpool v1.0.0 // indirect
+ github.com/zonedb/zonedb v1.0.3544 // indirect
+ golang.org/x/mod v0.31.0 // indirect
+ golang.org/x/net v0.48.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
+ golang.org/x/text v0.32.0 // indirect
+ golang.org/x/tools v0.40.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..2a2320a
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,73 @@
+github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
+github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
+github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
+github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
+github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
+github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
+github.com/domainr/whois v0.1.0 h1:36I1Hu+5pfvJzSXjnxN3lmIXeNNlZJmM5fkk9zlRF2o=
+github.com/domainr/whois v0.1.0/go.mod h1:/6Ej6qU9Xcl/8we/QKFWhJlvUlqmEDGXgHzOwbazVpo=
+github.com/domainr/whoistest v0.0.0-20180714175718-26cad4b7c941 h1:E7ehdIemEeScp8nVs0JXNXEbzb2IsHCk13ijvwKqRWI=
+github.com/domainr/whoistest v0.0.0-20180714175718-26cad4b7c941/go.mod h1:iuCHv1qZDoHJNQs56ZzzoKRSKttGgTr2yByGpSlKsII=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/haccer/available v0.0.0-20240515180643-5940a1670ee3 h1:2AVXrUaMjg4mXqpfa6pLz9BpyjMlbgar7SJCUXtrRVA=
+github.com/haccer/available v0.0.0-20240515180643-5940a1670ee3/go.mod h1:fvazc0w1/p6PNECfXmVagg+dq8RitGXEouay/HtNyPg=
+github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
+github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
+github.com/miekg/dns v1.1.46/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
+github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
+github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
+github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
+github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
+github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
+github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI=
+github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw=
+github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=
+github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
+github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/zonedb/zonedb v1.0.3544 h1:u5a3xOaI338FclecJ/H6J7fiImVEZ/qZomJnVYIlTeM=
+github.com/zonedb/zonedb v1.0.3544/go.mod h1:h9mfHV/S6lboOkltULrbNY52cd7JZo6MbxIiqKMWPLg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
+golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
+golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
+golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/main.go b/main.go
index 4f9e9ad..46e00f7 100644
--- a/main.go
+++ b/main.go
@@ -9,31 +9,25 @@ import (
)
func main() {
- GOPATH := os.Getenv("GOPATH")
- Project := "/src/github.com/haccer/subjack/"
- configFile := "fingerprints.json"
- defaultConfig := GOPATH + Project + configFile
-
o := subjack.Options{}
- flag.StringVar(&o.Domain, "d", "", "Domain.")
+ flag.StringVar(&o.Domain, "d", "", "Single domain to check.")
flag.StringVar(&o.Wordlist, "w", "", "Path to wordlist.")
- flag.IntVar(&o.Threads, "t", 10, "Number of concurrent threads (Default: 10).")
- flag.IntVar(&o.Timeout, "timeout", 10, "Seconds to wait before connection timeout (Default: 10).")
- flag.BoolVar(&o.Ssl, "ssl", false, "Force HTTPS connections (May increase accuracy (Default: http://).")
- flag.BoolVar(&o.All, "a", false, "Find those hidden gems by sending requests to every URL. (Default: Requests are only sent to URLs with identified CNAMEs).")
- flag.BoolVar(&o.Verbose, "v", false, "Display more information per each request.")
- flag.StringVar(&o.Output, "o", "", "Output results to file (Subjack will write JSON if file ends with '.json').")
- flag.StringVar(&o.Config, "c", defaultConfig, "Path to configuration file.")
- flag.BoolVar(&o.Manual, "m", false, "Flag the presence of a dead record, but valid CNAME entry.")
-
- flag.Parse()
+ flag.IntVar(&o.Threads, "t", 10, "Number of concurrent threads.")
+ flag.IntVar(&o.Timeout, "timeout", 10, "Seconds to wait before connection timeout.")
+ flag.BoolVar(&o.Ssl, "ssl", false, "Force HTTPS connections (may increase accuracy).")
+ flag.BoolVar(&o.All, "a", false, "Send requests to every URL, not just those with identified CNAMEs.")
+ flag.BoolVar(&o.Verbose, "v", false, "Display more information per request.")
+ flag.StringVar(&o.Output, "o", "", "Output results to file (use .json extension for JSON output).")
+ flag.BoolVar(&o.Manual, "m", false, "Flag dead CNAME records even if the domain is not available for registration.")
flag.Usage = func() {
- fmt.Printf("Usage of %s:\n", os.Args[0])
+ fmt.Fprintf(os.Stderr, "Usage: %s [options]\n\nOptions:\n", os.Args[0])
flag.PrintDefaults()
}
+ flag.Parse()
+
if flag.NFlag() == 0 {
flag.Usage()
os.Exit(1)
diff --git a/subjack/dns.go b/subjack/dns.go
index bb15fed..84be0f4 100644
--- a/subjack/dns.go
+++ b/subjack/dns.go
@@ -9,62 +9,51 @@ import (
"github.com/miekg/dns"
)
-func (s *Subdomain) dns(o *Options) {
- config := o.Fingerprints
+const dnsResolver = "8.8.8.8:53"
+func check(url string, o *Options) {
if o.All {
- detect(s.Url, o.Output, o.Ssl, o.Verbose, o.Manual, o.Timeout, config)
- } else {
- if VerifyCNAME(s.Url, config) {
- detect(s.Url, o.Output, o.Ssl, o.Verbose, o.Manual, o.Timeout, config)
- }
+ detect(url, o)
+ return
+ }
- if o.Verbose {
- result := fmt.Sprintf("[Not Vulnerable] %s\n", s.Url)
- c := "\u001b[31;1mNot Vulnerable\u001b[0m"
- out := strings.Replace(result, "Not Vulnerable", c, -1)
- fmt.Printf(out)
+ if verifyCNAME(url, o.fingerprints) {
+ detect(url, o)
+ return
+ }
- if o.Output != "" {
- if chkJSON(o.Output) {
- writeJSON("", s.Url, o.Output)
- } else {
- write(result, o.Output)
- }
- }
- }
+ if o.Verbose {
+ printResult("", url, o)
}
}
-func resolve(url string) (cname string) {
- cname = ""
- d := new(dns.Msg)
- d.SetQuestion(url+".", dns.TypeCNAME)
- ret, err := dns.Exchange(d, "8.8.8.8:53")
+func resolveCNAME(domain string) string {
+ msg := new(dns.Msg)
+ msg.SetQuestion(domain+".", dns.TypeCNAME)
+ resp, err := dns.Exchange(msg, dnsResolver)
if err != nil {
- return
+ return ""
}
- for _, a := range ret.Answer {
+ for _, a := range resp.Answer {
if t, ok := a.(*dns.CNAME); ok {
- cname = t.Target
+ return t.Target
}
}
- return cname
+ return ""
}
-func nslookup(domain string) (nameservers []string) {
- m := new(dns.Msg)
- m.SetQuestion(dotDomain(domain), dns.TypeNS)
- ret, err := dns.Exchange(m, "8.8.8.8:53")
+func lookupNS(domain string) []string {
+ msg := new(dns.Msg)
+ msg.SetQuestion(domain+".", dns.TypeNS)
+ resp, err := dns.Exchange(msg, dnsResolver)
if err != nil {
- return
+ return nil
}
- nameservers = []string{}
-
- for _, a := range ret.Answer {
+ var nameservers []string
+ for _, a := range resp.Answer {
if t, ok := a.(*dns.NS); ok {
nameservers = append(nameservers, t.Ns)
}
@@ -73,37 +62,30 @@ func nslookup(domain string) (nameservers []string) {
return nameservers
}
-func nxdomain(nameserver string) bool {
- if _, err := net.LookupHost(nameserver); err != nil {
- if strings.Contains(fmt.Sprintln(err), "no such host") {
- return true
- }
+func isNXDOMAIN(host string) bool {
+ _, err := net.LookupHost(host)
+ if err != nil {
+ return strings.Contains(err.Error(), "no such host")
}
-
return false
}
-func NS(domain, output string, verbose bool) {
- nameservers := nslookup(domain)
- for _, ns := range nameservers {
+// CheckNS checks whether any of a domain's nameservers are available for purchase.
+func CheckNS(domain, output string, verbose bool) {
+ for _, ns := range lookupNS(domain) {
if verbose {
msg := fmt.Sprintf("[*] %s: Nameserver is %s\n", domain, ns)
- fmt.Printf(msg)
-
+ fmt.Print(msg)
if output != "" {
- write(msg, output)
+ writeText(msg, output)
}
}
- if nxdomain(ns) {
- av := available.Domain(ns)
-
- if av {
- msg := fmt.Sprintf("[!] %s's nameserver: %s is available for purchase!\n", domain, ns)
- fmt.Printf(msg)
- if output != "" {
- write(msg, output)
- }
+ if isNXDOMAIN(ns) && available.Domain(ns) {
+ msg := fmt.Sprintf("[!] %s's nameserver: %s is available for purchase!\n", domain, ns)
+ fmt.Print(msg)
+ if output != "" {
+ writeText(msg, output)
}
}
}
diff --git a/subjack/dot.go b/subjack/dot.go
deleted file mode 100644
index 5731867..0000000
--- a/subjack/dot.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package subjack
-
-func dotDomain(domain string) string {
- return domain + "."
-}
-
-func joinHost(server string) string {
- return server + ":53"
-}
diff --git a/subjack/file.go b/subjack/file.go
deleted file mode 100644
index 78ac6b6..0000000
--- a/subjack/file.go
+++ /dev/null
@@ -1,126 +0,0 @@
-package subjack
-
-import (
- "bufio"
- "encoding/json"
- "io/ioutil"
- "log"
- "os"
- "strings"
-)
-
-type Results struct {
- Subdomain string `json:"subdomain"`
- Vulnerable bool `json:"vulnerable"`
- Service string `json:"service,omitempty"`
- Domain string `json:"nonexist_domain,omitempty"`
-}
-
-func open(path string) (lines []string, Error error) {
- file, err := os.Open(path)
- if err != nil {
- log.Fatalln(err)
- }
-
- defer file.Close()
-
- scanner := bufio.NewScanner(file)
-
- for scanner.Scan() {
- lines = append(lines, scanner.Text())
- }
-
- return lines, scanner.Err()
-}
-
-func chkJSON(output string) (json bool) {
- json = false
-
- if strings.Contains(output, ".json") {
- if output[len(output)-5:] == ".json" {
- json = true
- }
- }
-
- return json
-}
-
-func write(result, output string) {
- f, err := os.OpenFile(output, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
- if err != nil {
- log.Fatalln(err)
- }
-
- defer f.Close()
-
- _, err = f.WriteString(result)
- if err != nil {
- log.Fatalln(err)
- }
-}
-
-func writeJSON(service, url, output string) {
- var r Results
- if strings.Contains(service, "DOMAIN") {
- r = Results{
- Subdomain: strings.ToLower(url),
- Vulnerable: true,
- Service: "unregistered domain",
- Domain: strings.Split(service, " - ")[1],
- }
- } else {
- if service != "" {
- r = Results{
- Subdomain: strings.ToLower(url),
- Vulnerable: true,
- Service: strings.ToLower(service),
- }
- } else {
- r = Results{
- Subdomain: strings.ToLower(url),
- Vulnerable: false,
- }
- }
- }
-
- f, err := os.OpenFile(output, os.O_CREATE|os.O_RDWR, 0600)
- if err != nil {
- log.Fatalln(err)
- }
-
- defer f.Close()
-
- file, err := ioutil.ReadAll(f)
- if err != nil {
- log.Fatalln(err)
- }
-
- var data []Results
- json.Unmarshal(file, &data)
- data = append(data, r)
-
- results, _ := json.Marshal(data)
-
- wf, err := os.OpenFile(output, os.O_CREATE|os.O_RDWR, 0600)
- if err != nil {
- log.Fatalln(err)
- }
-
- defer wf.Close()
-
- wf.Write(results)
-}
-
-func fingerprints(file string) (data []Fingerprints) {
- config, err := ioutil.ReadFile(file)
- if err != nil {
- log.Fatalln(err)
- }
-
- err = json.Unmarshal(config, &data)
- if err != nil {
- log.Fatalln(err)
- }
-
- return data
-}
diff --git a/subjack/fingerprint.go b/subjack/fingerprint.go
index fcc7433..5e3ee65 100644
--- a/subjack/fingerprint.go
+++ b/subjack/fingerprint.go
@@ -2,129 +2,71 @@ package subjack
import (
"bytes"
- "fmt"
"strings"
"github.com/haccer/available"
)
-type Fingerprints struct {
+type Fingerprint struct {
Service string `json:"service"`
Cname []string `json:"cname"`
Fingerprint []string `json:"fingerprint"`
Nxdomain bool `json:"nxdomain"`
}
-/*
-* Triage step to check whether the CNAME matches
-* the fingerprinted CNAME of a vulnerable cloud service.
- */
-func VerifyCNAME(subdomain string, config []Fingerprints) (match bool) {
- cname := resolve(subdomain)
- match = false
-
-VERIFY:
- for n := range config {
- for c := range config[n].Cname {
- if strings.Contains(cname, config[n].Cname[c]) {
- match = true
- break VERIFY
+func verifyCNAME(subdomain string, fingerprints []Fingerprint) bool {
+ cname := resolveCNAME(subdomain)
+ for _, fp := range fingerprints {
+ for _, c := range fp.Cname {
+ if strings.Contains(cname, c) {
+ return true
}
}
}
-
- return match
+ return false
}
-func detect(url, output string, ssl, verbose, manual bool, timeout int, config []Fingerprints) {
- service := Identify(url, ssl, manual, timeout, config)
-
- if service != "" {
- result := fmt.Sprintf("[%s] %s\n", service, url)
- c := fmt.Sprintf("\u001b[32;1m%s\u001b[0m", service)
- out := strings.Replace(result, service, c, -1)
- fmt.Printf(out)
-
- if output != "" {
- if chkJSON(output) {
- writeJSON(service, url, output)
- } else {
- write(result, output)
- }
- }
- }
-
- if service == "" && verbose {
- result := fmt.Sprintf("[Not Vulnerable] %s\n", url)
- c := "\u001b[31;1mNot Vulnerable\u001b[0m"
- out := strings.Replace(result, "Not Vulnerable", c, -1)
- fmt.Printf(out)
-
- if output != "" {
- if chkJSON(output) {
- writeJSON(service, url, output)
- } else {
- write(result, output)
- }
- }
- }
+func detect(url string, o *Options) {
+ service := identify(url, o)
+ printResult(service, url, o)
}
-/*
-* This function aims to identify whether the subdomain
-* is attached to a vulnerable cloud service and able to
-* be taken over.
- */
-func Identify(subdomain string, forceSSL, manual bool, timeout int, fingerprints []Fingerprints) (service string) {
- body := get(subdomain, forceSSL, timeout)
-
- cname := resolve(subdomain)
-
+func identify(subdomain string, o *Options) string {
+ cname := resolveCNAME(subdomain)
if len(cname) <= 3 {
cname = ""
}
- service = ""
- nx := nxdomain(subdomain)
-
-IDENTIFY:
- for f := range fingerprints {
-
- // Begin subdomain checks if the subdomain returns NXDOMAIN
- if nx {
-
- // Check if we can register this domain.
- dead := available.Domain(cname)
- if dead {
- service = "DOMAIN AVAILABLE - " + cname
- break IDENTIFY
- }
+ if isNXDOMAIN(subdomain) {
+ if available.Domain(cname) {
+ return "DOMAIN AVAILABLE - " + cname
+ }
- // Check if subdomain matches fingerprinted cname
- if fingerprints[f].Nxdomain {
- for n := range fingerprints[f].Cname {
- if strings.Contains(cname, fingerprints[f].Cname[n]) {
- service = strings.ToUpper(fingerprints[f].Service)
- break IDENTIFY
+ for _, fp := range o.fingerprints {
+ if fp.Nxdomain {
+ for _, c := range fp.Cname {
+ if strings.Contains(cname, c) {
+ return strings.ToUpper(fp.Service)
}
}
}
+ }
- // Option to always print the CNAME and not check if it's available to be registered.
- if manual && !dead && cname != "" {
- service = "DEAD DOMAIN - " + cname
- break IDENTIFY
- }
+ if o.Manual && cname != "" {
+ return "DEAD DOMAIN - " + cname
}
- // Check if body matches fingerprinted response
- for n := range fingerprints[f].Fingerprint {
- if bytes.Contains(body, []byte(fingerprints[f].Fingerprint[n])) {
- service = strings.ToUpper(fingerprints[f].Service)
- break
+ return ""
+ }
+
+ body := httpGet(subdomain, o.Ssl, o.Timeout)
+ for _, fp := range o.fingerprints {
+ for _, pattern := range fp.Fingerprint {
+ if bytes.Contains(body, []byte(pattern)) {
+ return strings.ToUpper(fp.Service)
}
}
}
- return service
+ return ""
}
diff --git a/subjack/requests.go b/subjack/requests.go
deleted file mode 100644
index db57ff5..0000000
--- a/subjack/requests.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package subjack
-
-import (
- "crypto/tls"
- "github.com/valyala/fasthttp"
- "time"
-)
-
-func get(url string, ssl bool, timeout int) (body []byte) {
- req := fasthttp.AcquireRequest()
- req.SetRequestURI(site(url, ssl))
- req.Header.Add("Connection", "close")
- resp := fasthttp.AcquireResponse()
-
- client := &fasthttp.Client{TLSConfig: &tls.Config{InsecureSkipVerify: true}}
- client.DoTimeout(req, resp, time.Duration(timeout)*time.Second)
-
- return resp.Body()
-}
-
-func https(url string, ssl bool, timeout int) (body []byte) {
- newUrl := "https://" + url
- body = get(newUrl, ssl, timeout)
-
- return body
-}
-
-func site(url string, ssl bool) (site string) {
- site = "http://" + url
- if ssl {
- site = "https://" + url
- }
-
- return site
-}
diff --git a/subjack/subjack.go b/subjack/subjack.go
index f992aba..759a270 100644
--- a/subjack/subjack.go
+++ b/subjack/subjack.go
@@ -14,49 +14,41 @@ type Options struct {
Ssl bool
All bool
Verbose bool
- Config string
Manual bool
- Fingerprints []Fingerprints
+ fingerprints []Fingerprint
}
-type Subdomain struct {
- Url string
-}
-
-/* Start processing subjack from the defined options. */
func Process(o *Options) {
var list []string
var err error
- urls := make(chan *Subdomain, o.Threads*10)
-
- if(len(o.Domain) > 0){
+ if len(o.Domain) > 0 {
list = append(list, o.Domain)
} else {
- list, err = open(o.Wordlist)
+ list, err = readLines(o.Wordlist)
}
-
+
if err != nil {
log.Fatalln(err)
}
-
- o.Fingerprints = fingerprints(o.Config)
+ o.fingerprints = loadFingerprints()
+
+ urls := make(chan string, o.Threads*10)
wg := new(sync.WaitGroup)
for i := 0; i < o.Threads; i++ {
wg.Add(1)
go func() {
+ defer wg.Done()
for url := range urls {
- url.dns(o)
+ check(url, o)
}
-
- wg.Done()
}()
}
- for i := 0; i < len(list); i++ {
- urls <- &Subdomain{Url: list[i]}
+ for _, u := range list {
+ urls <- u
}
close(urls)
From af457c4b531b97019e4b6582e1742a7bbd4c2fee Mon Sep 17 00:00:00 2001
From: haccer <22013186+haccer@users.noreply.github.com>
Date: Sun, 15 Mar 2026 14:27:58 -0400
Subject: [PATCH 02/19] Modernize codebase: add Go modules, embed fingerprints,
fix bugs, and clean up architecture
---
.gitignore | 4 +-
subjack/fingerprints.json | 358 ++++++++++++++++++++++++++++++++++++++
subjack/http.go | 34 ++++
subjack/output.go | 136 +++++++++++++++
4 files changed, 531 insertions(+), 1 deletion(-)
create mode 100644 subjack/fingerprints.json
create mode 100644 subjack/http.go
create mode 100644 subjack/output.go
diff --git a/.gitignore b/.gitignore
index 19426fe..8b4cc2d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
# Binaries
-subjack
+# Ignore the built binary, not the subjack/ source directory
+/subjack
+!/subjack/
*.exe
*.exe~
*.dll
diff --git a/subjack/fingerprints.json b/subjack/fingerprints.json
new file mode 100644
index 0000000..f9af7cc
--- /dev/null
+++ b/subjack/fingerprints.json
@@ -0,0 +1,358 @@
+[
+ {
+ "service": "fastly",
+ "cname": [
+ "fastly"
+ ],
+ "fingerprint": [
+ "Fastly error: unknown domain"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "github",
+ "cname": [
+ "github.io"
+ ],
+ "fingerprint": [
+ "There isn't a GitHub Pages site here."
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "heroku",
+ "cname": [
+ "herokuapp"
+ ],
+ "fingerprint": [
+ "herokucdn.com/error-pages/no-such-app.html"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "pantheon",
+ "cname": [
+ "pantheonsite.io"
+ ],
+ "fingerprint": [
+ "The gods are wise, but do not know of the site which you seek."
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "tumblr",
+ "cname": [
+ "domains.tumblr.com"
+ ],
+ "fingerprint": [
+ "Whatever you were looking for doesn't currently exist at this address."
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "wordpress",
+ "cname": [
+ "wordpress.com"
+ ],
+ "fingerprint": [
+ "Do you want to register"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "teamwork",
+ "cname": [
+ "teamwork.com"
+ ],
+ "fingerprint": [
+ "Oops - We didn't find your site."
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "helpjuice",
+ "cname": [
+ "helpjuice.com"
+ ],
+ "fingerprint": [
+ "We could not find what you're looking for."
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "helpscout",
+ "cname": [
+ "helpscoutdocs.com"
+ ],
+ "fingerprint": [
+ "No settings were found for this company:"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "s3 bucket",
+ "cname": [
+ "amazonaws"
+ ],
+ "fingerprint": [
+ "The specified bucket does not exist"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "ghost",
+ "cname": [
+ "ghost.io"
+ ],
+ "fingerprint": [
+ "The thing you were looking for is no longer here, or never was"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "shopify",
+ "cname": [
+ "myshopify.com"
+ ],
+ "fingerprint": [
+ "Sorry, this shop is currently unavailable."
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "uservoice",
+ "cname": [
+ "uservoice.com"
+ ],
+ "fingerprint": [
+ "This UserVoice subdomain is currently available!"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "surge",
+ "cname": [
+ "surge.sh"
+ ],
+ "fingerprint": [
+ "project not found"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "bitbucket",
+ "cname": [
+ "bitbucket.io"
+ ],
+ "fingerprint": [
+ "Repository not found"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "intercom",
+ "cname": [
+ "custom.intercom.help"
+ ],
+ "fingerprint": [
+ "This page is reserved for artistic dogs.",
+ ""
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "webflow",
+ "cname": [
+ "proxy.webflow.com",
+ "proxy-ssl.webflow.com"
+ ],
+ "fingerprint": [
+ "
The page you are looking for doesn't exist or has been moved.
"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "wishpond",
+ "cname": [
+ "wishpond.com"
+ ],
+ "fingerprint": [
+ "https://www.wishpond.com/404?campaign=true"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "aftership",
+ "cname": [
+ "aftership.com"
+ ],
+ "fingerprint": [
+ "Oops.The page you're looking for doesn't exist."
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "aha",
+ "cname": [
+ "ideas.aha.io"
+ ],
+ "fingerprint": [
+ "There is no portal here ... sending you back to Aha!"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "tictail",
+ "cname": [
+ "domains.tictail.com"
+ ],
+ "fingerprint": [
+ "to target URL: Error Code: 404
"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "bigcartel",
+ "cname": [
+ "bigcartel.com"
+ ],
+ "fingerprint": [
+ "Oops! We could’t find that page.
"
+ ],
+ "nxdomain": false
+ },
+ {
+ "service": "campaignmonitor",
+ "cname": [
+ "createsend.com"
+ ],
+ "fingerprint": [
+ "Double check the URL or