├── .gitignore ├── main.go ├── main_test.go ├── .vscode └── launch.json ├── go.mod ├── core ├── logger │ └── logger.go ├── jsdata.go ├── utils.go ├── urladdr.go ├── signatures.go ├── core.go ├── proxy.go ├── cert.go └── webPage.go ├── LICENSE ├── cmd ├── search.go ├── proxy.go └── root.go ├── readme.md ├── go.sum └── .jsf_signatures.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | /test.txt 2 | tmp.txt 3 | .vscode/settings.json 4 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/ThreatUnkown/jsubfinder/cmd" 4 | 5 | //"strconv" 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var result int 8 | 9 | func BenchmarkMain(b *testing.B) { 10 | for x := 0; x < b.N; x++ { 11 | //run my awesome test method 12 | main() 13 | //fmt.Printf("Y = %d\n", y) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Package", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${fileDirname}", 13 | "args": ["search", "-f", "test_urls", "-d"] 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ThreatUnkown/jsubfinder 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/elazarl/goproxy v1.4.0 7 | github.com/elazarl/goproxy/ext v0.0.0-20220403042543-a53172b9392e // indirect 8 | github.com/jpillora/go-tld v1.2.1 9 | github.com/kr/pretty v0.3.0 // indirect 10 | github.com/mitchellh/go-homedir v1.1.0 11 | github.com/rogpeppe/go-internal v1.8.1 // indirect 12 | github.com/sirupsen/logrus v1.9.3 13 | github.com/spf13/cobra v1.8.1 14 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 15 | gopkg.in/yaml.v2 v2.4.0 // indirect 16 | gopkg.in/yaml.v3 v3.0.1 17 | ) 18 | -------------------------------------------------------------------------------- /core/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | "strings" 7 | 8 | "github.com/sirupsen/logrus" 9 | ) 10 | 11 | var ( 12 | Log *logrus.Logger 13 | //zip = flag.String("zipkin", os.Getenv("ZIPKIN"), "Zipkin address") 14 | // port = flag.String("port", "8080", "Port number on which the service should run") 15 | // ip = flag.String("ip", "0.0.0.0", "Preferred IP address to run the service on") 16 | //serviceName = "user" 17 | ) 18 | 19 | // This initiates a new Logger and defines the format for logs 20 | func InitDetailedLogger() { 21 | 22 | Log = logrus.New() 23 | Log.SetReportCaller(true) 24 | 25 | Log.SetFormatter(&logrus.JSONFormatter{ 26 | TimestampFormat: "", 27 | PrettyPrint: true, 28 | CallerPrettyfier: func(f *runtime.Frame) (string, string) { 29 | s := strings.Split(f.Function, ".") 30 | funcname := s[len(s)-1] 31 | 32 | return funcname, "" 33 | }, 34 | }) 35 | 36 | // Set output of logs to Stdout 37 | // Change to f for redirecting to file 38 | Log.SetOutput(os.Stdout) 39 | 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 hiddengearz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /core/jsdata.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "time" 7 | 8 | l "github.com/ThreatUnkown/jsubfinder/core/logger" 9 | ) 10 | 11 | type JavaScript struct { 12 | UrlAddr 13 | Content string //Content of the JS file 14 | subdomains map[string]bool //Subdomains found in content of JavaScript 15 | secrets map[string]bool //Secrets found content of JavaScript 16 | } 17 | 18 | //GetSubDomains uses regex to find subdomains in the content of JS files 19 | func (js *JavaScript) GetSubDomains() error { 20 | if Debug { 21 | defer TimeTrack(time.Now(), "GetSubDomains "+js.UrlAddr.string) 22 | } 23 | 24 | //Regex for finding a subdomain 25 | subdomainRegex, err := regexp.Compile("([a-zA-Z0-9][a-zA-Z0-9-]*\\." + js.UrlAddr.rootDomain + ")") 26 | if err != nil { 27 | l.Log.Debug(err) 28 | return err 29 | } 30 | 31 | results := subdomainRegex.FindAllStringSubmatch(js.Content, -1) 32 | for _, result := range results { //for all found subdomains... 33 | tmp := result[1] 34 | tmp = strings.Replace(tmp, "u002F", "", -1) 35 | tmp = strings.Replace(tmp, "x2F", "", -1) 36 | js.subdomains[tmp] = true 37 | if Command != "proxy" { 38 | if IsNewSubdomain(tmp) { 39 | AddNewSubdomain(tmp) 40 | } 41 | } 42 | } 43 | return nil 44 | 45 | } 46 | 47 | //GetSecrets uses regex to find secrets in the content of JS files 48 | func (js *JavaScript) GetSecrets() error { 49 | //js.secrets = make(map[string]bool) 50 | if Debug { 51 | defer TimeTrack(time.Now(), "GetSecrets "+js.UrlAddr.string) 52 | } 53 | for _, sig := range Signatures { 54 | for _, entry := range sig.Match(js) { 55 | js.secrets[entry] = true 56 | if Command != "proxy" { 57 | if IsNewSecret(entry) { 58 | AddNewSecret(entry) 59 | } 60 | } 61 | } 62 | 63 | } 64 | 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /cmd/search.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "os" 7 | "strings" 8 | "time" 9 | 10 | C "github.com/ThreatUnkown/jsubfinder/core" 11 | l "github.com/ThreatUnkown/jsubfinder/core/logger" 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | func init() { 16 | //rootCmd.AddCommand(cmdExec) 17 | searchExec.PersistentFlags().StringVarP(&C.InputFile, "inputFile", "f", "", "File containing domains") 18 | searchExec.PersistentFlags().StringSliceVarP(&C.InputURLs, "url", "u", []string{}, "Url to check") 19 | searchExec.PersistentFlags().BoolVarP(&C.Crawl, "crawl", "c", false, "Enable crawling") 20 | searchExec.PersistentFlags().IntVarP(&C.Threads, "threads", "t", 5, "Ammount of threads to be used") 21 | searchExec.PersistentFlags().BoolVarP(&C.Greedy, "greedy", "g", false, "Check all files for URL's not just Javascript") 22 | 23 | } 24 | 25 | //Search the provided URL's 26 | var searchExec = &cobra.Command{ 27 | Use: "search", 28 | Short: "Search javascript/URL's for domains", 29 | Long: `Execute the command specified`, 30 | PersistentPreRun: func(cmd *cobra.Command, args []string) { 31 | C.Command = "search" 32 | err := safetyChecks() //safety checks 33 | if err != nil { 34 | l.Log.Fatal(err) 35 | } 36 | 37 | err = getURLs() //Get the URL's from input file, -u flag or stdins 38 | if err != nil { 39 | l.Log.Fatal(err) 40 | } 41 | }, 42 | Run: func(cmd *cobra.Command, arguments []string) { 43 | if C.Debug { 44 | defer C.TimeTrack(time.Now(), "searchExec") 45 | } 46 | C.ExecSearch() //Start the search 47 | }, 48 | } 49 | 50 | //Retrieve the URL's needed to be searched 51 | func getURLs() (err error) { 52 | if len(C.InputURLs) != 0 { //if -u isn't empty 53 | return 54 | } else if C.InputFile != "" { //else if -f isn't empty 55 | C.InputURLs, err = C.ReadFile(C.InputFile) 56 | return 57 | } else if stat, _ := os.Stdin.Stat(); (stat.Mode() & os.ModeCharDevice) == 0 { //finally try from input being piped (stdin) 58 | sc := bufio.NewScanner(os.Stdin) 59 | for sc.Scan() { 60 | C.InputURLs = append(C.InputURLs, strings.ToLower(sc.Text())) 61 | } 62 | return 63 | } else { 64 | return errors.New("no URL's provided") 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /cmd/proxy.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | 7 | C "github.com/ThreatUnkown/jsubfinder/core" 8 | l "github.com/ThreatUnkown/jsubfinder/core/logger" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | func init() { 13 | //rootCmd.AddCommand(cmdExec) 14 | proxyExec.PersistentFlags().IntVarP(&C.LocalPort, "port", "p", 8444, "Port for the proxy to listen on") 15 | proxyExec.PersistentFlags().StringVarP(&C.UpsteamProxy, "upstream-proxy", "u", "http://127.0.0.1:8888", "Adress of upsteam proxy e.g http://127.0.0.1:8888") 16 | proxyExec.PersistentFlags().StringSliceVar(&C.Scope, "scope", []string{}, "Url's in scope seperated by commas. e.g www.google.com,www.netflix.com") 17 | } 18 | 19 | //Start JSubFiner in proxy mode 20 | var proxyExec = &cobra.Command{ 21 | Use: "proxy", 22 | Short: "Run JSubfinder as a proxy", 23 | Long: `Execute the command specified`, 24 | Run: func(cmd *cobra.Command, arguments []string) { 25 | var upsteamProxySet bool = false 26 | if cmd.Flags().Changed("upstream-proxy") { 27 | upsteamProxySet = true 28 | } 29 | //Start the proxy server 30 | C.StartProxy(":"+strconv.Itoa(C.LocalPort), upsteamProxySet) 31 | }, 32 | PersistentPreRun: func(cmd *cobra.Command, args []string) { 33 | C.Command = "proxy" 34 | //Run the safety checks 35 | err := safetyChecks() 36 | if err != nil { 37 | l.Log.Fatal(err) 38 | } 39 | 40 | //Make sure the provided proxy url is a http proxy 41 | if !strings.HasPrefix(C.UpsteamProxy, "http://") { 42 | l.Log.Fatal("Upsteam Proxy doesn't contain http://") 43 | } 44 | 45 | //Stuff to setup the CA 46 | /* 47 | //Get the homedir 48 | home, err := homedir.Dir() 49 | if err != nil { 50 | l.Log.Fatal(err) 51 | } 52 | 53 | C.SSHFolder = home + "/.ssh/" 54 | if !C.FolderExists(C.SSHFolder) { 55 | l.Log.Fatal("Folder " + C.SSHFolder + " doesnt exist. Please create it") 56 | } 57 | 58 | C.Certificate = C.SSHFolder + "jsubfinder.pub" 59 | C.Key = C.SSHFolder + "jsubfinder" 60 | 61 | if !C.FileExists(C.Certificate) || !C.FileExists(C.Key) { 62 | 63 | fmt.Println("creating cert!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") 64 | err = C.CreateAuthority(C.Certificate, C.Key) 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | } 69 | C.ReadCertificateDisk(C.Certificate, C.Key) 70 | */ 71 | }, 72 | } 73 | -------------------------------------------------------------------------------- /core/utils.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "os" 10 | "strconv" 11 | "strings" 12 | "time" 13 | 14 | l "github.com/ThreatUnkown/jsubfinder/core/logger" 15 | ) 16 | 17 | //RadFile reads the content of a file and returns it in a slice 18 | func ReadFile(filePath string) (content []string, err error) { 19 | if Debug { 20 | defer TimeTrack(time.Now(), "ReadFile "+filePath) 21 | } 22 | 23 | file, err := os.Open(filePath) 24 | if err != nil { 25 | l.Log.Debug(err) 26 | return 27 | } 28 | 29 | defer file.Close() 30 | 31 | scanner := bufio.NewScanner(file) 32 | for scanner.Scan() { 33 | //fmt.Println(scanner.Text()) 34 | content = append(content, scanner.Text()) 35 | } 36 | 37 | if err := scanner.Err(); err != nil { 38 | return content, err 39 | } 40 | 41 | return 42 | } 43 | 44 | func ReadFileIntoBytes(filePath string) (content []byte, err error) { 45 | content, err = ioutil.ReadFile(filePath) // b has type []byte 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | 50 | return 51 | } 52 | 53 | //Find searches a []string for a substring and return it's position in the array and a bool for if it's in the array 54 | func Find(slice []string, val string) (int, bool) { 55 | //if Debug { 56 | // defer TimeTrack(time.Now(), "Find "+val) 57 | //} 58 | for i, item := range slice { 59 | if item == val { 60 | return i, true 61 | } 62 | } 63 | return -1, false 64 | } 65 | 66 | //GetHTTprotocol parses a given URL to get it's protocol e.g http:// or https:// 67 | func GetHTTprotocol(url string) (protocol string, err error) { 68 | //if Debug { 69 | // defer TimeTrack(time.Now(), "GetHTTprotocol "+url) 70 | //} 71 | if strings.HasPrefix(url, "http://") { 72 | protocol = "http://" 73 | return 74 | } else if strings.HasPrefix(url, "https://") { 75 | protocol = "https://" 76 | return 77 | } else { 78 | err = errors.New("No prefix") 79 | return 80 | } 81 | } 82 | 83 | //TimeTrack times a function just for testing 84 | func TimeTrack(start time.Time, name string) { 85 | elapsed := time.Since(start) 86 | fmt.Println(name + " took " + strconv.FormatFloat(elapsed.Seconds(), 'f', 3, 64) + "s") 87 | } 88 | 89 | //SaveResults saves the content to the spcified file 90 | func SaveResults(fileLocation string, newContent []string) error { 91 | newFile, err := os.OpenFile(fileLocation, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 92 | 93 | if err != nil { 94 | return err 95 | } 96 | 97 | datawriter := bufio.NewWriter(newFile) 98 | 99 | for _, data := range newContent { 100 | _, _ = datawriter.WriteString(data + "\n") 101 | } 102 | 103 | datawriter.Flush() 104 | newFile.Close() 105 | return nil 106 | } 107 | 108 | //fileExists returns a bool if the file exists or not 109 | func FileExists(filename string) bool { 110 | info, err := os.Stat(filename) 111 | if os.IsNotExist(err) { 112 | l.Log.Debug(err) 113 | return false 114 | } 115 | return !info.IsDir() 116 | } 117 | 118 | //folderExists returns a bool if the folder exists or not 119 | func FolderExists(filename string) bool { 120 | info, err := os.Stat(filename) 121 | if os.IsNotExist(err) { 122 | l.Log.Debug(err) 123 | return false 124 | } 125 | return info.IsDir() 126 | } 127 | -------------------------------------------------------------------------------- /core/urladdr.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "errors" 5 | "io/ioutil" 6 | "net/http" 7 | "strings" 8 | "time" 9 | 10 | l "github.com/ThreatUnkown/jsubfinder/core/logger" 11 | tld "github.com/jpillora/go-tld" 12 | ) 13 | 14 | type UrlAddr struct { 15 | string //URL address 16 | rootDomain string //Top Level Domain of the URL 17 | } 18 | 19 | //GetContent retrieves the content of urls - #### MAYBE CHECK FOR redirects and follow them???? 20 | func (u *UrlAddr) GetContent(client *http.Client) (newContent string, isJS bool, err error) { 21 | //defer lock.RUnlock() 22 | 23 | var req *http.Request 24 | var resp *http.Response 25 | if Debug { 26 | defer TimeTrack(time.Now(), "GetContent "+u.string) 27 | } 28 | 29 | //If the provided URL starts with HTTP/s make a request 30 | if strings.HasPrefix(u.string, "https://") || strings.HasPrefix(u.string, "http://") { 31 | 32 | if IsUrlVisited(u.string) { 33 | err = errors.New("Url " + u.string + " was been scanned before") 34 | return 35 | } 36 | 37 | req, err = http.NewRequest(http.MethodGet, u.string, nil) 38 | if err != nil { 39 | l.Log.Debug("Client get failed: %s\n", err) 40 | return 41 | } 42 | 43 | req.Header.Set("User-agent", "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0") 44 | 45 | resp, err = client.Do(req) 46 | if err != nil { 47 | l.Log.Debug("Client get failed: %s\n", err) 48 | return 49 | } 50 | 51 | } else { //if the request doesn't start with HTTP/s, add it 52 | if IsUrlVisited("http://" + u.string) { 53 | err = errors.New("Url " + "http://" + u.string + " was been scanned before") 54 | return 55 | } 56 | 57 | req, err = http.NewRequest(http.MethodGet, "http://"+u.string, nil) 58 | if err != nil { 59 | l.Log.Debug("Client get failed: %s\n", err) 60 | return 61 | } 62 | 63 | req.Header.Set("User-agent", "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0") 64 | 65 | resp, err = client.Do(req) 66 | 67 | if err != nil && !strings.Contains(string(err.Error()), "no such host") { //if there is an error and its not due to dns 68 | l.Log.Debug("new err Client get failed: %s\n", err) 69 | 70 | if IsUrlVisited("https://" + u.string) { 71 | err = errors.New("Url " + u.string + " was been scanned before") 72 | return 73 | } 74 | 75 | req, err = http.NewRequest(http.MethodGet, "https://"+u.string, nil) //try a request with https 76 | if err != nil { 77 | l.Log.Debug("Client get failed: %s\n", err) 78 | return 79 | } 80 | 81 | resp, err = client.Do(req) 82 | if err != nil { 83 | l.Log.Debug("Client get failed: %s\n", err) 84 | return 85 | } 86 | u.string = "https://" + u.string 87 | } else if err != nil { 88 | l.Log.Debug("Client get failed: %s\n", err) 89 | return 90 | } else { //if no error 91 | u.string = "http://" + u.string 92 | 93 | } 94 | } 95 | 96 | contenType := resp.Header.Get("Content-Type") 97 | if contenType == "" { 98 | isJS = false 99 | } else if strings.Contains(contenType, "javascript") { 100 | isJS = true 101 | //fmt.Println("content type js" + u.string) 102 | } 103 | 104 | //read the body and return it 105 | defer resp.Body.Close() 106 | bodyBytes, err := ioutil.ReadAll(resp.Body) 107 | if err != nil { 108 | return 109 | } 110 | 111 | newContent = (string(bodyBytes)) 112 | 113 | return 114 | } 115 | 116 | //Get the Top Level Domain of the URL 117 | func (u *UrlAddr) GetRootDomain() (err error) { 118 | u2, err := tld.Parse(u.string) 119 | if err != nil { 120 | return 121 | } 122 | 123 | u.rootDomain = u2.Domain + "." + u2.TLD 124 | return 125 | } 126 | -------------------------------------------------------------------------------- /core/signatures.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "errors" 5 | "io/ioutil" 6 | "regexp" 7 | "regexp/syntax" 8 | 9 | l "github.com/ThreatUnkown/jsubfinder/core/logger" 10 | "gopkg.in/yaml.v3" 11 | 12 | //"strconv" 13 | "strings" 14 | ) 15 | 16 | var ConfigSigs ConfigSignature 17 | var Signatures []Signature 18 | var Blacklisted_extensions []string 19 | var PrintSecrets bool = false 20 | 21 | type ConfigSignature struct { 22 | Signatures []struct { 23 | Part string `yaml:"part"` 24 | Match string `yaml:"match,omitempty"` 25 | Name string `yaml:"name"` 26 | Regex string `yaml:"regex,omitempty"` 27 | Comment string `yaml:"comment,omitempty"` 28 | } `yaml:"signatures"` 29 | } 30 | 31 | func (config *ConfigSignature) ParseConfig(fileName string) error { 32 | if FileExists(fileName) { 33 | //fmt.Println("Parsing " + fileName) 34 | 35 | yamlFile, err := ioutil.ReadFile(fileName) 36 | if err != nil { 37 | return err 38 | 39 | } 40 | 41 | err = yaml.Unmarshal(yamlFile, &config) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | //fmt.Printf("Result: %v\n", config) 47 | 48 | } else { 49 | return errors.New(fileName + " doesn't exist") 50 | } 51 | return nil 52 | 53 | } 54 | 55 | type Signature interface { 56 | Name() string 57 | Match(js *JavaScript) []string 58 | } 59 | 60 | func (config *ConfigSignature) GetSignatures() ([]Signature, error) { 61 | var signatures []Signature 62 | for _, signature := range config.Signatures { 63 | if signature.Match != "" { 64 | signatures = append(signatures, &SimpleSignature{ 65 | name: signature.Name, 66 | part: signature.Part, 67 | match: signature.Match, 68 | }) 69 | } else { 70 | if _, err := syntax.Parse(signature.Match, syntax.FoldCase); err == nil { 71 | signatures = append(signatures, &PatternSignature{ 72 | name: signature.Name, 73 | part: signature.Part, 74 | match: regexp.MustCompile(signature.Regex), 75 | }) 76 | } else { 77 | return signatures, err 78 | } 79 | } 80 | } 81 | //fmt.Println("total signatures: " + strconv.Itoa(len(signatures))) 82 | return signatures, nil 83 | } 84 | 85 | type SimpleSignature struct { 86 | part string 87 | match string 88 | name string 89 | } 90 | 91 | func (s *SimpleSignature) Name() string { 92 | return s.name 93 | } 94 | func (s *SimpleSignature) Match(js *JavaScript) (secrets []string) { 95 | /* 96 | switch s.part { 97 | case PartPath: 98 | haystack = &file.Path 99 | matchPart = PartPath 100 | case PartFilename: 101 | haystack = &file.Filename 102 | matchPart = PartPath 103 | case PartExtension: 104 | haystack = &file.Extension 105 | matchPart = PartPath 106 | default: 107 | return false, matchPart 108 | } 109 | 110 | 111 | */ 112 | 113 | if s.match != "" { 114 | 115 | if strings.Contains(js.UrlAddr.string, s.match) { 116 | secrets = append(secrets, s.name+" "+s.match+" found in URL") 117 | l.Log.Debug(s.name + " " + s.match + " found within URL of " + js.UrlAddr.string) 118 | } 119 | if strings.Contains(js.Content, s.match) { 120 | secrets = append(secrets, s.name+" "+s.match+" found in content") 121 | l.Log.Debug(s.name + " " + s.match + " found in content of " + js.UrlAddr.string) 122 | } 123 | 124 | return secrets 125 | 126 | } 127 | 128 | return secrets 129 | 130 | } 131 | 132 | type PatternSignature struct { 133 | part string 134 | match *regexp.Regexp 135 | name string 136 | } 137 | 138 | func (s *PatternSignature) Name() string { 139 | return s.name 140 | } 141 | func (s *PatternSignature) Match(js *JavaScript) (secrets []string) { 142 | if s.match != nil { 143 | if s.match.MatchString(js.UrlAddr.string) { 144 | tmp := s.match.FindAllString(js.UrlAddr.string, -1) 145 | for _, secret := range tmp { 146 | secrets = append(secrets, s.name+" "+secret+" found within URL of "+js.UrlAddr.string) 147 | l.Log.Debug(s.name + " " + secret + " found within URL of " + js.UrlAddr.string) 148 | } 149 | } 150 | if s.match.MatchString(js.Content) { 151 | tmp := s.match.FindAllString(js.Content, -1) 152 | for _, secret := range tmp { 153 | secrets = append(secrets, s.name+" "+secret+" found within content of "+js.UrlAddr.string) 154 | l.Log.Debug(s.name + " " + secret + " found within content of " + js.UrlAddr.string) 155 | } 156 | } 157 | } 158 | 159 | return secrets 160 | } 161 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | 7 | C "github.com/ThreatUnkown/jsubfinder/core" 8 | l "github.com/ThreatUnkown/jsubfinder/core/logger" 9 | "github.com/mitchellh/go-homedir" 10 | "github.com/sirupsen/logrus" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var ( 15 | rootCmd = &cobra.Command{ 16 | Use: "JSubFinder ", 17 | Short: "jsubfinder searches webpages for javascript & analyzes them for hidden subdomains and secrets (wip).", 18 | Long: `jsubfinder searches webpages for javascript & analyzes them for hidden subdomains and secrets (wip).`, 19 | PersistentPreRun: func(cmd *cobra.Command, args []string) { 20 | }, 21 | } 22 | ) 23 | 24 | func init() { 25 | l.Log = logrus.New() 26 | rootCmd.AddCommand(searchExec) 27 | rootCmd.AddCommand(proxyExec) 28 | 29 | rootCmd.PersistentFlags().StringVarP(&C.OutputFile, "output-file", "o", "", "name/location to store the file") 30 | rootCmd.PersistentFlags().StringVarP(&C.SecretsOutputFile, "secrets", "s", "secrets.txt", "name/location to store the secrets, using --secret=\"\" will use the default filename") 31 | rootCmd.PersistentFlags().BoolVarP(&C.Debug, "debug", "d", false, "Enable debug mode. Logs are stored in log.info") 32 | 33 | rootCmd.PersistentFlags().BoolVarP(&C.Silent, "silent", "S", false, "Disable printing to the console") 34 | rootCmd.PersistentFlags().StringVar(&C.Sig, "sig", "", "Location of signatures for finding secrets") 35 | rootCmd.PersistentFlags().BoolVarP(&C.SSL, "nossl", "K", true, "Skip SSL cert verification") 36 | 37 | } 38 | 39 | func Execute() error { 40 | return rootCmd.Execute() 41 | } 42 | 43 | //Things to check before running any code. 44 | func safetyChecks() error { 45 | 46 | if C.Debug && C.Silent { //if debug and silent flag are enabled return error 47 | l.Log.SetLevel(logrus.DebugLevel) 48 | return errors.New("please choose Debug mode or silent mode. Enabling both is conflicting") 49 | } else if C.Debug { //Setup logging 50 | 51 | l.InitDetailedLogger() 52 | l.Log.SetLevel(logrus.DebugLevel) 53 | l.Log.Debug("Debug mode enabled") 54 | 55 | } else if C.Silent { //If silent supress all errors 56 | l.Log.SetLevel(logrus.PanicLevel) 57 | } 58 | 59 | if C.Silent && C.OutputFile == "" { //if silent and no output return error 60 | l.Log.SetLevel(logrus.DebugLevel) 61 | return errors.New("please disable silent mode or output the results to a file with the -o flag otherwise you can't view the results") 62 | } 63 | 64 | //Check if we can write to the outputFile 65 | if C.OutputFile != "" { 66 | file, err := os.OpenFile(C.OutputFile, os.O_WRONLY, 0666) 67 | if err != nil { 68 | if os.IsPermission(err) { 69 | return (err) 70 | } 71 | 72 | } 73 | file.Close() 74 | } 75 | 76 | //Ensure you don't provide both url and input file 77 | if C.InputFile != "" && len(C.InputURLs) != 0 { 78 | return errors.New("Provide either -f or -u, you can't provide both") 79 | } 80 | 81 | //ensure signature file exists 82 | if rootCmd.Flags().Changed("secrets") { 83 | C.FindSecrets = true 84 | 85 | if C.Sig == "" { 86 | home, err := homedir.Dir() 87 | if err != nil { 88 | l.Log.Debug("Unable to find homedir, please provide the location of the signature fil with -sig") 89 | return err 90 | } 91 | C.Sig = home + "/.jsf_signatures.yaml" 92 | } 93 | 94 | //Load signatures for secrets 95 | err := C.ConfigSigs.ParseConfig(C.Sig) //https://github.com/eth0izzle/shhgit/blob/090e3586ee089f013659e02be16fd0472b629bc7/core/signatures.go 96 | if err != nil { 97 | return err 98 | } 99 | C.Signatures, err = C.ConfigSigs.GetSignatures() 100 | if err != nil { 101 | return err 102 | } 103 | C.Blacklisted_extensions = []string{".exe", ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".tif", ".psd", ".xcf", ".zip", ".tar.gz", ".ttf", ".lock"} 104 | 105 | if C.Silent == true { 106 | C.PrintSecrets = false 107 | } else { 108 | C.PrintSecrets = true 109 | } 110 | 111 | if C.SecretsOutputFile == "" { 112 | C.SecretsOutputFile = "secrets.txt" 113 | } 114 | 115 | } 116 | 117 | //Ensure output and secret file aren't the same 118 | if C.OutputFile == C.SecretsOutputFile { 119 | return errors.New("The output file for the results and secrets can't be the same") 120 | } 121 | 122 | //if silent && debug { 123 | // l.Log.Fatal("Enable silent mode or debug mode. Can't print debug information if silent mode is enabled.") 124 | //} 125 | 126 | //ensure output is being sent to console or outputfile. 127 | if C.Silent && C.OutputFile == "" { 128 | return errors.New("if you aren't saving the output with -o and you want the console output silenced -S, what's the point of running JSubfinder?") 129 | } 130 | 131 | return nil 132 | } 133 | -------------------------------------------------------------------------------- /core/core.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "sync" 7 | 8 | l "github.com/ThreatUnkown/jsubfinder/core/logger" 9 | ) 10 | 11 | var ( 12 | InputURLs []string 13 | Threads int 14 | InputFile string 15 | OutputFile string 16 | SecretsOutputFile string 17 | Greedy bool 18 | Debug bool = false 19 | Crawl bool 20 | FindSecrets bool = false 21 | Sig string 22 | Silent bool 23 | SSL bool = false 24 | LocalPort int 25 | UpsteamProxy string 26 | 27 | allPages []WebPage 28 | 29 | newSubdomains map[string]bool = make(map[string]bool) 30 | newSecrets map[string]bool = make(map[string]bool) 31 | urlsVisited map[string]bool = make(map[string]bool) 32 | 33 | lock sync.RWMutex 34 | 35 | Command string 36 | ) 37 | 38 | func ExecSearch() error { 39 | 40 | //setup go routine 41 | var allPages []WebPage 42 | var wg = sync.WaitGroup{} 43 | 44 | guard := make(chan struct{}, Threads) 45 | 46 | //Start a go routine and start fetching results for each URL provided 47 | results := make(chan WebPage, len(InputURLs)) 48 | for _, url := range InputURLs { 49 | guard <- struct{}{} 50 | wg.Add(1) 51 | go func(url string) { 52 | 53 | results <- GetResults(url) //fetch results and return them to a channel 54 | <-guard 55 | wg.Done() 56 | }(url) 57 | } 58 | 59 | wg.Wait() 60 | close(guard) 61 | close(results) 62 | 63 | //Take results from the channel and add them to []webpage 64 | for result := range results { 65 | if result.Content != "" { //the urladdr will be blank if the page can't be reached. Thus don't add it. 66 | allPages = append(allPages, result) 67 | } 68 | } 69 | 70 | //If Debug mode, print results in debug format 71 | if Debug { 72 | for _, url := range allPages { //For each URL the user provided 73 | fmt.Println("url: " + url.UrlAddr.string) //print the url 74 | fmt.Println("\trootDomain: " + url.UrlAddr.rootDomain) //print the root domain 75 | for _, js := range url.JSFiles { //For each URL with JS 76 | fmt.Println("\tjs: " + js.UrlAddr.string) //Print the URL 77 | fmt.Println("\t\tcontent length: " + strconv.Itoa(len(js.Content))) // Print the content length 78 | for subdomain := range js.subdomains { //print the subdomain found in the js 79 | fmt.Println("\t\tsubdomain: " + subdomain) 80 | 81 | } 82 | for secret := range js.secrets { 83 | fmt.Println("\t\tsecret: " + secret) 84 | 85 | } 86 | } 87 | } 88 | } else { //if not debug mode print subdomains & secrets 89 | var tmp []string 90 | for subdomain := range newSubdomains { 91 | if !Silent { 92 | fmt.Println(subdomain) 93 | } 94 | if OutputFile != "" { 95 | tmp = append(tmp, subdomain) 96 | } 97 | 98 | } 99 | if OutputFile != "" { 100 | err := SaveResults(OutputFile, tmp) 101 | if err != nil { 102 | l.Log.Error(err) 103 | } 104 | } 105 | if FindSecrets { 106 | var tmp []string 107 | for secret := range newSecrets { 108 | if !Silent { 109 | fmt.Println(secret) 110 | } 111 | if SecretsOutputFile != "" { 112 | tmp = append(tmp, secret) 113 | } 114 | } 115 | if SecretsOutputFile != "" { 116 | err := SaveResults(SecretsOutputFile, tmp) 117 | if err != nil { 118 | l.Log.Error(err) 119 | } 120 | } 121 | } 122 | 123 | } 124 | 125 | return nil 126 | } 127 | 128 | //Add string to urlVisited, thread safe 129 | func AddUrlVisited(url string) { 130 | lock.Lock() 131 | urlsVisited[url] = true 132 | lock.Unlock() 133 | } 134 | 135 | //is string in urlVisited, thread safe 136 | func IsUrlVisited(url string) bool { 137 | lock.RLock() 138 | if urlsVisited[url] { 139 | lock.RUnlock() 140 | return true 141 | } 142 | lock.RUnlock() 143 | return false 144 | } 145 | 146 | //add string to newSubdomains, thread safe 147 | func AddNewSubdomain(url string) { 148 | lock.Lock() 149 | newSubdomains[url] = true 150 | lock.Unlock() 151 | } 152 | 153 | //is string in newSubdomains, thread safe 154 | func IsNewSubdomain(url string) bool { 155 | lock.RLock() 156 | if !newSubdomains[url] { 157 | lock.RUnlock() 158 | return true 159 | } 160 | lock.RUnlock() 161 | return false 162 | } 163 | 164 | //add string to newSecrets 165 | func AddNewSecret(url string) { 166 | lock.Lock() 167 | newSecrets[url] = true 168 | lock.Unlock() 169 | } 170 | 171 | //is string in newSecrets 172 | func IsNewSecret(url string) bool { 173 | lock.RLock() 174 | if !newSecrets[url] { 175 | lock.RUnlock() 176 | return true 177 | } 178 | lock.RUnlock() 179 | return false 180 | } 181 | -------------------------------------------------------------------------------- /core/proxy.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "errors" 7 | "fmt" 8 | "io/ioutil" 9 | "log" 10 | "net/http" 11 | "net/url" 12 | "os" 13 | "strings" 14 | 15 | l "github.com/ThreatUnkown/jsubfinder/core/logger" 16 | "github.com/elazarl/goproxy" 17 | ) 18 | 19 | var SSHFolder string 20 | var Certificate string 21 | var Key string 22 | var X509pair tls.Certificate 23 | var subDomainlogger *log.Logger 24 | var secretsLogger *log.Logger 25 | var hasScope bool = false 26 | var Scope []string 27 | var inScope bool 28 | 29 | //start the proxy server 30 | func StartProxy(port string, upsteamProxySet bool) (err error) { 31 | if len(Scope) > 0 { 32 | hasScope = true 33 | } 34 | 35 | proxy := goproxy.NewProxyHttpServer() 36 | 37 | //if upstream proxy set, proxy all requests 38 | if upsteamProxySet { 39 | proxy.Tr = &http.Transport{Proxy: func(req *http.Request) (*url.URL, error) { 40 | return url.Parse(UpsteamProxy) 41 | }} 42 | proxy.ConnectDial = proxy.NewConnectDialToProxy(UpsteamProxy) 43 | } 44 | 45 | if Debug { 46 | proxy.Verbose = true 47 | } else { 48 | proxy.Logger = log.New(ioutil.Discard, "", 0) 49 | } 50 | proxy.Logger = log.New(ioutil.Discard, "", 0) 51 | /* 52 | tlsConfig := &tls.Config{ 53 | InsecureSkipVerify: true, 54 | //GetCertificate: returnCert, 55 | } 56 | 57 | X509pair, err = tls.LoadX509KeyPair(Certificate, Key) 58 | if err != nil { 59 | log.Fatalf("Unable to load certificate %s: %v", Certificate, err) 60 | return errors.New(fmt.Sprintf("Unable to load certificate %s: %v", Certificate, err)) 61 | } 62 | tlsConfig.Certificates = append(tlsConfig.Certificates, X509pair) 63 | 64 | // Not strictly required but appears to help with SNI 65 | tlsConfig.BuildNameToCertificate() 66 | 67 | goproxy.MitmConnect.TLSConfig = func(host string, ctx *goproxy.ProxyCtx) (*tls.Config, error) { 68 | return tlsConfig, nil 69 | } 70 | */ 71 | 72 | //always intercepthttp requests 73 | proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) 74 | 75 | proxy.OnResponse().DoFunc(func(r *http.Response, ctx *goproxy.ProxyCtx) *http.Response { 76 | var result JavaScript 77 | 78 | //if there is a scope, check it 79 | if hasScope { 80 | inScope = false 81 | for _, host := range Scope { 82 | if host+":"+r.Request.URL.Port() == r.Request.URL.Host { 83 | inScope = true 84 | break 85 | } 86 | 87 | } 88 | 89 | //if provided url isnt in scope, then return 90 | if !inScope { 91 | l.Log.Debug(r.Request.URL.String() + " not in scope") 92 | return r 93 | } 94 | } 95 | 96 | result.subdomains = make(map[string]bool) 97 | result.secrets = make(map[string]bool) 98 | result.UrlAddr.string = r.Request.URL.String() 99 | 100 | //read request 101 | bodyBytes, err := ioutil.ReadAll(r.Body) 102 | if err != nil { 103 | l.Log.Debug(errors.New("Failed to read body of " + result.UrlAddr.string)) 104 | return nil 105 | } 106 | 107 | r.Body = ioutil.NopCloser(bytes.NewReader([]byte(string(bodyBytes)))) 108 | 109 | result.Content = string(bodyBytes) 110 | if result.Content == "" { //if no content, then there is no JS, return 111 | l.Log.Debug(result.UrlAddr.string + " has no body") 112 | return r 113 | } 114 | 115 | contenType := r.Header.Get("Content-Type") 116 | 117 | //if the header or page contains javascript 118 | if strings.Contains(contenType, "javascript") || strings.Contains(result.Content, "") || strings.Contains(result.Content, "\"script\"") { 120 | if IsUrlVisited(result.UrlAddr.string) { //if the url has been visited return 121 | l.Log.Debug(result.UrlAddr.string + " has already been visited") 122 | return r 123 | } 124 | go func() { //process page 125 | ParseProxyResponse(result) 126 | //time.Sleep(2 * time.Second) 127 | }() 128 | AddUrlVisited(result.UrlAddr.string) 129 | } else { 130 | l.Log.Debug(result.UrlAddr.string + " has no js") 131 | } 132 | return r 133 | }) 134 | 135 | //if outputFile is set, setup the output files 136 | if OutputFile != "" { 137 | f, err := os.OpenFile(OutputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 138 | if err != nil { 139 | return err 140 | } 141 | subDomainlogger = log.New(f, "", 0) 142 | } 143 | 144 | if FindSecrets { 145 | f, err := os.OpenFile(SecretsOutputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 146 | if err != nil { 147 | return err 148 | } 149 | secretsLogger = log.New(f, "", 0) 150 | } 151 | 152 | fmt.Println("Proxy started on", port) 153 | http.ListenAndServe(port, proxy) 154 | 155 | fmt.Println("Proxy stopped") 156 | return nil 157 | } 158 | 159 | //Process requests, print them to console and to file 160 | func ParseProxyResponse(js JavaScript) { 161 | l.Log.Debug("parsing " + js.UrlAddr.string) 162 | err := js.UrlAddr.GetRootDomain() 163 | if err != nil { 164 | l.Log.Debug(err) 165 | return 166 | } 167 | 168 | err = js.GetSubDomains() 169 | if err != nil { 170 | l.Log.Debug(err) 171 | return 172 | } 173 | if FindSecrets { 174 | err := js.GetSecrets() 175 | if err != nil { 176 | l.Log.Debug(err) 177 | return 178 | } 179 | } 180 | for subdomain := range js.subdomains { 181 | if IsNewSubdomain(subdomain) { 182 | if !Silent { 183 | fmt.Println("Subdomain: " + subdomain) 184 | } 185 | AddNewSubdomain(subdomain) 186 | if OutputFile != "" { 187 | subDomainlogger.Output(2, subdomain) 188 | } 189 | } 190 | } 191 | for secret := range js.secrets { 192 | if IsNewSecret(secret) { 193 | if PrintSecrets { 194 | fmt.Println(secret + " of " + js.UrlAddr.string) 195 | } 196 | AddNewSecret(secret) 197 | if OutputFile != "" { 198 | secretsLogger.Output(2, secret) 199 | } 200 | } 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /core/cert.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "crypto/tls" 7 | "crypto/x509" 8 | "crypto/x509/pkix" 9 | "encoding/pem" 10 | "errors" 11 | "math/big" 12 | "net" 13 | "os" 14 | "time" 15 | 16 | "github.com/elazarl/goproxy" 17 | ) 18 | 19 | // Constants used for the SSL Certificates 20 | const ( 21 | bits = 2048 22 | organization = "Jsubfinder" 23 | country = "CA" 24 | province = "ON" 25 | locality = "Toronto" 26 | streetAddress = "1 Not Your Business" 27 | postalCode = "123456" 28 | ) 29 | 30 | //Not used, was testing. Need to figure out how to setup a CA in golang 31 | 32 | //https://github.com/Skamaniak/ssl-decryption/blob/cc35498125e66e0ef770a85e4b9b7a5df582de7a/crypto/spoof.go#L65 33 | //https://github.com/Skamaniak/ssl-decryption/blob/cc35498125e66e0ef770a85e4b9b7a5df582de7a/server/web.go#L132 34 | //https://github.com/projectdiscovery/proxify/blob/0fdaa7d0fc4122d1a2e48d054771c744636b5caf/pkg/certs/ca.go 35 | 36 | // createCertificateAuthority creates a new certificate authority 37 | func CreateAuthority(certPath, keyPath string) error { 38 | priv, err := rsa.GenerateKey(rand.Reader, bits) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | notBefore := time.Now() 44 | notAfter := notBefore.Add(time.Duration(365*24) * time.Hour) 45 | serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 46 | serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | template := x509.Certificate{ 52 | SerialNumber: serialNumber, 53 | Subject: pkix.Name{ 54 | Organization: []string{organization}, 55 | Country: []string{country}, 56 | Province: []string{province}, 57 | Locality: []string{locality}, 58 | StreetAddress: []string{streetAddress}, 59 | PostalCode: []string{postalCode}, 60 | CommonName: organization, 61 | }, 62 | NotBefore: notBefore, 63 | NotAfter: notAfter, 64 | KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, 65 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, 66 | BasicConstraintsValid: true, 67 | IsCA: true, 68 | } 69 | cert, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | keyFile, err := os.Create(keyPath) 75 | if err != nil { 76 | return err 77 | } 78 | defer keyFile.Close() 79 | 80 | certFile, err := os.Create(certPath) 81 | if err != nil { 82 | return err 83 | } 84 | defer certFile.Close() 85 | 86 | if err := pem.Encode(keyFile, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil { 87 | return err 88 | } 89 | return pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: cert}) 90 | } 91 | 92 | func signCertificate(host string) (*tls.Certificate, error) { 93 | x509ca, err := x509.ParseCertificate(X509pair.Leaf.Raw) 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | notBefore := time.Now() 99 | notAfter := notBefore.Add(time.Duration(365*24) * time.Hour) 100 | serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 101 | serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 102 | if err != nil { 103 | return nil, err 104 | } 105 | 106 | template := x509.Certificate{ 107 | SerialNumber: serialNumber, 108 | Issuer: x509ca.Subject, 109 | Subject: pkix.Name{ 110 | Organization: []string{organization}, 111 | Country: []string{country}, 112 | Province: []string{province}, 113 | Locality: []string{locality}, 114 | StreetAddress: []string{streetAddress}, 115 | PostalCode: []string{postalCode}, 116 | CommonName: host, 117 | }, 118 | NotBefore: notBefore, 119 | NotAfter: notAfter, 120 | KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, 121 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, 122 | BasicConstraintsValid: true, 123 | } 124 | 125 | if ip := net.ParseIP(host); ip != nil { 126 | template.IPAddresses = append(template.IPAddresses, ip) 127 | } else { 128 | template.DNSNames = append(template.DNSNames, host) 129 | } 130 | 131 | certpriv, err := rsa.GenerateKey(rand.Reader, bits) 132 | if err != nil { 133 | return nil, err 134 | } 135 | 136 | derBytes, err := x509.CreateCertificate(rand.Reader, &template, x509ca, &certpriv.PublicKey, X509pair.PrivateKey) 137 | if err != nil { 138 | return nil, err 139 | } 140 | return &tls.Certificate{Certificate: [][]byte{derBytes, X509pair.Leaf.Raw}, PrivateKey: certpriv}, nil 141 | } 142 | 143 | func returnCert(helloInfo *tls.ClientHelloInfo) (*tls.Certificate, error) { 144 | return signCertificate(helloInfo.ServerName) 145 | } 146 | 147 | // readCertificateDisk reads a certificate and key file from disk 148 | func ReadCertificateDisk(certFile, keyFile string) error { 149 | goproxyCa, err := tls.LoadX509KeyPair(certFile, keyFile) 150 | if err != nil { 151 | return err 152 | } 153 | goproxyCa.Leaf, err = x509.ParseCertificate(goproxyCa.Certificate[0]) 154 | if err != nil { 155 | return err 156 | } 157 | // Check the expiration. 158 | if time.Now().After(goproxyCa.Leaf.NotAfter) { 159 | return errors.New("Expired Certificate") 160 | } 161 | 162 | goproxy.GoproxyCa = goproxyCa 163 | goproxy.OkConnect = &goproxy.ConnectAction{Action: goproxy.ConnectAccept, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)} 164 | goproxy.MitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectMitm, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)} 165 | goproxy.HTTPMitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectHTTPMitm, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)} 166 | goproxy.RejectConnect = &goproxy.ConnectAction{Action: goproxy.ConnectReject, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)} 167 | 168 | return nil 169 | } 170 | -------------------------------------------------------------------------------- /core/webPage.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "regexp" 9 | "strings" 10 | "sync" 11 | "time" 12 | 13 | l "github.com/ThreatUnkown/jsubfinder/core/logger" 14 | ) 15 | 16 | type WebPage struct { 17 | UrlAddr 18 | Content string //Contant of the webpage 19 | JSFiles []JavaScript 20 | } 21 | 22 | //Get Subdomains and secrets from URL's 23 | func GetResults(url string) (wp WebPage) { 24 | if Debug { 25 | defer TimeTrack(time.Now(), "GetResults "+url) 26 | } 27 | var err error 28 | var contenTypeJS bool = false 29 | 30 | wp.UrlAddr.string = url //set the URL, needed for GetContent() 31 | client := &http.Client{ 32 | Transport: &http.Transport{ 33 | TLSClientConfig: &tls.Config{InsecureSkipVerify: SSL}, 34 | }, 35 | } 36 | 37 | wp.Content, contenTypeJS, err = wp.UrlAddr.GetContent(client) //retrieve the content of the webpage 38 | if err != nil { 39 | l.Log.Debug(err) 40 | return 41 | } 42 | if contenTypeJS { 43 | AddUrlVisited(wp.UrlAddr.string) 44 | } 45 | 46 | err = wp.UrlAddr.GetRootDomain() //Set the The root domain, e.g www.google.com > google.com 47 | if err != nil { 48 | l.Log.Debug(err) 49 | return 50 | } 51 | 52 | if Crawl { 53 | wp.JSFiles, err = wp.GetJSLinks() 54 | if err != nil { 55 | l.Log.Debug(err) 56 | } 57 | } 58 | 59 | //If the base url happens to be a JS via its content-type header, add it to the list 60 | if contenTypeJS { 61 | wp.JSFiles = append(wp.JSFiles, JavaScript{wp.UrlAddr, wp.Content, make(map[string]bool), make(map[string]bool)}) //Add the base url to JSFiles as there may be inline JS 62 | fmt.Println("GetResults content type JS") 63 | } 64 | 65 | if strings.Contains(wp.Content, "") || strings.Contains(wp.Content, "\"script\"") { 66 | wp.JSFiles = append(wp.JSFiles, JavaScript{wp.UrlAddr, wp.Content, make(map[string]bool), make(map[string]bool)}) //Add the base url to JSFiles as there may be inline JS 67 | } else { 68 | l.Log.Debug("no script tags in: " + wp.UrlAddr.string) 69 | } 70 | 71 | //setup go routings 72 | var wg = sync.WaitGroup{} 73 | maxGoroutines := 2 74 | guard := make(chan struct{}, maxGoroutines) 75 | 76 | type result struct { 77 | int 78 | string 79 | } 80 | 81 | results := make(chan result, len(wp.JSFiles)) 82 | 83 | //for each JSFile, get content 84 | for i, js := range wp.JSFiles { 85 | if js.Content == "" { 86 | guard <- struct{}{} 87 | wg.Add(1) 88 | go func(i int, js JavaScript) { 89 | 90 | tmp, contenTypeJS, err := js.GetContent(client) 91 | if err != nil { 92 | l.Log.Debug(err) 93 | } 94 | if contenTypeJS { //if the page was a JS file or has js, add it to urls visited 95 | AddUrlVisited(wp.UrlAddr.string) 96 | } 97 | tmpResult := result{ 98 | int: i, 99 | string: tmp, 100 | } 101 | results <- tmpResult 102 | <-guard 103 | wg.Done() 104 | }(i, js) 105 | } 106 | 107 | } 108 | 109 | wg.Wait() 110 | close(guard) 111 | close(results) 112 | 113 | var i int = 0 114 | for entry := range results { 115 | 116 | wp.JSFiles[entry.int].Content = entry.string 117 | 118 | } 119 | for i = range wp.JSFiles { 120 | if wp.JSFiles[i].Content != "" { 121 | err := wp.JSFiles[i].GetSubDomains() 122 | if err != nil { 123 | l.Log.Error(err) 124 | } 125 | } 126 | } 127 | 128 | if FindSecrets { 129 | for i = range wp.JSFiles { 130 | if wp.JSFiles[i].Content != "" { 131 | err := wp.JSFiles[i].GetSecrets() 132 | if err != nil { 133 | l.Log.Debug(err) 134 | } 135 | } 136 | } 137 | } 138 | 139 | return wp 140 | } 141 | 142 | //GetJSLinks retrieves the links to JS files from the content of the url 143 | func (wp *WebPage) GetJSLinks() (JSFile []JavaScript, err error) { 144 | var results [][]string 145 | 146 | if Debug { 147 | defer TimeTrack(time.Now(), "GetJSLinks "+wp.string) 148 | } 149 | 150 | //([-a-zA-Z0-9@:%._/\+~#=]{1,256}\.js)\b 151 | //jsRegex, err := regexp.Compile("src\\s?=\\s?\"(.*.js)\"") 152 | jsRegex, err := regexp.Compile("\\s?=\\s?\"([-a-z0-9\\/@:%.-_\\+~#=]+.js)\"") 153 | if err != nil { 154 | log.Fatal(err) 155 | } 156 | GreedyRegex, err := regexp.Compile("\\s?=\\s?\"([-a-z0-9\\/@:%.-_\\+~#=]+)\"") //shold prob just remove this tbh 157 | if err != nil { 158 | log.Fatal(err) 159 | } 160 | 161 | if !Greedy { 162 | results = jsRegex.FindAllStringSubmatch(wp.Content, -1) 163 | } else { 164 | results = GreedyRegex.FindAllStringSubmatch(wp.Content, -1) 165 | } 166 | 167 | for _, result := range results { 168 | 169 | var protocol string 170 | if result[1] != "" { 171 | if strings.HasPrefix(result[1], "http://") || strings.HasPrefix(result[1], "https://") { 172 | if IsUrlVisited(result[1]) { 173 | continue 174 | } 175 | JSFile = append(JSFile, JavaScript{UrlAddr{result[1], wp.UrlAddr.rootDomain}, "", make(map[string]bool), make(map[string]bool)}) 176 | } else if strings.HasPrefix(result[1], "//") { 177 | protocol, err = GetHTTprotocol(wp.UrlAddr.string) //assumption that the JS file will be hosted on same protocol as web server 178 | if err != nil { 179 | return 180 | } 181 | link := result[1] 182 | link = protocol + link[2:] 183 | if IsUrlVisited(link) { 184 | continue 185 | } 186 | JSFile = append(JSFile, JavaScript{UrlAddr{link, wp.UrlAddr.rootDomain}, "", make(map[string]bool), make(map[string]bool)}) 187 | } else { 188 | protocol, err = GetHTTprotocol(wp.UrlAddr.string) //assumption that the JS file will be hosted on same protocol as web server 189 | if err != nil { 190 | return 191 | } 192 | link := strings.Replace(wp.UrlAddr.string, protocol, "", 1) + "/" + result[1] 193 | link = protocol + strings.Replace(link, "//", "/", -1) 194 | if IsUrlVisited(link) { 195 | continue 196 | } 197 | JSFile = append(JSFile, JavaScript{UrlAddr{link, wp.UrlAddr.rootDomain}, "", make(map[string]bool), make(map[string]bool)}) 198 | } 199 | //JSFile append 200 | } 201 | } 202 | //time.Sleep(300) 203 | return 204 | } 205 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## ![jsubfinder logo](https://user-images.githubusercontent.com/17349277/146734055-8b836305-7a13-4c66-a02b-d92932322b42.png) 2 | 3 | 4 | 5 | 6 | JSubFinder is a tool writtin in golang to search webpages & javascript for hidden subdomains and secrets in the given URL. Developed with BugBounty hunters in mind JSubFinder takes advantage of Go's amazing performance allowing it to utilize large data sets & be easily chained with other tools. 7 | 8 | 9 | ![z69D8q](https://user-images.githubusercontent.com/17349277/147615346-9c1471a6-a9a8-45cb-a429-f789b255950c.gif) 10 | 11 | ## Install 12 | --- 13 | Install the application and download the signatures needed to find secrets 14 | 15 | Using GO: 16 | 17 | ```bash 18 | go install github.com/ThreatUnkown/jsubfinder@latest 19 | wget https://raw.githubusercontent.com/ThreatUnkown/jsubfinder/master/.jsf_signatures.yaml && mv .jsf_signatures.yaml ~/.jsf_signatures.yaml 20 | ``` 21 | 22 | or 23 | 24 | [Downloads Page](https://github.com/hiddengearz/jsubfinder/tags) 25 | 26 | 27 | ## Basic Usage 28 | --- 29 | 30 | ### Search 31 | 32 | Search the given url's for subdomains and secrets 33 | 34 | ```text 35 | $ jsubfinder search -h 36 | 37 | Execute the command specified 38 | 39 | Usage: 40 | JSubFinder search [flags] 41 | 42 | Flags: 43 | -c, --crawl Enable crawling 44 | -g, --greedy Check all files for URL's not just Javascript 45 | -h, --help help for search 46 | -f, --inputFile string File containing domains 47 | -t, --threads int Ammount of threads to be used (default 5) 48 | -u, --url strings Url to check 49 | 50 | Global Flags: 51 | -d, --debug Enable debug mode. Logs are stored in log.info 52 | -K, --nossl Skip SSL cert verification (default true) 53 | -o, --outputFile string name/location to store the file 54 | -s, --secrets Check results for secrets e.g api keys 55 | --sig string Location of signatures for finding secrets 56 | -S, --silent Disable printing to the console 57 | ``` 58 | 59 | Examples (results are the same in this case): 60 | 61 | ```bash 62 | $ jsubfinder search -u www.google.com 63 | $ jsubfinder search -f file.txt 64 | $ echo www.google.com | jsubfinder search 65 | $ echo www.google.com | httpx --silent | jsubfinder search$ 66 | 67 | apis.google.com 68 | ogs.google.com 69 | store.google.com 70 | mail.google.com 71 | accounts.google.com 72 | www.google.com 73 | policies.google.com 74 | support.google.com 75 | adservice.google.com 76 | play.google.com 77 | ``` 78 | 79 | 80 | 81 | #### With Secrets Enabled 82 | *note `--secrets=""` will save the secret results in a secrets.txt file* 83 | ```bash 84 | 85 | $ echo www.youtube.com | jsubfinder search --secrets="" 86 | www.youtube.com 87 | youtubei.youtube.com 88 | payments.youtube.com 89 | 2Fwww.youtube.com 90 | 252Fwww.youtube.com 91 | m.youtube.com 92 | tv.youtube.com 93 | music.youtube.com 94 | creatoracademy.youtube.com 95 | artists.youtube.com 96 | 97 | Google Cloud API Key found in content of https://www.youtube.com 98 | Google Cloud API Key found in content of https://www.youtube.com 99 | Google Cloud API Key found in content of https://www.youtube.com 100 | Google Cloud API Key found in content of https://www.youtube.com 101 | Google Cloud API Key found in content of https://www.youtube.com 102 | Google Cloud API Key found in content of https://www.youtube.com 103 | ``` 104 | 105 | 106 | #### Advanced examples 107 | ```bash 108 | $ echo www.google.com | jsubfinder search -crawl -s "google_secrets.txt" -S -o jsf_google.txt -t 10 -g 109 | ``` 110 | 111 | * `-crawl` use the default crawler to crawl pages for other URL's to analyze 112 | * `-s` enables JSubFinder to search for secrets 113 | * `-S` Silence output to console 114 | * `-o ` save output to specified file 115 | * `-t 10` use 10 threads 116 | * `-g` search every URL for JS, even ones we don't think have any 117 | 118 | ### Proxy 119 | Enables the upstream HTTP proxy with TLS MITM sypport. This allows you to: 120 | 121 | 1) Browse sites in realtime and have JSubFinder search for subdomains and secrets real time. 122 | 2) If needed run jsubfinder on another server to offload the workload 123 | 124 | ```text 125 | $ JSubFinder proxy -h 126 | 127 | Execute the command specified 128 | 129 | Usage: 130 | JSubFinder proxy [flags] 131 | 132 | Flags: 133 | -h, --help help for proxy 134 | -p, --port int Port for the proxy to listen on (default 8444) 135 | --scope strings Url's in scope seperated by commas. e.g www.google.com,www.netflix.com 136 | -u, --upstream-proxy string Adress of upsteam proxy e.g http://127.0.0.1:8888 (default "http://127.0.0.1:8888") 137 | 138 | Global Flags: 139 | -d, --debug Enable debug mode. Logs are stored in log.info 140 | -K, --nossl Skip SSL cert verification (default true) 141 | -o, --outputFile string name/location to store the file 142 | -s, --secrets Check results for secrets e.g api keys 143 | --sig string Location of signatures for finding secrets 144 | -S, --silent Disable printing to the console 145 | ``` 146 | 147 | ```bash 148 | $ jsubfinder proxy 149 | Proxy started on :8444 150 | Subdomain: out.reddit.com 151 | Subdomain: www.reddit.com 152 | Subdomain: 2Fwww.reddit.com 153 | Subdomain: alb.reddit.com 154 | Subdomain: about.reddit.com 155 | ``` 156 | 157 | #### With Burp Suite 158 | 1) Configure Burp Suite to forward traffic to an upstream proxy/ (User Options > Connections > Upsteam Proxy Servers > Add) 159 | 2) Run JSubFinder in proxy mode 160 | 161 | Burp Suite will now forward all traffic proxied through it to JSubFinder. JSubFinder will retrieve the response, return it to burp and in another thread search for subdomains and secrets. 162 | 163 | #### With Proxify 164 | 1) Launch [Proxify](https://github.com/projectdiscovery/proxify) & dump traffic to a folder `proxify -output logs` 165 | 2) Configure Burp Suite, a Browser or other tool to forward traffic to Proxify (see instructions on their [github page](https://github.com/projectdiscovery/proxify)) 166 | 3) Launch JSubFinder in proxy mode & set the upstream proxy as Proxify `jsubfinder proxy -u http://127.0.0.1:8443` 167 | 4) Use Proxify's replay utility to replay the dumped traffic to jsubfinder `replay -output logs -burp-addr http://127.0.0.1:8444` 168 | 169 | 170 | #### Run on another server 171 | Simple, run JSubFinder in proxy mode on another server e.g 192.168.1.2. Follow the proxy steps above but set your applications upstream proxy as 192.168.1.2:8443 172 | 173 | #### Advanced Examples 174 | 175 | ```bash 176 | $ jsubfinder proxy --scope www.reddit.com -p 8081 -S -o jsf_reddit.txt 177 | ``` 178 | 179 | * `--scope` limits JSubFinder to only analyze responses from www.reddit.com 180 | * `-p` port JSubFinders proxy server is running on 181 | * `-S` silence output to the console/stdout 182 | * `-o ` output examples to this file 183 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 2 | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 3 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= 8 | github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= 9 | github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= 10 | github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= 11 | github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= 12 | github.com/elazarl/goproxy/ext v0.0.0-20220403042543-a53172b9392e h1:e/Wg7FbjBKSeXutwi1qdjms9pfTK7eLKo0I+yp5Wsmw= 13 | github.com/elazarl/goproxy/ext v0.0.0-20220403042543-a53172b9392e/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= 14 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 15 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 16 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 17 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 18 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 19 | github.com/jpillora/go-tld v1.1.1 h1:P1ZwtKDHBYYUl235R/D64cdBARfGYzEy1Hg2Ikir3FQ= 20 | github.com/jpillora/go-tld v1.1.1/go.mod h1:kitBxOF//DR5FxYeIGw+etdiiTIq5S7bx0dwy1GUNAk= 21 | github.com/jpillora/go-tld v1.2.1 h1:kDKOkmXLlskqjcvNs7w5XHLep7c8WM7Xd4HQjxllVMk= 22 | github.com/jpillora/go-tld v1.2.1/go.mod h1:plzIl7xr5UWKGy7R+giuv+L/nOjrPjsoWxy/ST9OBUk= 23 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 24 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 25 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 26 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 27 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 28 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 29 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 30 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 31 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 32 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 33 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 34 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 35 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 36 | github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= 37 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 38 | github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= 39 | github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= 40 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 41 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 42 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 43 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 44 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 45 | github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= 46 | github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= 47 | github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= 48 | github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 49 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 50 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 51 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 52 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 53 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 54 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 55 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 56 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 57 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 58 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 59 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 60 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 61 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 62 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 63 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 64 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 65 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 66 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 67 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 68 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 69 | golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= 70 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 71 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 72 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 73 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 74 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 75 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 76 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 77 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 78 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 79 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 80 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 81 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 82 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 83 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 84 | golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= 85 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 86 | golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= 87 | golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= 88 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 89 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 90 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 91 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 92 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 93 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 94 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 95 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 96 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 97 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 98 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 99 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 100 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 101 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 102 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 103 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 104 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 105 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 106 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 107 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 108 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= 109 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 110 | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= 111 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 112 | golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 113 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 114 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 115 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 116 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 117 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 118 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 119 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 120 | golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= 121 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 122 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 123 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 124 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 125 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 126 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 127 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 128 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 129 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 130 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 131 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 132 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 133 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 134 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 135 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 136 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 137 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 138 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 139 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 140 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 141 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 142 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 143 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 144 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 145 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 146 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 147 | -------------------------------------------------------------------------------- /.jsf_signatures.yaml: -------------------------------------------------------------------------------- 1 | signatures: 2 | - part: 'extension' 3 | match: '.pem' 4 | name: 'Potential cryptographic private key' 5 | - part: 'extension' 6 | match: '.log' 7 | name: 'Log file' 8 | - part: 'extension' 9 | match: '.pkcs12' 10 | name: 'Potential cryptographic key bundle' 11 | - part: 'extension' 12 | match: '.p12' 13 | name: 'Potential cryptographic key bundle' 14 | - part: 'extension' 15 | match: '.pfx' 16 | name: 'Potential cryptographic key bundle' 17 | - part: 'extension' 18 | match: '.asc' 19 | name: 'Potential cryptographic key bundle' 20 | - part: 'filename' 21 | match: 'otr.private_key' 22 | name: 'Pidgin OTR private key' 23 | - part: 'extension' 24 | match: '.ovpn' 25 | name: 'OpenVPN client configuration file' 26 | - part: 'extension' 27 | match: '.cscfg' 28 | name: 'Azure service configuration schema file' 29 | - part: 'extension' 30 | match: '.rdp' 31 | name: 'Remote Desktop connection file' 32 | - part: 'extension' 33 | match: '.mdf' 34 | name: 'Microsoft SQL database file' 35 | - part: 'extension' 36 | match: '.sdf' 37 | name: 'Microsoft SQL server compact database file' 38 | - part: 'extension' 39 | match: '.sqlite' 40 | name: 'SQLite database file' 41 | - part: 'extension' 42 | match: '.sqlite3' 43 | name: 'SQLite3 database file' 44 | - part: 'extension' 45 | match: '.bek' 46 | name: 'Microsoft BitLocker recovery key file' 47 | - part: 'extension' 48 | match: '.tpm' 49 | name: 'Microsoft BitLocker Trusted Platform Module password file' 50 | - part: 'extension' 51 | match: '.fve' 52 | name: 'Windows BitLocker full volume encrypted data file' 53 | - part: 'extension' 54 | match: '.jks' 55 | name: 'Java keystore file' 56 | - part: 'extension' 57 | match: '.psafe3' 58 | name: 'Password Safe database file' 59 | - part: 'filename' 60 | match: 'secret_token.rb' 61 | name: 'Ruby On Rails secret token configuration file' 62 | - part: 'filename' 63 | match: 'carrierwave.rb' 64 | name: 'Carrierwave configuration file' 65 | - part: 'filename' 66 | match: 'database.yml' 67 | name: 'Potential Ruby On Rails database configuration file' 68 | - part: 'filename' 69 | match: 'omniauth.rb' 70 | name: 'OmniAuth configuration file' 71 | - part: 'filename' 72 | match: 'settings.py' 73 | name: 'Django configuration file' 74 | - part: 'extension' 75 | match: '.agilekeychain' 76 | name: '1Password password manager database file' 77 | - part: 'extension' 78 | match: '.keychain' 79 | name: 'Apple Keychain database file' 80 | - part: 'extension' 81 | match: '.pcap' 82 | name: 'Network traffic capture file' 83 | - part: 'extension' 84 | match: '.gnucash' 85 | name: 'GnuCash database file' 86 | - part: 'filename' 87 | match: 'jenkins.plugins.publish_over_ssh.BapSshPublisherPlugin.xml' 88 | name: 'Jenkins publish over SSH plugin file' 89 | - part: 'filename' 90 | match: 'credentials.xml' 91 | name: 'Potential Jenkins credentials file' 92 | - part: 'extension' 93 | match: '.kwallet' 94 | name: 'KDE Wallet Manager database file' 95 | - part: 'filename' 96 | match: 'LocalSettings.php' 97 | name: 'Potential MediaWiki configuration file' 98 | - part: 'extension' 99 | match: '.tblk' 100 | name: 'Tunnelblick VPN configuration file' 101 | - part: 'filename' 102 | match: 'Favorites.plist' 103 | name: 'Sequel Pro MySQL database manager bookmark file' 104 | - part: 'filename' 105 | match: 'configuration.user.xpl' 106 | name: 'Little Snitch firewall configuration file' 107 | - part: 'extension' 108 | match: '.dayone' 109 | name: 'Day One journal file' 110 | - part: 'filename' 111 | match: 'journal.txt' 112 | name: 'Potential jrnl journal file' 113 | - part: 'filename' 114 | match: 'knife.rb' 115 | name: 'Chef Knife configuration file' 116 | - part: 'filename' 117 | match: 'proftpdpasswd' 118 | name: 'cPanel backup ProFTPd credentials file' 119 | - part: 'filename' 120 | match: 'robomongo.json' 121 | name: 'Robomongo MongoDB manager configuration file' 122 | - part: 'filename' 123 | match: 'filezilla.xml' 124 | name: 'FileZilla FTP configuration file' 125 | - part: 'filename' 126 | match: 'recentservers.xml' 127 | name: 'FileZilla FTP recent servers file' 128 | - part: 'filename' 129 | match: 'ventrilo_srv.ini' 130 | name: 'Ventrilo server configuration file' 131 | - part: 'filename' 132 | match: 'terraform.tfvars' 133 | name: 'Terraform variable config file' 134 | - part: 'filename' 135 | match: '.exports' 136 | name: 'Shell configuration file' 137 | - part: 'filename' 138 | match: '.functions' 139 | name: 'Shell configuration file' 140 | - part: 'filename' 141 | match: '.extra' 142 | name: 'Shell configuration file' 143 | - part: 'filename' 144 | regex: '^.*_rsa$' 145 | name: 'Private SSH key' 146 | - part: 'filename' 147 | regex: '^.*_dsa$' 148 | name: 'Private SSH key' 149 | - part: 'filename' 150 | regex: '^.*_ed25519$' 151 | name: 'Private SSH key' 152 | - part: 'filename' 153 | regex: '^.*_ecdsa$' 154 | name: 'Private SSH key' 155 | - part: 'path' 156 | regex: '\.?ssh/config$' 157 | name: 'SSH configuration file' 158 | - part: 'extension' 159 | regex: '^key(pair)?$' 160 | name: 'Potential cryptographic private key' 161 | - part: 'filename' 162 | regex: '^\.?(bash_|zsh_|sh_|z)?history$' 163 | name: 'Shell command history file' 164 | - part: 'filename' 165 | regex: '^\.?mysql_history$' 166 | name: 'MySQL client command history file' 167 | - part: 'filename' 168 | regex: '^\.?psql_history$' 169 | name: 'PostgreSQL client command history file' 170 | - part: 'filename' 171 | regex: '^\.?pgpass$' 172 | name: 'PostgreSQL password file' 173 | - part: 'filename' 174 | regex: '^\.?irb_history$' 175 | name: 'Ruby IRB console history file' 176 | - part: 'path' 177 | regex: '\.?purple/accounts\.xml$' 178 | name: 'Pidgin chat client account configuration file' 179 | - part: 'path' 180 | regex: '\.?xchat2?/servlist_?\.conf$' 181 | name: 'Hexchat/XChat IRC client server list configuration file' 182 | - part: 'path' 183 | regex: '\.?irssi/config$' 184 | name: 'Irssi IRC client configuration file' 185 | - part: 'path' 186 | regex: '\.?recon-ng/keys\.db$' 187 | name: 'Recon-ng web reconnaissance framework API key database' 188 | - part: 'filename' 189 | regex: '^\.?dbeaver-data-sources.xml$' 190 | name: 'DBeaver SQL database manager configuration file' 191 | - part: 'filename' 192 | regex: '^\.?muttrc$' 193 | name: 'Mutt e-mail client configuration file' 194 | - part: 'filename' 195 | regex: '^\.?s3cfg$' 196 | name: 'S3cmd configuration file' 197 | - part: 'path' 198 | regex: '\.?aws/credentials$' 199 | name: 'AWS CLI credentials file' 200 | - part: 'filename' 201 | regex: '^sftp-config(\.json)?$' 202 | name: 'SFTP connection configuration file' 203 | - part: 'filename' 204 | regex: '^\.?trc$' 205 | name: 'T command-line Twitter client configuration file' 206 | - part: 'filename' 207 | regex: '^\.?(bash|zsh|csh)rc$' 208 | name: 'Shell configuration file' 209 | - part: 'filename' 210 | regex: '^\.?(bash_|zsh_)?profile$' 211 | name: 'Shell profile configuration file' 212 | - part: 'filename' 213 | regex: '^\.?(bash_|zsh_)?aliases$' 214 | name: 'Shell command alias configuration file' 215 | - part: 'filename' 216 | regex: 'config(\.inc)?\.php$' 217 | name: 'PHP configuration file' 218 | - part: 'extension' 219 | regex: '^key(store|ring)$' 220 | name: 'GNOME Keyring database file' 221 | - part: 'extension' 222 | regex: '^kdbx?$' 223 | name: 'KeePass password manager database file' 224 | - part: 'extension' 225 | regex: '^sql(dump)?$' 226 | name: 'SQL dump file' 227 | - part: 'filename' 228 | regex: '^\.?htpasswd$' 229 | name: 'Apache htpasswd file' 230 | - part: 'filename' 231 | regex: '^(\.|_)?netrc$' 232 | name: 'Configuration file for auto-login process' 233 | - part: 'path' 234 | regex: '\.?gem/credentials$' 235 | name: 'Rubygems credentials file' 236 | - part: 'filename' 237 | regex: '^\.?tugboat$' 238 | name: 'Tugboat DigitalOcean management tool configuration' 239 | - part: 'path' 240 | regex: 'doctl/config.yaml$' 241 | name: 'DigitalOcean doctl command-line client configuration file' 242 | - part: 'filename' 243 | regex: '^\.?git-credentials$' 244 | name: 'git-credential-store helper credentials file' 245 | - part: 'path' 246 | regex: 'config/hub$' 247 | name: 'GitHub Hub command-line client configuration file' 248 | - part: 'filename' 249 | regex: '^\.?gitconfig$' 250 | name: 'Git configuration file' 251 | - part: 'path' 252 | regex: '\.?chef/(.*)\.pem$' 253 | name: 'Chef private key' 254 | - part: 'path' 255 | regex: 'etc/shadow$' 256 | name: 'Potential Linux shadow file' 257 | - part: 'path' 258 | regex: 'etc/passwd$' 259 | name: 'Potential Linux passwd file' 260 | comment: 'Contains system user information' 261 | - part: 'filename' 262 | regex: '^\.?dockercfg$' 263 | name: 'Docker configuration file' 264 | - part: 'filename' 265 | regex: '^\.?npmrc$' 266 | name: 'NPM configuration file' 267 | - part: 'filename' 268 | regex: '^\.?env$' 269 | name: 'Environment configuration file' 270 | - part: 'contents' 271 | regex: '(A3T[A-Z0-9]|AKIA|AGPA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}' 272 | name: 'AWS Access Key ID Value' 273 | - part: 'contents' 274 | regex: "((\\\"|'|`)?((?i)aws)?_?((?i)access)_?((?i)key)?_?((?i)id)?(\\\"|'|`)?\\\\s{0,50}(:|=>|=)\\\\s{0,50}(\\\"|'|`)?(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}(\\\"|'|`)?)" 275 | name: 'AWS Access Key ID' 276 | - part: 'contents' 277 | regex: "((\\\"|'|`)?((?i)aws)?_?((?i)account)_?((?i)id)?(\\\"|'|`)?\\\\s{0,50}(:|=>|=)\\\\s{0,50}(\\\"|'|`)?[0-9]{4}-?[0-9]{4}-?[0-9]{4}(\\\"|'|`)?)" 278 | name: 'AWS Account ID' 279 | - part: 'contents' 280 | regex: "((\\\"|'|`)?((?i)aws)?_?((?i)secret)_?((?i)access)?_?((?i)key)?_?((?i)id)?(\\\"|'|`)?\\\\s{0,50}(:|=>|=)\\\\s{0,50}(\\\"|'|`)?[A-Za-z0-9/+=]{40}(\\\"|'|`)?)" 281 | name: 'AWS Secret Access Key' 282 | - part: 'contents' 283 | regex: "((\\\"|'|`)?((?i)aws)?_?((?i)session)?_?((?i)token)?(\\\"|'|`)?\\\\s{0,50}(:|=>|=)\\\\s{0,50}(\\\"|'|`)?[A-Za-z0-9/+=]{16,}(\\\"|'|`)?)" 284 | name: 'AWS Session Token' 285 | - part: 'contents' 286 | regex: "(?i)artifactory.{0,50}(\\\"|'|`)?[a-zA-Z0-9=]{112}(\\\"|'|`)?" 287 | name: 'Artifactory' 288 | - part: 'contents' 289 | regex: "(?i)codeclima.{0,50}(\\\"|'|`)?[0-9a-f]{64}(\\\"|'|`)?" 290 | name: 'CodeClimate' 291 | - part: 'contents' 292 | regex: 'EAACEdEose0cBA[0-9A-Za-z]+' 293 | name: 'Facebook access token' 294 | - part: 'contents' 295 | regex: "((\\\"|'|`)?type(\\\"|'|`)?\\\\s{0,50}(:|=>|=)\\\\s{0,50}(\\\"|'|`)?service_account(\\\"|'|`)?,?)" 296 | name: 'Google (GCM) Service account' 297 | - part: 'contents' 298 | regex: '(?:r|s)k_[live|test]_[0-9a-zA-Z]{24}' 299 | name: 'Stripe API key' 300 | - part: 'contents' 301 | regex: '[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com' 302 | name: 'Google OAuth Key' 303 | - part: 'contents' 304 | regex: 'AIza[0-9A-Za-z\\-_]{35}' 305 | name: 'Google Cloud API Key' 306 | - part: 'contents' 307 | regex: 'ya29\\.[0-9A-Za-z\\-_]+' 308 | name: 'Google OAuth Access Token' 309 | - part: 'contents' 310 | regex: 'sk_[live|test]_[0-9a-z]{32}' 311 | name: 'Picatic API key' 312 | - part: 'contents' 313 | regex: 'sq0atp-[0-9A-Za-z\-_]{22}' 314 | name: 'Square Access Token' 315 | - part: 'contents' 316 | regex: 'sq0csp-[0-9A-Za-z\-_]{43}' 317 | name: 'Square OAuth Secret' 318 | - part: 'contents' 319 | regex: 'access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}' 320 | name: 'PayPal/Braintree Access Token' 321 | - part: 'contents' 322 | regex: 'amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' 323 | name: 'Amazon MWS Auth Token' 324 | - part: 'contents' 325 | regex: 'SK[0-9a-fA-F]{32}' 326 | name: 'Twilo API Key' 327 | - part: 'contents' 328 | regex: 'SG\.[0-9A-Za-z\-_]{22}\.[0-9A-Za-z\-_]{43}' 329 | name: 'SendGrid API Key' 330 | - part: 'contents' 331 | regex: 'key-[0-9a-zA-Z]{32}' 332 | name: 'MailGun API Key' 333 | - part: 'contents' 334 | regex: '[0-9a-f]{32}-us[0-9]{12}' 335 | name: 'MailChimp API Key' 336 | - part: 'contents' 337 | regex: "sshpass -p.*['|\\\"]" 338 | name: 'SSH Password' 339 | - part: 'contents' 340 | regex: '(https\\://outlook\\.office.com/webhook/[0-9a-f-]{36}\\@)' 341 | name: 'Outlook team' 342 | - part: 'contents' 343 | regex: "(?i)sauce.{0,50}(\\\"|'|`)?[0-9a-f-]{36}(\\\"|'|`)?" 344 | name: 'Sauce Token' 345 | - part: 'contents' 346 | regex: '(xox[pboa]-[0-9]{12}-[0-9]{12}-[0-9]{12}-[a-z0-9]{32})' 347 | name: 'Slack Token' 348 | - part: 'contents' 349 | regex: 'https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}' 350 | name: 'Slack Webhook' 351 | - part: 'contents' 352 | regex: "(?i)sonar.{0,50}(\\\"|'|`)?[0-9a-f]{40}(\\\"|'|`)?" 353 | name: 'SonarQube Docs API Key' 354 | - part: 'contents' 355 | regex: "(?i)hockey.{0,50}(\\\"|'|`)?[0-9a-f]{32}(\\\"|'|`)?" 356 | name: 'HockeyApp' 357 | - part: 'contents' 358 | regex: '([\w+]{1,24})(://)([^$<]{1})([^\s";]{1,}):([^$<]{1})([^\s";/]{1,})@[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,24}([^\s]+)' 359 | name: 'Username and password in URI' 360 | - part: 'contents' 361 | regex: 'oy2[a-z0-9]{43}' 362 | name: 'NuGet API Key' 363 | - part: 'contents' 364 | regex: 'hawk\.[0-9A-Za-z\-_]{20}\.[0-9A-Za-z\-_]{20}' 365 | name: 'StackHawk API Key' 366 | - part: 'extension' 367 | match: '.ppk' 368 | name: 'Potential PuTTYgen private key' 369 | - part: 'filename' 370 | match: 'heroku.json' 371 | name: 'Heroku config file' 372 | - part: 'extension' 373 | match: '.sqldump' 374 | name: 'SQL Data dump file' 375 | - part: 'filename' 376 | match: 'dump.sql' 377 | name: 'MySQL dump w/ bcrypt hashes' 378 | - part: 'filename' 379 | match: 'id_rsa_pub' 380 | name: 'Public ssh key' 381 | - part: 'filename' 382 | match: 'mongoid.yml' 383 | name: 'Mongoid config file' 384 | - part: 'filename' 385 | match: 'salesforce.js' 386 | name: 'Salesforce credentials in a nodejs project' 387 | - part: 'extension' 388 | match: '.netrc' 389 | name: 'netrc with SMTP credentials' 390 | - part: 'filename' 391 | regex: '.remote-sync.json$' 392 | name: 'Created by remote-sync for Atom, contains FTP and/or SCP/SFTP/SSH server details and credentials' 393 | - part: 'filename' 394 | regex: '.esmtprc$' 395 | name: 'esmtp configuration' 396 | - part: 'filename' 397 | regex: '^deployment-config.json?$' 398 | name: 'Created by sftp-deployment for Atom, contains server details and credentials' 399 | - part: 'filename' 400 | regex: '.ftpconfig$' 401 | name: 'Created by sftp-deployment for Atom, contains server details and credentials' 402 | - part: 'contents' 403 | regex: '-----BEGIN (EC|RSA|DSA|OPENSSH|PGP) PRIVATE KEY' 404 | name: 'Contains a private key' 405 | - part: 'contents' 406 | regex: 'define(.{0,20})?(DB_CHARSET|NONCE_SALT|LOGGED_IN_SALT|AUTH_SALT|NONCE_KEY|DB_HOST|DB_PASSWORD|AUTH_KEY|SECURE_AUTH_KEY|LOGGED_IN_KEY|DB_NAME|DB_USER)(.{0,20})?[''|"].{10,120}[''|"]' 407 | name: 'WP-Config' 408 | - part: 'contents' 409 | regex: '(?i)(aws_access_key_id|aws_secret_access_key)(.{0,20})?=.[0-9a-zA-Z\/+]{20,40}' 410 | name: 'AWS cred file info' 411 | - part: 'contents' 412 | regex: '(?i)(facebook|fb)(.{0,20})?(?-i)[''\"][0-9a-f]{32}[''\"]' 413 | name: 'Facebook Secret Key' 414 | - part: 'contents' 415 | regex: '(?i)(facebook|fb)(.{0,20})?[''\"][0-9]{13,17}[''\"]' 416 | name: 'Facebook Client ID' 417 | - part: 'contents' 418 | regex: '(?i)twitter(.{0,20})?[''\"][0-9a-z]{35,44}[''\"]' 419 | name: 'Twitter Secret Key' 420 | - part: 'contents' 421 | regex: '(?i)twitter(.{0,20})?[''\"][0-9a-z]{18,25}[''\"]' 422 | name: 'Twitter Client ID' 423 | - part: 'contents' 424 | regex: '(?i)github(.{0,20})?(?-i)[''\"][0-9a-zA-Z]{35,40}[''\"]' 425 | name: 'Github Key' 426 | - part: 'contents' 427 | regex: '(?i)heroku(.{0,20})?[''"][0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}[''"]' 428 | name: 'Heroku API key' 429 | - part: 'contents' 430 | regex: '(?i)linkedin(.{0,20})?(?-i)[''\"][0-9a-z]{12}[''\"]' 431 | name: 'Linkedin Client ID' 432 | - part: 'contents' 433 | regex: '(?i)linkedin(.{0,20})?[''\"][0-9a-z]{16}[''\"]' 434 | name: 'LinkedIn Secret Key' 435 | - part: 'path' 436 | regex: '\.?idea[\\\/]WebServers.xml$' 437 | name: 'Created by Jetbrains IDEs, contains webserver credentials with encoded passwords (not encrypted!)' 438 | - part: 'path' 439 | regex: '\.?vscode[\\\/]sftp.json$' 440 | name: 'Created by vscode-sftp for VSCode, contains SFTP/SSH server details and credentials' 441 | - part: 'path' 442 | regex: 'web[\\\/]ruby[\\\/]secrets.yml' 443 | name: 'Ruby on rails secrets.yml file (contains passwords)' 444 | - part: 'path' 445 | regex: '\.?docker[\\\/]config.json$' 446 | name: 'Docker registry authentication file' 447 | - part: 'path' 448 | regex: 'ruby[\\\/]config[\\\/]master.key$' 449 | name: 'Rails master key (used for decrypting credentials.yml.enc for Rails 5.2+)' 450 | - part: 'path' 451 | regex: '\.?mozilla[\\\/]firefox[\\\/]logins.json$' 452 | name: 'Firefox saved password collection (can be decrypted using keys4.db)' 453 | --------------------------------------------------------------------------------