├── .dockerignore ├── .github ├── FUNDING.yml └── workflows │ └── codeql.yml ├── .gitignore ├── .goreleaser.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── cert.go ├── ip.go ├── ipc.go ├── net.go ├── related.go ├── root.go └── scan.go ├── core ├── chrome.go ├── common.go ├── filter.go ├── helper.go ├── options.go ├── request.go └── version.go ├── go.mod ├── go.sum ├── main.go └── modules ├── asn.go ├── cert.go ├── cert_test.go ├── ga.go ├── ip.go ├── netblock.go ├── netblock_test.go ├── nmap.go ├── nmap_test.go ├── related.go ├── scan.go ├── scan_test.go └── static └── ip2asn-combined.tsv.gz /.dockerignore: -------------------------------------------------------------------------------- 1 | .github/* 2 | .gitignore 3 | .goreleaser.yml 4 | LICENSE 5 | README.md 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: j3ssie 5 | open_collective: osmedeus 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: [ 'https://paypal.me/j3ssiejjj', 'https://www.buymeacoffee.com/j3ssie' ] -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | # on: push 15 | 16 | jobs: 17 | analyze: 18 | name: Analyze 19 | # Runner size impacts CodeQL analysis time. To learn more, please see: 20 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 21 | # - https://gh.io/supported-runners-and-hardware-resources 22 | # - https://gh.io/using-larger-runners 23 | # Consider using larger runners for possible analysis time improvements. 24 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 25 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 26 | permissions: 27 | actions: read 28 | contents: read 29 | security-events: write 30 | 31 | strategy: 32 | fail-fast: false 33 | matrix: 34 | language: [ 'go' ] 35 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ] 36 | # Use only 'java' to analyze code written in Java, Kotlin or both 37 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 38 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v3 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v2 47 | with: 48 | languages: ${{ matrix.language }} 49 | # config: ${{ vars.CODEQL_CONF }} # better put the config as another YAML file 50 | config: | 51 | disable-default-queries: true 52 | queries: 53 | - uses: security-extended 54 | query-filters: 55 | - exclude: 56 | problem.severity: 57 | - warning 58 | - recommendation 59 | 60 | # If you wish to specify custom queries, you can do so here or in a config file. 61 | # By default, queries listed here will override any specified in a config file. 62 | # Prefix the list here with "+" to use these queries and those in the config file. 63 | 64 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 65 | # queries: security-extended,security-and-quality 66 | 67 | 68 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). 69 | # If this step fails, then you should remove it and run the build manually (see below) 70 | - name: Autobuild 71 | uses: github/codeql-action/autobuild@v2 72 | 73 | # ℹ️ Command-line programs to run using the OS shell. 74 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 75 | 76 | # If the Autobuild fails above, remove it and uncomment the following three lines. 77 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 78 | 79 | # - run: | 80 | # echo "Run, Build Application using script" 81 | # ./location_of_script_within_repo/buildscript.sh 82 | 83 | - name: Perform CodeQL Analysis 84 | uses: github/codeql-action/analyze@v2 85 | with: 86 | category: "/language:${{matrix.language}}" 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | .idea 3 | dist 4 | out*dist/ 5 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # This is an example .goreleaser.yml file with some sane defaults. 2 | # Make sure to check the documentation at http://goreleaser.com 3 | before: 4 | hooks: 5 | # You may remove this if you don't use go modules. 6 | - go mod tidy 7 | # you may remove this if you don't need go generate 8 | - go generate ./... 9 | builds: 10 | - env: 11 | - CGO_ENABLED=0 12 | goos: 13 | - linux 14 | - windows 15 | - darwin 16 | archives: 17 | - name_template: "{{ .ProjectName }}_{{ .Tag }}_{{ .Os }}_{{ .Arch }}" 18 | 19 | checksum: 20 | name_template: 'checksums.txt' 21 | snapshot: 22 | name_template: "{{ .Tag }}" 23 | changelog: 24 | sort: asc 25 | filters: 26 | exclude: 27 | - '^docs:' 28 | - '^test:' 29 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:latest-bullseye AS builder 2 | 3 | # Build Args 4 | ARG BUILD_COMMAND="go build -o metabigor ." 5 | ARG BINARY_NAME="metabigor" 6 | ARG CGO_ENABLED="0" 7 | # Env setup 8 | ENV CGO_ENABLED=${CGO_ENABLED} 9 | 10 | # Setup workdir 11 | WORKDIR /build 12 | 13 | # Copy source code 14 | COPY . . 15 | 16 | # Fetch dependencies 17 | RUN apt install build-essential 18 | RUN go mod download 19 | 20 | RUN go build -o ${BINARY_NAME} . 21 | 22 | # Runner stage 23 | FROM golang:latest-alpine AS runner 24 | 25 | # Build Args 26 | ARG BINARY_NAME="metabigor" 27 | ARG START_COMMAND="./metabigor" 28 | 29 | # Setup workdir 30 | WORKDIR /app 31 | RUN useradd -D metabigor 32 | RUN chown -R metabigor:metabigor /app 33 | 34 | # Copy binary 35 | COPY --from=builder /build/${BINARY_NAME} . 36 | 37 | # Create entrypoint 38 | RUN echo ${START_COMMAND} > /app/entrypoint.sh 39 | RUN chmod +x /app/entrypoint.sh 40 | USER metabigor 41 | 42 | # Setup Entrypoint 43 | ENTRYPOINT ["sh", "-c", "/user/entrypoint.sh"] 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 j3ssie 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 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET ?= metabigor 2 | GO ?= go 3 | GOFLAGS ?= 4 | 5 | build: 6 | go install 7 | rm -rf ./dist/* 8 | GOOS=darwin GOARCH=amd64 $(GO) build $(GOFLAGS) -o dist/$(TARGET) 9 | zip -j dist/$(TARGET)-darwin.zip dist/$(TARGET) 10 | rm -rf ./dist/$(TARGET) 11 | # for linux build on mac 12 | GOOS=linux GOARCH=amd64 go build -o dist/$(TARGET) 13 | zip -j dist/$(TARGET)-linux.zip dist/$(TARGET) 14 | rm -rf ./dist/$(TARGET) 15 | 16 | update: 17 | rm -rf $(GOPATH)/src/github.com/j3ssie/metabigor/modules/static/ip2asn-combined.tsv.gz 18 | wget -q https://iptoasn.com/data/ip2asn-combined.tsv.gz -O $(GOPATH)/src/github.com/j3ssie/metabigor/modules/static/ip2asn-combined.tsv.gz 19 | echo "Done." 20 | 21 | run: 22 | $(GO) $(GOFLAGS) run *.go 23 | 24 | test: 25 | $(GO) $(GOFLAGS) test ./... -v% -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Metabigor 3 |
4 | Metabigor - An Intelligence Tool but without API key 5 | 6 |

7 | 8 | 9 | 10 |

11 |

12 | 13 | *** 14 | 15 | # What is Metabigor? 16 | 17 | Metabigor is Intelligence tool, its goal is to do OSINT tasks and more but without any API key. 18 | 19 | # Installation 20 | 21 | ### Pre-built Binaries 22 | 23 | You can download pre-built binaries for your platform from the [releases page](https://github.com/j3ssie/metabigor/releases). Choose the appropriate binary for your operating system and architecture, download it, and place it in your `PATH`. 24 | 25 | If the binary fails to execute, consider incorporating this into your environment: `export ASSUME_NO_MOVING_GC_UNSAFE_RISK_IT_WITH=go1.20`. 26 | 27 | ### Building from Source 28 | 29 | To install this project from source, you'll need Go installed on your system. If you haven't installed Go yet, you can follow the official installation instructions [here](https://golang.org/doc/install). 30 | 31 | ```shell 32 | git clone https://github.com/j3ssie/metabigor.git 33 | cd metabigor 34 | go install 35 | ``` 36 | 37 | 38 | # Main features 39 | 40 | - Searching information about IP Address, ASN and Organization. 41 | - Wrapper for running rustscan, masscan and nmap more efficient on IP/CIDR. 42 | - Finding more related domains of the target by applying various techniques (certificate, whois, Google Analytics, etc). 43 | - Get Summary about IP address (powered by [**@thebl4ckturtle**](https://github.com/theblackturtle)) 44 | 45 | # Usage 46 | 47 | ## Discovery IP of a company/organization - `metabigor net` 48 | 49 | The difference between net and **netd** command is that **netd** will get the dynamic result from the third-party source 50 | while net command will get the static result from the database. 51 | 52 | ```bash 53 | # discovery IP of a company/organization 54 | echo "company" | metabigor net --org -o /tmp/result.txt 55 | 56 | # discovery IP of an ASN 57 | echo "ASN1111" | metabigor net --asn -o /tmp/result.txt 58 | cat list_of_ASNs | metabigor net --asn -o /tmp/result.txt 59 | 60 | echo "ASN1111" | metabigor netd --asn -o /tmp/result.txt 61 | ``` 62 | 63 | *** 64 | 65 | ## Finding more related domains of the target by applying various techniques (certificate, whois, Google Analytics, etc) - `metabigor related` 66 | 67 | > Note some of the results are not 100% accurate. Please do a manual check first before put it directly to other tools 68 | > to scan. 69 | 70 | Some specific technique require different input so please see the usage of each technique. 71 | 72 | ## Using certificate to find related domains on crt.sh 73 | 74 | ```bash 75 | # Getting more related domains by searching for certificate info 76 | echo 'Target Inc' | metabigor cert --json | jq -r '.Domain' | unfurl format %r.%t | sort -u # this is old command 77 | 78 | # Getting more related domains by searching for certificate info 79 | echo 'example Inc' | metabigor related -s 'cert' 80 | ``` 81 | 82 | ## Wrapper for running rustscan, masscan and nmap more efficient on IP/CIDR - `metabigor scan` 83 | 84 | This command will require you to install `masscan`, `rustscan` and `nmap` first or at least the pre-scan result of them. 85 | 86 | ```bash 87 | # Only run rustscan with full ports 88 | echo '1.2.3.4/24' | metabigor scan -o result.txt 89 | 90 | # Only run nmap detail scan based on pre-scan data 91 | echo '1.2.3.4:21' | metabigor scan -s 92 | cat list_of_ip_with_port.txt | metabigor scan -c 10 --8 -s -o result.txt 93 | cat list_of_ip_with_port.txt | metabigor scan -c 10 --tmp /tmp/raw-result/ -s -o result.txt 94 | echo '1.2.3.4 -> [80,443,2222]' | metabigor scan -R 95 | 96 | # Run rustscan with full ports and nmap detail scan based on pre-scan data 97 | echo '1.2.3.4/24' | metabigor scan --pipe | metabigor scan -R 98 | ``` 99 | 100 | *** 101 | 102 | ## Using Reverse Whois to find related domains 103 | 104 | ```bash 105 | echo 'example.com' | metabigor related -s 'whois' 106 | ``` 107 | 108 | ## Getting more related by searching for Google Analytics ID 109 | 110 | ```bash 111 | # Get it directly from the URL 112 | echo 'https://example.com' | metabigor related -s 'google-analytic' 113 | 114 | # You can also search it directly from the UA ID too 115 | metabigor related -s 'google-analytic' -i 'UA-9152XXX' --debug 116 | ``` 117 | 118 | *** 119 | 120 | ## Get Summary about IP address (powered by [**@thebl4ckturtle**](https://github.com/theblackturtle)) - `metabigor ipc` 121 | 122 | This will show you the summary of the IP address provided like ASN, Organization, Country, etc. 123 | 124 | ```bash 125 | cat list_of_ips.txt | metabigor ipc --json 126 | ``` 127 | 128 | ## Extract Shodan IPInfo from internetdb.shodan.io 129 | 130 | ```bash 131 | echo '1.2.3.4' | metabigor ip -open 132 | 1.2.3.4:80 133 | 1.2.3.4:443 134 | 135 | # lookup CIDR range 136 | echo '1.2.3.4/24' | metabigor ip -open -c 20 137 | 1.2.3.4:80 138 | 1.2.3.5:80 139 | 140 | # get raw JSON response 141 | echo '1.2.3.4' | metabigor ip -json 142 | ``` 143 | 144 | # Demo 145 | 146 | [![asciicast](https://asciinema.org/a/301745.svg)](https://asciinema.org/a/301745) 147 | 148 | *** 149 | 150 | ## Painless integrate Metabigor into your recon workflow? 151 | 152 |

153 | OsmedeusEngine 154 |

155 | This project was part of Osmedeus Engine. Check out how it was integrated at @OsmedeusEngine 156 |

157 |

158 | 159 | # Credits 160 | 161 | Logo from [flaticon](https://image.flaticon.com/icons/svg/1789/1789851.svg) 162 | by [freepik](https://www.flaticon.com/authors/freepik) 163 | 164 | # Disclaimer 165 | 166 | This tool is for educational purposes only. You are responsible for your own actions. If you mess something up or break 167 | any laws while using this software, it's your fault, and your fault only. 168 | 169 | # License 170 | 171 | `Metabigor` is made with ♥ by [@j3ssiejjj](https://twitter.com/j3ssiejjj) and it is released under the MIT license. 172 | 173 | # Donation 174 | 175 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://paypal.me/j3ssiejjj) 176 | 177 | [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/j3ssie) 178 | -------------------------------------------------------------------------------- /cmd/cert.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "strings" 5 | "sync" 6 | 7 | "github.com/j3ssie/metabigor/core" 8 | "github.com/j3ssie/metabigor/modules" 9 | "github.com/panjf2000/ants" 10 | 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | func init() { 15 | var certCmd = &cobra.Command{ 16 | Use: "cert", 17 | Aliases: []string{"crt", "ctr"}, 18 | Short: "Certificates search", 19 | Long: core.DESC, 20 | RunE: runCert, 21 | } 22 | 23 | certCmd.Flags().BoolVarP(&options.Cert.Clean, "clean", "C", false, "Auto clean the result") 24 | certCmd.Flags().BoolVarP(&options.Cert.OnlyWildCard, "wildcard", "W", false, "Only get wildcard domain") 25 | RootCmd.AddCommand(certCmd) 26 | } 27 | 28 | func runCert(_ *cobra.Command, _ []string) error { 29 | options.Timeout = options.Timeout * 3 30 | 31 | var wg sync.WaitGroup 32 | p, _ := ants.NewPoolWithFunc(options.Concurrency, func(i interface{}) { 33 | job := i.(string) 34 | searchResult := runCertSearch(job, options) 35 | StoreData(searchResult, options) 36 | wg.Done() 37 | }, ants.WithPreAlloc(true)) 38 | defer p.Release() 39 | 40 | for _, target := range options.Inputs { 41 | wg.Add(1) 42 | _ = p.Invoke(strings.TrimSpace(target)) 43 | } 44 | 45 | wg.Wait() 46 | core.Unique(options.Output) 47 | return nil 48 | } 49 | 50 | func runCertSearch(input string, options core.Options) []string { 51 | var data []string 52 | core.BannerF("Searching on crt.sh for: ", input) 53 | result := modules.CrtSHOrg(input, options) 54 | data = append(data, result...) 55 | return data 56 | } 57 | -------------------------------------------------------------------------------- /cmd/ip.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/j3ssie/metabigor/core" 6 | "github.com/j3ssie/metabigor/modules" 7 | jsoniter "github.com/json-iterator/go" 8 | "github.com/panjf2000/ants" 9 | "github.com/projectdiscovery/mapcidr" 10 | "github.com/spf13/cobra" 11 | "strings" 12 | "sync" 13 | 14 | "net" 15 | ) 16 | 17 | var csvOutput = false 18 | var onlyHost = false 19 | 20 | func init() { 21 | var ipCmd = &cobra.Command{ 22 | Use: "ip", 23 | Short: "Extract Shodan IPInfo from internetdb.shodan.io", 24 | Long: core.DESC, 25 | RunE: runIP, 26 | } 27 | ipCmd.PersistentFlags().Bool("csv", true, "Show Output as CSV format") 28 | ipCmd.PersistentFlags().Bool("open", false, "Show Output as format 'IP:Port' only") 29 | RootCmd.AddCommand(ipCmd) 30 | } 31 | 32 | func runIP(cmd *cobra.Command, _ []string) error { 33 | csvOutput, _ = cmd.Flags().GetBool("csv") 34 | onlyHost, _ = cmd.Flags().GetBool("open") 35 | 36 | var wg sync.WaitGroup 37 | p, _ := ants.NewPoolWithFunc(options.Concurrency, func(i interface{}) { 38 | job := i.(string) 39 | StartJob(job) 40 | wg.Done() 41 | }, ants.WithPreAlloc(true)) 42 | defer p.Release() 43 | 44 | for _, target := range options.Inputs { 45 | wg.Add(1) 46 | _ = p.Invoke(strings.TrimSpace(target)) 47 | } 48 | 49 | wg.Wait() 50 | return nil 51 | } 52 | 53 | func StartJob(raw string) { 54 | _, _, err := net.ParseCIDR(raw) 55 | if err != nil { 56 | GetShodanIPInfo(raw) 57 | return 58 | } 59 | 60 | if ips, err := mapcidr.IPAddresses(raw); err == nil { 61 | for _, ip := range ips { 62 | GetShodanIPInfo(ip) 63 | } 64 | } 65 | } 66 | 67 | type ShodanIPInfo struct { 68 | Cpes []string `json:"cpes"` 69 | Hostnames []string `json:"hostnames"` 70 | IP string `json:"ip"` 71 | Ports []int `json:"ports"` 72 | Tags []string `json:"tags"` 73 | Vulns []string `json:"vulns"` 74 | } 75 | 76 | func GetShodanIPInfo(IP string) { 77 | data := modules.InternetDB(IP) 78 | if data == "" { 79 | core.ErrorF("No data found for: %s", IP) 80 | return 81 | } 82 | 83 | if options.JsonOutput { 84 | fmt.Println(data) 85 | return 86 | } 87 | 88 | var shodanIPInfo ShodanIPInfo 89 | if ok := jsoniter.Unmarshal([]byte(data), &shodanIPInfo); ok != nil { 90 | return 91 | } 92 | 93 | if csvOutput { 94 | for _, port := range shodanIPInfo.Ports { 95 | line := fmt.Sprintf("%s:%d", IP, port) 96 | if onlyHost { 97 | fmt.Println(line) 98 | continue 99 | } 100 | 101 | line = fmt.Sprintf("%s,%s,%s,%s,%s", line, strings.Join(shodanIPInfo.Hostnames, ";"), strings.Join(shodanIPInfo.Tags, ";"), strings.Join(shodanIPInfo.Cpes, ";"), strings.Join(shodanIPInfo.Vulns, ";")) 102 | fmt.Println(line) 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /cmd/ipc.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/j3ssie/metabigor/core" 7 | jsoniter "github.com/json-iterator/go" 8 | asnmap "github.com/projectdiscovery/asnmap/libs" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | func init() { 13 | var netCmd = &cobra.Command{ 14 | Use: "ipc", 15 | Short: "Summary about IP list", 16 | Long: core.DESC, 17 | RunE: runIPC, 18 | } 19 | RootCmd.AddCommand(netCmd) 20 | } 21 | 22 | var ASNClient *asnmap.Client 23 | 24 | func runIPC(_ *cobra.Command, _ []string) error { 25 | ASNClient, err := asnmap.NewClient() 26 | if err != nil { 27 | core.ErrorF("Unable to init asnmap client: %v", err) 28 | return err 29 | } 30 | 31 | asnCount := make(map[string]int) 32 | asnGroupByCIDR := map[string]AsnSummaryByCIDR{} 33 | asnSums := []AsnSummaryByCIDR{} 34 | 35 | for _, input := range options.Inputs { 36 | item, err := ASNClient.GetData(input) 37 | if err != nil { 38 | continue 39 | } 40 | 41 | listOfCIDR, err := asnmap.GetCIDR(item) 42 | if err != nil { 43 | continue 44 | } 45 | for _, cidr := range listOfCIDR { 46 | asnCount[cidr.String()]++ 47 | 48 | asnGroupByCIDR[cidr.String()] = AsnSummaryByCIDR{ 49 | Number: item[0].ASN, 50 | Description: item[0].Org, 51 | CountryCode: item[0].Country, 52 | } 53 | } 54 | 55 | } 56 | 57 | for cidr, count := range asnCount { 58 | asnInfo := asnGroupByCIDR[cidr] 59 | asnSums = append(asnSums, AsnSummaryByCIDR{ 60 | CIDR: cidr, 61 | Count: count, 62 | Number: asnInfo.Number, 63 | Description: asnInfo.Description, 64 | CountryCode: asnInfo.CountryCode, 65 | }) 66 | 67 | } 68 | 69 | // print the output here 70 | var contents []string 71 | for _, asnSum := range asnSums { 72 | if options.JsonOutput { 73 | if data, err := jsoniter.MarshalToString(asnSum); err == nil { 74 | contents = append(contents, data) 75 | } 76 | continue 77 | } 78 | 79 | data := fmt.Sprintf("%d - %s - %d", asnSum.Number, asnSum.CIDR, asnSum.Count) 80 | contents = append(contents, data) 81 | } 82 | 83 | StoreData(contents, options) 84 | return nil 85 | } 86 | 87 | type AsnSummaryByCIDR struct { 88 | Number int 89 | Description string 90 | CountryCode string 91 | CIDR string 92 | Count int 93 | } 94 | -------------------------------------------------------------------------------- /cmd/net.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/netip" 7 | "os" 8 | "strings" 9 | "sync" 10 | 11 | "github.com/j3ssie/metabigor/core" 12 | "github.com/j3ssie/metabigor/modules" 13 | jsoniter "github.com/json-iterator/go" 14 | "github.com/panjf2000/ants" 15 | "github.com/spf13/cast" 16 | "github.com/spf13/cobra" 17 | "github.com/thoas/go-funk" 18 | ) 19 | 20 | func init() { 21 | var netCmd = &cobra.Command{ 22 | Use: "net", 23 | Short: "Discover Network Information about targets (same with net command but use static data)", 24 | Long: core.DESC, 25 | RunE: runNet, 26 | } 27 | 28 | netCmd.Flags().Bool("asn", false, "Take input as ASN") 29 | netCmd.Flags().Bool("org", false, "Take input as Organization") 30 | netCmd.Flags().Bool("ip", false, "Take input as a single IP address") 31 | netCmd.Flags().Bool("domain", false, "Take input as a domain") 32 | netCmd.Flags().BoolVarP(&options.Net.ExactMatch, "exact", "x", false, "Only get from highly trusted source") 33 | RootCmd.AddCommand(netCmd) 34 | 35 | var netdCmd = &cobra.Command{ 36 | Use: "netd", 37 | Short: "Discover Network Information about targets (similar with 'net' command but use 3rd data)", 38 | Long: fmt.Sprintf(`Metabigor - Intelligence Framework but without API key - %v by %v`, core.VERSION, core.AUTHOR), 39 | RunE: runNetD, 40 | } 41 | 42 | netdCmd.Flags().Bool("asn", false, "Take input as ASN") 43 | netdCmd.Flags().Bool("org", false, "Take input as Organization") 44 | netdCmd.Flags().Bool("ip", false, "Take input as a single IP address") 45 | netdCmd.Flags().Bool("domain", false, "Take input as a domain") 46 | netdCmd.Flags().BoolP("accurate", "x", false, "Only get from highly trusted source") 47 | RootCmd.AddCommand(netdCmd) 48 | } 49 | 50 | var ASNMap modules.AsnMap 51 | 52 | func runNet(cmd *cobra.Command, _ []string) error { 53 | asn, _ := cmd.Flags().GetBool("asn") 54 | org, _ := cmd.Flags().GetBool("org") 55 | ip, _ := cmd.Flags().GetBool("ip") 56 | domain, _ := cmd.Flags().GetBool("domain") 57 | 58 | if asn { 59 | options.Net.SearchType = "asn" 60 | } else if org { 61 | options.Net.SearchType = "org" 62 | } else if ip { 63 | options.Net.SearchType = "ip" 64 | } else if domain { 65 | options.Net.SearchType = "domain" 66 | } 67 | if options.Net.SearchType == "" { 68 | fmt.Fprintf(os.Stderr, "You need to specify search type with one of these flag: --asn, --org or --ip") 69 | os.Exit(-1) 70 | } 71 | 72 | var err error 73 | ASNMap, err = modules.GetAsnMap() 74 | if err != nil { 75 | fmt.Fprintf(os.Stderr, "Error to generate asn info") 76 | os.Exit(-1) 77 | } 78 | 79 | var wg sync.WaitGroup 80 | p, _ := ants.NewPoolWithFunc(options.Concurrency, func(i interface{}) { 81 | job := i.(string) 82 | var osintResult []string 83 | osintResult = runNetJob(job, options) 84 | StoreData(osintResult, options) 85 | wg.Done() 86 | }, ants.WithPreAlloc(true)) 87 | defer p.Release() 88 | 89 | for _, target := range options.Inputs { 90 | wg.Add(1) 91 | _ = p.Invoke(strings.TrimSpace(target)) 92 | } 93 | wg.Wait() 94 | 95 | if options.Output != "" && !core.FileExists(options.Output) { 96 | core.ErrorF("No data found") 97 | } 98 | return nil 99 | } 100 | 101 | func runNetJob(input string, options core.Options) []string { 102 | var data []string 103 | var asnInfos []modules.ASInfo 104 | 105 | if !options.Net.ExactMatch { 106 | input = strings.ToLower(input) 107 | 108 | } 109 | 110 | switch options.Net.SearchType { 111 | case "asn": 112 | input = strings.ToLower(input) 113 | if strings.Contains(input, "as") { 114 | input = strings.ReplaceAll(input, "as", "") 115 | } 116 | asInfos := ASNMap.ASInfo(cast.ToInt(input)) 117 | if len(asInfos) > 0 { 118 | asnInfos = append(asnInfos, asInfos...) 119 | } 120 | 121 | case "org": 122 | asnNums := ASNMap.ASDesc(input) 123 | if len(asnNums) > 0 { 124 | for _, asnNum := range asnNums { 125 | asnInfos = append(asnInfos, ASNMap.ASInfo(asnNum)...) 126 | } 127 | } 128 | 129 | case "ip": 130 | asnInfos = append(asnInfos, searchByIP(input)...) 131 | 132 | case "domain": 133 | ips, err := net.LookupHost(input) 134 | if err == nil { 135 | for _, ip := range ips { 136 | asnInfos = append(asnInfos, searchByIP(ip)...) 137 | } 138 | } 139 | } 140 | 141 | if len(asnInfos) == 0 { 142 | core.ErrorF("No result found for: %s", input) 143 | return data 144 | } 145 | 146 | for _, asnInfo := range asnInfos { 147 | line := genOutput(asnInfo) 148 | data = append(data, line) 149 | } 150 | 151 | return data 152 | } 153 | 154 | func genOutput(asnInfo modules.ASInfo) string { 155 | var line string 156 | if options.JsonOutput { 157 | if content, err := jsoniter.MarshalToString(asnInfo); err == nil { 158 | return content 159 | } 160 | return line 161 | } 162 | if options.Verbose { 163 | line = fmt.Sprintf("%d - %s - %s - %s", asnInfo.Number, asnInfo.CIDR, asnInfo.Description, asnInfo.CountryCode) 164 | } else { 165 | line = asnInfo.CIDR 166 | } 167 | return line 168 | } 169 | 170 | func searchByIP(input string) []modules.ASInfo { 171 | var asnInfo []modules.ASInfo 172 | 173 | ip, err := netip.ParseAddr(input) 174 | if err != nil { 175 | return asnInfo 176 | } 177 | 178 | if asn := ASNMap.ASofIP(ip); asn.AS != 0 { 179 | return ASNMap.ASInfo(asn.AS) 180 | } 181 | return asnInfo 182 | } 183 | 184 | /////////// netd command 185 | 186 | func runNetD(cmd *cobra.Command, _ []string) error { 187 | asn, _ := cmd.Flags().GetBool("asn") 188 | org, _ := cmd.Flags().GetBool("org") 189 | ip, _ := cmd.Flags().GetBool("ip") 190 | domain, _ := cmd.Flags().GetBool("domain") 191 | options.Net.Optimize, _ = cmd.Flags().GetBool("accurate") 192 | 193 | var wg sync.WaitGroup 194 | p, _ := ants.NewPoolWithFunc(options.Concurrency, func(i interface{}) { 195 | job := i.(string) 196 | var osintResult []string 197 | if asn { 198 | osintResult = runASN(job, options) 199 | } else if org { 200 | osintResult = runOrg(job, options) 201 | } else if ip { 202 | options.Net.IP = job 203 | osintResult = runSingle(job, options) 204 | } else if domain { 205 | options.Net.Domain = job 206 | osintResult = runSingle(job, options) 207 | } 208 | StoreData(osintResult, options) 209 | 210 | wg.Done() 211 | }, ants.WithPreAlloc(true)) 212 | defer p.Release() 213 | 214 | for _, target := range options.Inputs { 215 | wg.Add(1) 216 | _ = p.Invoke(strings.TrimSpace(target)) 217 | } 218 | 219 | wg.Wait() 220 | 221 | if options.Output != "" && !core.FileExists(options.Output) { 222 | core.ErrorF("No data found") 223 | } 224 | return nil 225 | } 226 | 227 | func runSingle(input string, options core.Options) []string { 228 | core.BannerF("Starting get ASN from: ", input) 229 | var data []string 230 | ans := modules.ASNFromIP(options) 231 | 232 | // get more IP by result ASN 233 | for _, item := range ans { 234 | if strings.HasPrefix(strings.ToLower(item), "as") { 235 | data = append(data, runASN(item, options)...) 236 | } 237 | } 238 | return data 239 | } 240 | 241 | func runASN(input string, options core.Options) []string { 242 | core.BannerF("Starting get IP Info from ASN: ", input) 243 | options.Net.Asn = input 244 | var data []string 245 | var wg sync.WaitGroup 246 | 247 | wg.Add(1) 248 | go func() { 249 | data = append(data, modules.GetIPInfo(options)...) 250 | wg.Done() 251 | }() 252 | 253 | wg.Wait() 254 | return data 255 | } 256 | 257 | func runOrg(input string, options core.Options) []string { 258 | core.BannerF("Starting get IP Info for Organization: ", input) 259 | options.Net.Org = input 260 | var data []string 261 | var wg sync.WaitGroup 262 | wg.Add(1) 263 | go func() { 264 | data = append(data, modules.OrgBgpDotNet(options)...) 265 | wg.Done() 266 | }() 267 | 268 | wg.Add(1) 269 | go func() { 270 | data = append(data, modules.OrgBgbView(options)...) 271 | wg.Done() 272 | }() 273 | 274 | // disable when enable trusted source 275 | if !options.Net.Optimize { 276 | wg.Add(1) 277 | go func() { 278 | data = append(data, modules.ASNLookup(options)...) 279 | wg.Done() 280 | }() 281 | } 282 | wg.Wait() 283 | 284 | var cidrs []string 285 | // get more IP by result ASN 286 | for _, item := range data { 287 | // get more range from ASN 288 | if strings.HasPrefix(strings.ToLower(item), "as") { 289 | wg.Add(1) 290 | go func(item string) { 291 | cidrs = append(cidrs, runASN(item, options)...) 292 | wg.Done() 293 | }(item) 294 | continue 295 | } else if core.StartWithNum(item) { 296 | cidrs = append(cidrs, item) 297 | } 298 | } 299 | wg.Wait() 300 | return funk.Uniq(cidrs).([]string) 301 | } 302 | 303 | // StoreData store data to output 304 | func StoreData(data []string, options core.Options) { 305 | if len(data) == 0 && !options.PipeTheOutput { 306 | core.ErrorF("Empty data to write") 307 | return 308 | } 309 | 310 | fmt.Println(strings.Join(data, "\n")) 311 | _, err := core.AppendToContent(options.Output, strings.Join(data, "\n")) 312 | if err == nil { 313 | core.InforF("Write output to: %v", options.Output) 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /cmd/related.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/j3ssie/metabigor/core" 6 | "github.com/j3ssie/metabigor/modules" 7 | jsoniter "github.com/json-iterator/go" 8 | "github.com/panjf2000/ants" 9 | "github.com/spf13/cobra" 10 | "strings" 11 | "sync" 12 | ) 13 | 14 | func init() { 15 | var tldCmd = &cobra.Command{ 16 | Use: "related", 17 | Aliases: []string{"tld", "relate"}, 18 | Short: "Finding more related domains of the target by applying various techniques", 19 | Long: core.DESC, 20 | RunE: runTLD, 21 | } 22 | tldCmd.Flags().StringVarP(&options.Tld.Source, "src", "s", "all", "Source for gathering TLD") 23 | RootCmd.AddCommand(tldCmd) 24 | } 25 | 26 | func runTLD(_ *cobra.Command, _ []string) error { 27 | var wg sync.WaitGroup 28 | p, _ := ants.NewPoolWithFunc(options.Concurrency, func(i interface{}) { 29 | job := i.(string) 30 | TLDJob(job) 31 | wg.Done() 32 | }, ants.WithPreAlloc(true)) 33 | defer p.Release() 34 | 35 | for _, target := range options.Inputs { 36 | wg.Add(1) 37 | _ = p.Invoke(strings.TrimSpace(target)) 38 | } 39 | 40 | wg.Wait() 41 | return nil 42 | } 43 | 44 | func TLDJob(raw string) { 45 | var results []core.RelatedDomain 46 | switch options.Tld.Source { 47 | case "all": 48 | results = append(results, modules.CrtSH(raw, options)...) 49 | results = append(results, modules.ReverseWhois(raw, options)...) 50 | results = append(results, modules.GoogleAnalytic(raw, options)...) 51 | case "crt", "cert": 52 | results = append(results, modules.CrtSH(raw, options)...) 53 | case "whois", "who": 54 | results = append(results, modules.ReverseWhois(raw, options)...) 55 | case "ua", "gtm", "google-analytic": 56 | results = append(results, modules.GoogleAnalytic(raw, options)...) 57 | } 58 | 59 | for _, item := range results { 60 | if options.JsonOutput { 61 | if data, err := jsoniter.MarshalToString(item); err == nil { 62 | fmt.Println(data) 63 | } 64 | continue 65 | } 66 | 67 | if options.Verbose { 68 | fmt.Println(item.Output) 69 | } else { 70 | fmt.Println(item.Domain) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/fatih/color" 10 | "github.com/j3ssie/metabigor/core" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var options = core.Options{} 15 | 16 | var RootCmd = &cobra.Command{ 17 | Use: "metabigor", 18 | Long: core.DESC, 19 | } 20 | 21 | // Execute main function 22 | func Execute() { 23 | if err := RootCmd.Execute(); err != nil { 24 | fmt.Println(err) 25 | os.Exit(1) 26 | } 27 | } 28 | 29 | func init() { 30 | cobra.OnInitialize(initConfig) 31 | RootCmd.PersistentFlags().StringVar(&options.Proxy, "proxy", "", "Proxy for doing request") 32 | RootCmd.PersistentFlags().IntVarP(&options.Concurrency, "concurrency", "c", 5, "concurrency") 33 | RootCmd.PersistentFlags().IntVar(&options.Timeout, "timeout", 40, "timeout") 34 | RootCmd.PersistentFlags().IntVar(&options.Retry, "retry", 3, "Retry") 35 | 36 | RootCmd.PersistentFlags().StringSliceVarP(&options.Inputs, "input", "i", []string{}, "Input to run") 37 | RootCmd.PersistentFlags().StringVarP(&options.InputFile, "inputFile", "I", "", "Input file") 38 | 39 | RootCmd.PersistentFlags().StringVarP(&options.Output, "output", "o", "", "Output File") 40 | RootCmd.PersistentFlags().StringVarP(&options.Scan.TmpOutput, "tmp", "T", "", "Temp Output folder") 41 | RootCmd.PersistentFlags().BoolVarP(&options.JsonOutput, "json", "J", false, "Output as JSON") 42 | RootCmd.PersistentFlags().BoolVarP(&options.Verbose, "verbose", "v", false, "Verbose") 43 | RootCmd.PersistentFlags().BoolVarP(&options.Quiet, "quiet", "q", false, "Show only essential information") 44 | 45 | RootCmd.PersistentFlags().BoolVar(&options.Debug, "debug", false, "Debug") 46 | RootCmd.SetHelpFunc(RootMessage) 47 | } 48 | 49 | // initConfig reads in config file and ENV variables if set. 50 | func initConfig() { 51 | if options.Debug { 52 | options.Verbose = true 53 | options.Quiet = false 54 | } 55 | if options.PipeTheOutput { 56 | options.Quiet = true 57 | } 58 | core.InitLog(&options) 59 | 60 | if options.Scan.TmpOutput != "" && !core.FolderExists(options.Scan.TmpOutput) { 61 | core.InforF("Create new temp folder: %v", options.Scan.TmpOutput) 62 | os.MkdirAll(options.Scan.TmpOutput, 0750) 63 | } 64 | 65 | // detect if anything came from stdin 66 | stat, _ := os.Stdin.Stat() 67 | if (stat.Mode() & os.ModeCharDevice) == 0 { 68 | sc := bufio.NewScanner(os.Stdin) 69 | for sc.Scan() { 70 | target := strings.TrimSpace(sc.Text()) 71 | if err := sc.Err(); err == nil && target != "" { 72 | options.Inputs = append(options.Inputs, target) 73 | } 74 | } 75 | } 76 | 77 | if core.FileExists(options.InputFile) { 78 | options.Input = core.GetFileContent(options.Input) 79 | } 80 | 81 | // get input from a file or just a string 82 | if core.FileExists(options.InputFile) { 83 | options.Input = core.GetFileContent(options.InputFile) 84 | } 85 | 86 | core.InforF("Metabigor %v by %v", core.VERSION, color.HiMagentaString(core.AUTHOR)) 87 | core.DebugF(fmt.Sprintf("Store log file to: %v", options.LogFile)) 88 | } 89 | 90 | // RootMessage print help message 91 | func RootMessage(cmd *cobra.Command, _ []string) { 92 | fmt.Printf(cmd.UsageString()) 93 | h := ` 94 | Examples Commands 95 | ================= 96 | 97 | # discovery IP of a company/organization 98 | echo "company" | metabigor net --org -o /tmp/result.txt 99 | 100 | # discovery IP of an ASN 101 | echo "ASN1111" | metabigor net --asn -o /tmp/result.txt 102 | cat list_of_ASNs | metabigor net --asn -o /tmp/result.txt 103 | 104 | # Only run rustscan with full ports 105 | echo '1.2.3.4/24' | metabigor scan -o result.txt 106 | 107 | # Only run nmap detail scan based on pre-scan data 108 | echo '1.2.3.4:21' | metabigor scan -s 109 | cat list_of_ip_with_port.txt | metabigor scan -c 10 --8 -s -o result.txt 110 | cat list_of_ip_with_port.txt | metabigor scan -c 10 --tmp /tmp/raw-result/ -s -o result.txt 111 | echo '1.2.3.4 -> [80,443,2222]' | metabigor scan -R 112 | 113 | # Run rustscan with full ports and nmap detail scan based on pre-scan data 114 | echo '1.2.3.4/24' | metabigor scan --pipe | metabigor scan -R 115 | 116 | # Only run scan with zmap 117 | cat ranges.txt | metabigor scan -p '443,80' -z 118 | 119 | # Get Summary about IP address (powered by @thebl4ckturtle) 120 | cat list_of_ips.txt | metabigor ipc --json 121 | 122 | # Finding more related domains of the target by applying various techniques 123 | 124 | ## Getting more related domains by searching for certificate info 125 | echo 'example.com' | metabigor related -s 'cert' 126 | 127 | ## Getting more related by searching for whois info 128 | echo 'example.com' | metabigor related -s 'whois' 129 | 130 | ## Getting more related by searching for google analytics ID 131 | echo 'https://example.com' | metabigor related -s 'google-analytic' 132 | metabigor related -s 'google-analytic' -i 'UA-9152XXX' --debug 133 | ` 134 | fmt.Printf(h) 135 | } 136 | -------------------------------------------------------------------------------- /cmd/scan.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "sync" 8 | 9 | "github.com/pkg/errors" 10 | "github.com/thoas/go-funk" 11 | 12 | "github.com/j3ssie/metabigor/core" 13 | "github.com/j3ssie/metabigor/modules" 14 | "github.com/spf13/cobra" 15 | ) 16 | 17 | func init() { 18 | var scanCmd = &cobra.Command{ 19 | Use: "scan", 20 | Short: "Wrapper to run port scan from provided input", 21 | Long: core.DESC, 22 | RunE: runScan, 23 | } 24 | // scan options 25 | scanCmd.Flags().StringVarP(&options.Scan.Ports, "ports", "p", "0-65535", "Port range for previous command") 26 | scanCmd.Flags().StringVarP(&options.Scan.Rate, "rate", "r", "3000", "rate limit for rustscan command") 27 | scanCmd.Flags().StringVar(&options.Scan.Retry, "retry", "", "retry limit for rustscan command") 28 | scanCmd.Flags().StringVar(&options.Scan.Timeout, "timeout", "", "timeout for rustscan command") 29 | scanCmd.Flags().BoolVarP(&options.Scan.All, "join", "A", false, "Join all inputs to a file first then do a scan") 30 | // scan strategy option 31 | scanCmd.Flags().BoolVarP(&options.Scan.Flat, "flat", "f", true, "format output like this: 1.2.3.4:443") 32 | scanCmd.Flags().BoolVarP(&options.Scan.NmapOverview, "nmap", "n", false, "Use nmap instead of masscan for overview scan") 33 | scanCmd.Flags().BoolVarP(&options.Scan.ZmapOverview, "zmap", "z", false, "Only scan range with zmap") 34 | scanCmd.Flags().BoolVar(&options.PipeTheOutput, "pipe", false, "pipe the output to another command") 35 | scanCmd.Flags().BoolVarP(&options.Scan.InputFromRustScan, "rstd", "R", false, "run nmap from rustscan input format like: 1.2.3.4 -> [80,443,8080,8443,8880]") 36 | // more nmap options 37 | scanCmd.Flags().StringVarP(&options.Scan.NmapScripts, "script", "S", "", "nmap scripts") 38 | scanCmd.Flags().StringVar(&options.Scan.NmapTemplate, "nmap-command", "nmap -sSV -sC -p {{.ports}} {{.input}} {{.script}} -T4 --open -oA {{.output}}", "Nmap template command to run") 39 | scanCmd.Flags().StringVar(&options.Scan.GrepString, "grep", "", "match string to confirm script success") 40 | scanCmd.Flags().String("result-folder", "", "Result folder") 41 | scanCmd.Flags().BoolVar(&options.Scan.IPv4, "4", true, "Filter input to only get ipv4") 42 | scanCmd.Flags().BoolVar(&options.Scan.Skip80And443, "8", false, "Skip ports 80 and 443. Useful when you want to look for service behind the list of pre-scanned data") 43 | //scanCmd.Flags().Bool("6", false, "Filter input to only get ipv4") 44 | scanCmd.Flags().BoolP("detail", "D", false, "Do Nmap scan based on previous output") 45 | scanCmd.Flags().Bool("uniq", true, "Unique input first") 46 | scanCmd.SetHelpFunc(ScanHelp) 47 | RootCmd.AddCommand(scanCmd) 48 | } 49 | 50 | func runScan(cmd *cobra.Command, _ []string) error { 51 | // only parse result 52 | resultFolder, _ := cmd.Flags().GetString("result-folder") 53 | uniq, _ := cmd.Flags().GetBool("uniq") 54 | if resultFolder != "" { 55 | parseResult(resultFolder, options) 56 | os.Exit(0) 57 | } 58 | 59 | if options.Scan.InputFromRustScan { 60 | options.Scan.SkipOverview = true 61 | } 62 | 63 | // make sure input is valid 64 | if options.Scan.IPv4 { 65 | // only filter when run zmap 66 | if !options.Scan.SkipOverview { 67 | options.Inputs = core.FilterIpv4(options.Inputs) 68 | } 69 | } 70 | if uniq { 71 | options.Inputs = funk.UniqString(options.Inputs) 72 | } 73 | if len(options.Inputs) == 0 { 74 | core.ErrorF("No input provided") 75 | os.Exit(1) 76 | } 77 | 78 | var result []string 79 | var wg sync.WaitGroup 80 | jobs := make(chan string) 81 | 82 | if options.Scan.All || options.Scan.ZmapOverview { 83 | options.Scan.InputFile = StoreTmpInput(options.Inputs, options) 84 | core.DebugF("Store temp input in: %v", options.Scan.InputFile) 85 | 86 | if options.Scan.ZmapOverview { 87 | ports := core.GenPorts(options.Scan.Ports) 88 | core.DebugF("Run port scan with: %v", strings.Trim(strings.Join(ports, ","), ",")) 89 | if options.Scan.InputFile == "" || len(ports) == 0 { 90 | core.ErrorF("Error gen input or ports") 91 | return nil 92 | } 93 | for i := 0; i < options.Concurrency; i++ { 94 | wg.Add(1) 95 | go func() { 96 | defer wg.Done() 97 | for job := range jobs { 98 | // do real stuff here 99 | core.BannerF("Run zmap scan on port ", job) 100 | result = modules.RunZmap(options.Scan.InputFile, job, options) 101 | StoreData(result, options) 102 | } 103 | }() 104 | } 105 | for _, port := range ports { 106 | jobs <- port 107 | } 108 | close(jobs) 109 | wg.Wait() 110 | return nil 111 | } 112 | 113 | core.BannerF("Run overview scan on port ", options.Scan.InputFile) 114 | if options.Scan.NmapOverview { 115 | result = modules.RunNmap(options.Scan.InputFile, "", options) 116 | } else { 117 | result = modules.RunRustScan(options.Scan.InputFile, options) 118 | } 119 | StoreData(result, options) 120 | return nil 121 | } 122 | 123 | for i := 0; i < options.Concurrency; i++ { 124 | wg.Add(1) 125 | go func() { 126 | defer wg.Done() 127 | // do real stuff here 128 | for job := range jobs { 129 | if options.Scan.SkipOverview { 130 | result = directDetail(job, options) 131 | } else { 132 | result = runRoutine(job, options) 133 | } 134 | StoreData(result, options) 135 | } 136 | }() 137 | } 138 | 139 | for _, input := range options.Inputs { 140 | jobs <- input 141 | } 142 | 143 | close(jobs) 144 | wg.Wait() 145 | 146 | return nil 147 | } 148 | 149 | func runRoutine(input string, options core.Options) []string { 150 | var data []string 151 | core.BannerF("Run overview scan on: ", input) 152 | if options.Scan.NmapOverview { 153 | data = append(data, modules.RunNmap(input, "", options)...) 154 | } else { 155 | data = append(data, modules.RunRustScan(input, options)...) 156 | } 157 | 158 | if !options.Scan.Detail { 159 | return data 160 | } 161 | 162 | var wg sync.WaitGroup 163 | var realData []string 164 | for _, item := range data { 165 | wg.Add(1) 166 | go func(item string) { 167 | realData = append(realData, runDetail(item, options)...) 168 | wg.Done() 169 | }(item) 170 | } 171 | wg.Wait() 172 | return realData 173 | } 174 | 175 | func runDetail(input string, options core.Options) []string { 176 | if options.Scan.Flat { 177 | return directDetail(input, options) 178 | } 179 | if input == "" { 180 | return []string{} 181 | } 182 | if len(strings.Split(input, " - ")) == 1 { 183 | return []string{} 184 | } 185 | 186 | host := strings.Split(input, " - ")[0] 187 | ports := strings.Split(input, " - ")[1] 188 | core.BannerF("Run detail scan on: ", fmt.Sprintf("%v:%v", host, ports)) 189 | return modules.RunNmap(host, ports, options) 190 | } 191 | 192 | func directDetail(input string, options core.Options) []string { 193 | var out []string 194 | if options.Scan.Skip80And443 { 195 | if strings.HasSuffix(input, ":80") && strings.HasSuffix(input, ":443") { 196 | return out 197 | } 198 | } 199 | 200 | if input == "" { 201 | return out 202 | } 203 | var host, ports string 204 | 205 | if options.Scan.InputFromRustScan { 206 | // 1.1.1.1 -> [80,443,2095,2096,8080,8443,8880] 207 | if !strings.Contains(input, "->") { 208 | return out 209 | } 210 | host = strings.Split(input, " -> ")[0] 211 | ports = strings.Split(input, " -> ")[1] 212 | ports = strings.TrimLeft(strings.TrimRight(ports, "]"), "[") 213 | } else { 214 | if len(strings.Split(input, ":")) == 1 { 215 | return out 216 | } 217 | host = strings.Split(input, ":")[0] 218 | ports = strings.Split(input, ":")[1] 219 | } 220 | 221 | core.BannerF("Run detail scan on: ", fmt.Sprintf("%v:%v", host, ports)) 222 | out = modules.RunNmap(host, ports, options) 223 | return out 224 | } 225 | 226 | // only parse result 227 | func parseResult(resultFolder string, options core.Options) { 228 | if !core.FolderExists(resultFolder) { 229 | core.ErrorF("Result Folder not found: ", resultFolder) 230 | return 231 | } 232 | dir, err := os.Open(resultFolder) 233 | if err != nil { 234 | fmt.Println(errors.Wrap(err, "error opening directory")) 235 | return 236 | } 237 | defer dir.Close() 238 | 239 | Files, err := dir.Readdir(0) 240 | if err != nil { 241 | fmt.Println(errors.Wrap(err, "error reading directory contents")) 242 | return 243 | } 244 | 245 | if options.Scan.Detail { 246 | // nmap 247 | for _, file := range Files { 248 | filename := file.Name() 249 | core.DebugF("Reading: %v", filename) 250 | if strings.HasSuffix(file.Name(), "xml") && strings.HasPrefix(filename, "nmap") { 251 | data := core.GetFileContent(filename) 252 | result := modules.ParseNmap(data, options) 253 | if len(result) > 0 { 254 | fmt.Printf(strings.Join(result, "\n")) 255 | } 256 | } 257 | } 258 | return 259 | } 260 | 261 | // masscan 262 | for _, file := range Files { 263 | filename := file.Name() 264 | core.DebugF("Reading: %v", filename) 265 | if strings.HasPrefix(filename, "masscan") { 266 | data := core.GetFileContent(filename) 267 | fmt.Println(data) 268 | rawResult := modules.ParsingMasscan(data) 269 | fmt.Println(rawResult) 270 | for k, v := range rawResult { 271 | for _, port := range v { 272 | fmt.Printf("%v:%v\n", k, port) 273 | } 274 | } 275 | } 276 | } 277 | } 278 | 279 | // StoreTmpInput store list of string to tmp file 280 | func StoreTmpInput(raw []string, options core.Options) string { 281 | tmpDest := options.Scan.TmpOutput 282 | tmpFile, err := os.CreateTemp(tmpDest, "joined-*.txt") 283 | if err != nil { 284 | return "" 285 | } 286 | defer tmpFile.Close() 287 | 288 | if tmpDest != "" { 289 | tmpFile, err = os.CreateTemp(tmpDest, "joined-input-*.txt") 290 | if err != nil { 291 | return "" 292 | } 293 | defer tmpFile.Close() 294 | } 295 | 296 | tmpDest = tmpFile.Name() 297 | core.WriteToFile(tmpDest, strings.Join(raw, "\n")) 298 | return tmpDest 299 | } 300 | 301 | // ScanHelp print help message 302 | func ScanHelp(cmd *cobra.Command, _ []string) { 303 | fmt.Println(cmd.UsageString()) 304 | h := "\nExample Commands:\n" 305 | h += " # Run Nmap with output from rustscan\n" 306 | h += " echo '1.2.3.4 -> [80,443,2222]' | metabigor scan -R\n" 307 | h += " # Only run masscan full ports\n" 308 | h += " echo '1.2.3.4/24' | metabigor scan -o result.txt\n\n" 309 | h += " # Only run nmap detail scan\n" 310 | h += " echo '1.2.3.4:21' | metabigor scan -s -c 10\n" 311 | h += " echo '1.2.3.4:21' | metabigor scan --tmp /tmp/raw-result/ -s -o result.txt\n\n" 312 | h += " # Only run scan with zmap \n" 313 | h += " cat ranges.txt | metabigor scan -p '443,80' -z\n" 314 | h += "\n" 315 | fmt.Printf(h) 316 | } 317 | -------------------------------------------------------------------------------- /core/chrome.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "path" 8 | "path/filepath" 9 | "time" 10 | 11 | "github.com/chromedp/chromedp" 12 | ) 13 | 14 | // RequestWithChrome Do request with real browser 15 | func RequestWithChrome(url string, contentID string, timeout int) string { 16 | // prepare the chrome options 17 | opts := append(chromedp.DefaultExecAllocatorOptions[:], 18 | chromedp.Flag("headless", true), 19 | chromedp.Flag("ignore-certificate-errors", true), 20 | chromedp.Flag("disable-gpu", true), 21 | chromedp.Flag("enable-automation", true), 22 | chromedp.Flag("disable-extensions", true), 23 | chromedp.Flag("disable-setuid-sandbox", true), 24 | chromedp.Flag("disable-web-security", true), 25 | chromedp.Flag("no-first-run", true), 26 | chromedp.Flag("no-default-browser-check", true), 27 | ) 28 | 29 | allocCtx, bcancel := chromedp.NewExecAllocator(context.Background(), opts...) 30 | defer bcancel() 31 | 32 | ctx, cancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf)) 33 | ctx, cancel = context.WithTimeout(ctx, time.Duration(timeout)*time.Second) 34 | defer cancel() 35 | 36 | // run task list 37 | var data string 38 | err := chromedp.Run(ctx, 39 | chromedp.Navigate(url), 40 | chromedp.OuterHTML(contentID, &data, chromedp.NodeVisible, chromedp.ByID), 41 | ) 42 | DebugF(data) 43 | 44 | // clean chromedp-runner folder 45 | cleanUp() 46 | 47 | if err != nil { 48 | InforF("[ERRR] %v", err) 49 | return "" 50 | } 51 | return data 52 | } 53 | 54 | func cleanUp() { 55 | tmpFolder := path.Join(os.TempDir(), "chromedp-runner*") 56 | if _, err := os.Stat("/tmp/"); !os.IsNotExist(err) { 57 | tmpFolder = path.Join("/tmp/", "chromedp-runner*") 58 | } 59 | junks, err := filepath.Glob(tmpFolder) 60 | if err != nil { 61 | return 62 | } 63 | for _, junk := range junks { 64 | os.RemoveAll(junk) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /core/common.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "path" 8 | 9 | // "github.com/Sirupsen/logrus" 10 | "github.com/fatih/color" 11 | "github.com/sirupsen/logrus" 12 | prefixed "github.com/x-cray/logrus-prefixed-formatter" 13 | ) 14 | 15 | // NullWriter implements the io.Writer interface 16 | // and discards all data written to it. 17 | type NullWriter struct{} 18 | 19 | // Write implements the Write method of the io.Writer interface. 20 | // It discards all data written to it. 21 | func (nw *NullWriter) Write(p []byte) (n int, err error) { 22 | return len(p), nil 23 | } 24 | 25 | var logger = logrus.New() 26 | 27 | // InitLog init log 28 | func InitLog(options *Options) { 29 | if options.Scan.TmpOutput == "" { 30 | options.Scan.TmpOutput = path.Join(os.TempDir(), "mtg-log") 31 | } 32 | if !FolderExists(options.Scan.TmpOutput) { 33 | os.MkdirAll(options.Scan.TmpOutput, 0755) 34 | } 35 | options.LogFile = path.Join(options.Scan.TmpOutput, fmt.Sprintf("metabigor-%s.log", GetTS())) 36 | f, err := os.OpenFile(options.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) 37 | if err != nil { 38 | logger.Error("error opening file: %v", err) 39 | } 40 | 41 | mwr := io.MultiWriter(os.Stderr, f) 42 | logger.SetLevel(logrus.InfoLevel) 43 | logger = &logrus.Logger{ 44 | Out: mwr, 45 | Level: logrus.InfoLevel, 46 | Formatter: &prefixed.TextFormatter{ 47 | ForceColors: true, 48 | ForceFormatting: true, 49 | }, 50 | } 51 | 52 | if options.Debug == true { 53 | logger.SetOutput(mwr) 54 | logger.SetLevel(logrus.DebugLevel) 55 | } else if options.Verbose == true { 56 | logger.SetOutput(mwr) 57 | logger.SetLevel(logrus.InfoLevel) 58 | } 59 | if options.Quiet { 60 | logger.SetOutput(&NullWriter{}) 61 | } 62 | } 63 | 64 | // GoodF print good message 65 | func GoodF(format string, args ...interface{}) { 66 | good := color.HiGreenString("[+]") 67 | fmt.Fprintf(os.Stderr, "%s %s\n", good, fmt.Sprintf(format, args...)) 68 | } 69 | 70 | // BannerF print info message 71 | func BannerF(prefix string, data string) { 72 | logger.Info(fmt.Sprintf("%v %v%v", color.HiBlueString("==>"), prefix, color.HiGreenString(data))) 73 | } 74 | 75 | // InforF print info message 76 | func InforF(format string, args ...interface{}) { 77 | logger.Info(fmt.Sprintf(format, args...)) 78 | } 79 | 80 | // WarningF print good message 81 | func WarningF(format string, args ...interface{}) { 82 | good := color.YellowString("[!]") 83 | fmt.Fprintf(os.Stderr, "%s %s\n", good, fmt.Sprintf(format, args...)) 84 | } 85 | 86 | // DebugF print debug message 87 | func DebugF(format string, args ...interface{}) { 88 | logger.Debug(fmt.Sprintf(format, args...)) 89 | } 90 | 91 | // ErrorF print good message 92 | func ErrorF(format string, args ...interface{}) { 93 | logger.Error(fmt.Sprintf(format, args...)) 94 | } 95 | -------------------------------------------------------------------------------- /core/filter.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import "regexp" 4 | 5 | // FilterIpv4 only get Ipv4 6 | func FilterIpv4(raw []string) []string { 7 | var result []string 8 | var re = regexp.MustCompile(`(?m)^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$`) 9 | for _, input := range raw { 10 | match := re.MatchString(input) 11 | if match { 12 | result = append(result, input) 13 | } 14 | } 15 | return result 16 | } 17 | 18 | // FilterIpv6 only get Ipv6 19 | func FilterIpv6(raw []string) []string { 20 | var result []string 21 | var re = regexp.MustCompile(`^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$`) 22 | for _, input := range raw { 23 | match := re.MatchString(input) 24 | if match { 25 | result = append(result, input) 26 | } 27 | } 28 | return result 29 | } 30 | -------------------------------------------------------------------------------- /core/helper.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "archive/zip" 5 | "bufio" 6 | "crypto/sha1" 7 | "encoding/base64" 8 | "encoding/json" 9 | "fmt" 10 | "io" 11 | "net/url" 12 | "os" 13 | "path/filepath" 14 | "regexp" 15 | "strconv" 16 | "strings" 17 | "time" 18 | 19 | "github.com/mitchellh/go-homedir" 20 | ) 21 | 22 | // GetFileContent reads file and returns its content. 23 | func GetFileContent(filename string) string { 24 | var result strings.Builder 25 | 26 | if strings.Contains(filename, "~") { 27 | var err error 28 | filename, err = homedir.Expand(filename) 29 | if err != nil { 30 | return "" 31 | } 32 | } 33 | 34 | file, err := os.Open(filename) 35 | if err != nil { 36 | return "" 37 | } 38 | defer file.Close() 39 | 40 | // Create a buffer to store file content 41 | buf := make([]byte, 1024) 42 | 43 | // Read file content into the buffer 44 | for { 45 | n, err := file.Read(buf) 46 | if err != nil && err != io.EOF { 47 | return "" 48 | } 49 | if n == 0 { 50 | break 51 | } 52 | result.Write(buf[:n]) 53 | } 54 | 55 | return result.String() 56 | } 57 | 58 | // ReadingFile Reading file and return content as []string 59 | func ReadingFile(filename string) []string { 60 | var result []string 61 | if strings.HasPrefix(filename, "~") { 62 | filename, _ = homedir.Expand(filename) 63 | } 64 | file, err := os.Open(filename) 65 | defer file.Close() 66 | if err != nil { 67 | return result 68 | } 69 | 70 | scanner := bufio.NewScanner(file) 71 | for scanner.Scan() { 72 | val := scanner.Text() 73 | result = append(result, val) 74 | } 75 | 76 | if err := scanner.Err(); err != nil { 77 | return result 78 | } 79 | return result 80 | } 81 | 82 | // ReadingFileUnique Reading file and return content as []string 83 | func ReadingFileUnique(filename string) []string { 84 | var result []string 85 | if strings.Contains(filename, "~") { 86 | filename, _ = homedir.Expand(filename) 87 | } 88 | file, err := os.Open(filename) 89 | defer file.Close() 90 | if err != nil { 91 | return result 92 | } 93 | 94 | unique := true 95 | seen := make(map[string]bool) 96 | 97 | scanner := bufio.NewScanner(file) 98 | for scanner.Scan() { 99 | val := scanner.Text() 100 | // unique stuff 101 | if val == "" { 102 | continue 103 | } 104 | val = strings.TrimSpace(val) 105 | if seen[val] && unique { 106 | continue 107 | } 108 | 109 | if unique { 110 | seen[val] = true 111 | result = append(result, val) 112 | } 113 | } 114 | 115 | if err := scanner.Err(); err != nil { 116 | return result 117 | } 118 | return result 119 | } 120 | 121 | // WriteToFile write string to a file 122 | func WriteToFile(filename string, data string) (string, error) { 123 | file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) 124 | if err != nil { 125 | return "", err 126 | } 127 | defer file.Close() 128 | 129 | _, err = io.WriteString(file, data+"\n") 130 | if err != nil { 131 | return "", err 132 | } 133 | return filename, file.Sync() 134 | } 135 | 136 | // Unique unique content of a file and remove blank line 137 | func Unique(filename string) { 138 | if filename == "" { 139 | return 140 | } 141 | DebugF("Unique Output: %v", filename) 142 | data := ReadingFileUnique(filename) 143 | WriteToFile(filename, strings.Join(data, "\n")) 144 | } 145 | 146 | // AppendToContent append string to a file 147 | func AppendToContent(filename string, data string) (string, error) { 148 | // If the file doesn't exist, create it, or append to the file 149 | f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 150 | if err != nil { 151 | return "", err 152 | } 153 | if _, err := f.Write([]byte(data + "\n")); err != nil { 154 | return "", err 155 | } 156 | if err := f.Close(); err != nil { 157 | return "", err 158 | } 159 | return filename, nil 160 | } 161 | 162 | // FileExists check if file is exist or not 163 | func FileExists(filename string) bool { 164 | info, err := os.Stat(filename) 165 | if os.IsNotExist(err) { 166 | return false 167 | } 168 | return !info.IsDir() 169 | } 170 | 171 | // FolderExists check if file is exist or not 172 | func FolderExists(foldername string) bool { 173 | if _, err := os.Stat(foldername); os.IsNotExist(err) { 174 | return false 175 | } 176 | return true 177 | } 178 | 179 | // GetFileNames get all file name with extension 180 | func GetFileNames(dir string, ext string) []string { 181 | if _, err := os.Stat(dir); os.IsNotExist(err) { 182 | return nil 183 | } 184 | 185 | var files []string 186 | filepath.Walk(dir, func(path string, f os.FileInfo, _ error) error { 187 | if !f.IsDir() { 188 | if strings.HasSuffix(f.Name(), ext) { 189 | filename, _ := filepath.Abs(path) 190 | files = append(files, filename) 191 | } 192 | } 193 | return nil 194 | }) 195 | return files 196 | } 197 | 198 | // IsJSON check if string is JSON or not 199 | func IsJSON(str string) bool { 200 | var js json.RawMessage 201 | return json.Unmarshal([]byte(str), &js) == nil 202 | } 203 | 204 | // GetTS get current timestamp and return a string 205 | func GetTS() string { 206 | return strconv.FormatInt(time.Now().Unix(), 10) 207 | } 208 | 209 | // GenHash gen SHA1 hash from string 210 | func GenHash(text string) string { 211 | h := sha1.New() 212 | h.Write([]byte(text)) 213 | hashed := h.Sum(nil) 214 | return fmt.Sprintf("%x", hashed) 215 | } 216 | 217 | // Unzip will decompress a zip archive, moving all files and folders 218 | // within the zip file (parameter 1) to an output directory (parameter 2). 219 | func Unzip(src string, dest string) ([]string, error) { 220 | 221 | var filenames []string 222 | 223 | r, err := zip.OpenReader(src) 224 | if err != nil { 225 | return filenames, err 226 | } 227 | defer r.Close() 228 | 229 | for _, f := range r.File { 230 | 231 | // Store filename/path for returning and using later on 232 | fpath := filepath.Join(dest, f.Name) 233 | 234 | if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { 235 | return filenames, fmt.Errorf("%s: illegal file path", fpath) 236 | } 237 | 238 | filenames = append(filenames, fpath) 239 | 240 | if f.FileInfo().IsDir() { 241 | // Make Folder 242 | os.MkdirAll(fpath, os.ModePerm) 243 | continue 244 | } 245 | 246 | // Make File 247 | if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { 248 | return filenames, err 249 | } 250 | 251 | outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) 252 | if err != nil { 253 | return filenames, err 254 | } 255 | 256 | rc, err := f.Open() 257 | if err != nil { 258 | return filenames, err 259 | } 260 | 261 | _, err = io.Copy(outFile, rc) 262 | 263 | // Close the file without defer to close before next iteration of loop 264 | outFile.Close() 265 | rc.Close() 266 | 267 | if err != nil { 268 | return filenames, err 269 | } 270 | } 271 | return filenames, nil 272 | } 273 | 274 | // ExpandLength make slice to length 275 | func ExpandLength(list []string, length int) []string { 276 | c := []string{} 277 | for i := 1; i <= length; i++ { 278 | c = append(c, list[i%len(list)]) 279 | } 280 | return c 281 | } 282 | 283 | // StartWithNum check if string start with number 284 | func StartWithNum(raw string) bool { 285 | r, err := regexp.Compile("^[0-9].*") 286 | if err != nil { 287 | return false 288 | } 289 | return r.MatchString(raw) 290 | } 291 | 292 | // StripPath just strip some invalid string path 293 | func StripPath(raw string) string { 294 | raw = strings.Replace(raw, "/", "_", -1) 295 | raw = strings.Replace(raw, " ", "_", -1) 296 | return raw 297 | } 298 | 299 | // Base64Encode just Base64 Encode 300 | func Base64Encode(raw string) string { 301 | return base64.StdEncoding.EncodeToString([]byte(raw)) 302 | } 303 | 304 | // Base64Decode just Base64 Encode 305 | func Base64Decode(raw string) string { 306 | data, err := base64.StdEncoding.DecodeString(raw) 307 | if err != nil { 308 | return raw 309 | } 310 | return string(data) 311 | } 312 | 313 | // URLDecode decode url 314 | func URLDecode(raw string) string { 315 | decodedValue, err := url.QueryUnescape(raw) 316 | if err != nil { 317 | return raw 318 | } 319 | return decodedValue 320 | } 321 | 322 | // URLEncode Encode query 323 | func URLEncode(raw string) string { 324 | decodedValue := url.QueryEscape(raw) 325 | return decodedValue 326 | } 327 | 328 | // GenPorts gen list of ports based on input 329 | func GenPorts(raw string) []string { 330 | var ports []string 331 | if strings.Contains(raw, ",") { 332 | items := strings.Split(raw, ",") 333 | for _, item := range items { 334 | if strings.Contains(item, "-") { 335 | min, err := strconv.Atoi(strings.Split(item, "-")[0]) 336 | if err != nil { 337 | continue 338 | } 339 | max, err := strconv.Atoi(strings.Split(item, "-")[1]) 340 | if err != nil { 341 | continue 342 | } 343 | for i := min; i <= max; i++ { 344 | ports = append(ports, fmt.Sprintf("%v", i)) 345 | } 346 | } else { 347 | ports = append(ports, item) 348 | } 349 | } 350 | } else { 351 | if strings.Contains(raw, "-") { 352 | min, err := strconv.Atoi(strings.Split(raw, "-")[0]) 353 | if err != nil { 354 | return ports 355 | } 356 | max, err := strconv.Atoi(strings.Split(raw, "-")[1]) 357 | if err != nil { 358 | return ports 359 | } 360 | for i := min; i <= max; i++ { 361 | ports = append(ports, fmt.Sprintf("%v", i)) 362 | } 363 | } else { 364 | ports = append(ports, raw) 365 | } 366 | } 367 | 368 | return ports 369 | } 370 | -------------------------------------------------------------------------------- /core/options.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | // Options global options 4 | type Options struct { 5 | Input string 6 | InputFile string 7 | 8 | Inputs []string 9 | Output string 10 | TmpOutput string 11 | ConfigFile string 12 | LogFile string 13 | Proxy string 14 | 15 | Concurrency int 16 | Delay int 17 | SaveRaw bool 18 | Timeout int 19 | Retry int 20 | JsonOutput bool 21 | PipeTheOutput bool 22 | Verbose bool 23 | Quiet bool 24 | Debug bool 25 | Scan ScanOptions 26 | Net NetOptions 27 | Search SearchOptions 28 | CVE CVEOptions 29 | Cert CertOptions 30 | Tld TldOptions 31 | } 32 | 33 | // TldOptions options for tld command 34 | type TldOptions struct { 35 | Source string 36 | } 37 | 38 | // CertOptions options for cert command 39 | type CertOptions struct { 40 | Clean bool 41 | OnlyWildCard bool 42 | } 43 | 44 | // ScanOptions options for net command 45 | type ScanOptions struct { 46 | Ports string 47 | Rate string 48 | Retry string 49 | Timeout string 50 | NmapTemplate string 51 | NmapOverview bool 52 | ZmapOverview bool 53 | Detail bool 54 | Flat bool 55 | All bool 56 | IPv4 bool 57 | IPv6 bool 58 | Skip80And443 bool 59 | SkipOverview bool 60 | InputFromRustScan bool 61 | TmpOutput string 62 | NmapScripts string 63 | GrepString string 64 | InputFile string 65 | } 66 | 67 | // NetOptions options for net command 68 | type NetOptions struct { 69 | Asn string 70 | Org string 71 | IP string 72 | Domain string 73 | SearchType string 74 | Optimize bool 75 | ExactMatch bool 76 | } 77 | 78 | // SearchOptions options for net command 79 | type SearchOptions struct { 80 | Source string 81 | Query string 82 | Optimize bool 83 | More bool 84 | } 85 | 86 | // CVEOptions options for cve command 87 | type CVEOptions struct { 88 | Software string 89 | Version string 90 | } 91 | 92 | // Request all information about request 93 | type Request struct { 94 | Timeout int 95 | Repeat int 96 | Scheme string 97 | Host string 98 | Port string 99 | Path string 100 | URL string 101 | Proxy string 102 | Method string 103 | Redirect bool 104 | Headers []map[string]string 105 | Body string 106 | Beautify string 107 | } 108 | 109 | // Response all information about response 110 | type Response struct { 111 | HasPopUp bool 112 | StatusCode int 113 | Status string 114 | Headers []map[string]string 115 | Body string 116 | ResponseTime float64 117 | Length int 118 | Beautify string 119 | BeautifyHeader string 120 | } 121 | 122 | type RelatedDomain struct { 123 | Domain string 124 | RawData string 125 | Technique string 126 | Source string 127 | Output string 128 | } 129 | -------------------------------------------------------------------------------- /core/request.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "io" 7 | "net" 8 | "net/http" 9 | "strconv" 10 | "strings" 11 | "time" 12 | 13 | "github.com/go-resty/resty/v2" 14 | ) 15 | 16 | var headers map[string]string 17 | 18 | // SendGET just send GET request 19 | func SendGET(url string, options Options) string { 20 | headers = map[string]string{ 21 | "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36", 22 | "Accept-Encoding": "*/*", 23 | "Accept-Language": "en-US,en;q=0.8", 24 | } 25 | resp, _ := JustSend(options, "GET", url, headers, "") 26 | return resp.Body 27 | } 28 | 29 | func GetResponse(baseUrl string, options Options) (string, error) { 30 | content, err := getResponse(baseUrl, options) 31 | // simple retry if something went wrong 32 | for i := 0; i < options.Retry; i++ { 33 | if err != nil { 34 | content, err = getResponse(baseUrl, options) 35 | } else { 36 | return content, err 37 | } 38 | } 39 | 40 | if err != nil { 41 | content, err = getResponse(baseUrl, options) 42 | } 43 | return content, err 44 | } 45 | 46 | func getResponse(baseUrl string, options Options) (string, error) { 47 | client := &http.Client{ 48 | Timeout: time.Duration(options.Timeout*3) * time.Second, 49 | Transport: &http.Transport{ 50 | DialContext: (&net.Dialer{ 51 | Timeout: time.Second * 60, 52 | }).DialContext, 53 | MaxIdleConns: 1000, 54 | MaxIdleConnsPerHost: 500, 55 | MaxConnsPerHost: 500, 56 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true, Renegotiation: tls.RenegotiateOnceAsClient}, 57 | }, 58 | CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, 59 | } 60 | 61 | req, _ := http.NewRequest("GET", baseUrl, nil) 62 | req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36") 63 | resp, err := client.Do(req) 64 | if err != nil { 65 | ErrorF("%v", err) 66 | return "", err 67 | } 68 | defer resp.Body.Close() 69 | 70 | // Create a buffer to store response body 71 | var bodyBuilder strings.Builder 72 | 73 | // Read response body into the buffer 74 | _, err = io.Copy(&bodyBuilder, resp.Body) 75 | if err != nil { 76 | ErrorF("%v", err) 77 | return "", err 78 | } 79 | return bodyBuilder.String(), nil 80 | } 81 | 82 | // SendPOST just send POST request 83 | func SendPOST(url string, options Options) string { 84 | resp, _ := JustSend(options, "POST", url, headers, "") 85 | return resp.Body 86 | } 87 | 88 | // JustSend just sending request 89 | func JustSend(options Options, method string, url string, headers map[string]string, body string) (res Response, err error) { 90 | timeout := options.Timeout 91 | 92 | client := resty.New() 93 | client.SetTransport(&http.Transport{ 94 | MaxIdleConns: 100, 95 | MaxConnsPerHost: 1000, 96 | IdleConnTimeout: time.Duration(timeout) * time.Second, 97 | ExpectContinueTimeout: time.Duration(timeout) * time.Second, 98 | ResponseHeaderTimeout: time.Duration(timeout) * time.Second, 99 | TLSHandshakeTimeout: time.Duration(timeout) * time.Second, 100 | DisableCompression: true, 101 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 102 | }) 103 | 104 | client.SetHeaders(headers) 105 | client.SetCloseConnection(true) 106 | 107 | client.SetTimeout(time.Duration(timeout) * time.Second) 108 | client.SetRetryWaitTime(time.Duration(timeout/2) * time.Second) 109 | client.SetRetryMaxWaitTime(time.Duration(timeout) * time.Second) 110 | 111 | if options.Proxy != "" { 112 | client.SetProxy(options.Proxy) 113 | } 114 | 115 | var resp *resty.Response 116 | // really sending things here 117 | method = strings.ToLower(strings.TrimSpace(method)) 118 | switch method { 119 | case "get": 120 | resp, err = client.R(). 121 | Get(url) 122 | break 123 | case "post": 124 | resp, err = client.R(). 125 | SetBody([]byte(body)). 126 | Post(url) 127 | break 128 | } 129 | 130 | // in case we want to get redirect stuff 131 | if res.StatusCode != 0 { 132 | return res, nil 133 | } 134 | 135 | if err != nil || resp == nil { 136 | ErrorF("%v %v", url, err) 137 | return res, err 138 | } 139 | 140 | return ParseResponse(*resp), nil 141 | } 142 | 143 | // ParseResponse field to Response 144 | func ParseResponse(resp resty.Response) (res Response) { 145 | // var res libs.Response 146 | resLength := len(string(resp.Body())) 147 | // format the headers 148 | var resHeaders []map[string]string 149 | for k, v := range resp.RawResponse.Header { 150 | element := make(map[string]string) 151 | element[k] = strings.Join(v[:], "") 152 | resLength += len(fmt.Sprintf("%s: %s\n", k, strings.Join(v[:], ""))) 153 | resHeaders = append(resHeaders, element) 154 | } 155 | // response time in second 156 | resTime := float64(resp.Time()) / float64(time.Second) 157 | resHeaders = append(resHeaders, 158 | map[string]string{"Total Length": strconv.Itoa(resLength)}, 159 | map[string]string{"Response Time": fmt.Sprintf("%f", resTime)}, 160 | ) 161 | 162 | // set some variable 163 | res.Headers = resHeaders 164 | res.StatusCode = resp.StatusCode() 165 | res.Status = fmt.Sprintf("%v %v", resp.Status(), resp.RawResponse.Proto) 166 | res.Body = string(resp.Body()) 167 | res.ResponseTime = resTime 168 | res.Length = resLength 169 | // beautify 170 | res.Beautify = BeautifyResponse(res) 171 | res.BeautifyHeader = BeautifyHeaders(res) 172 | return res 173 | } 174 | 175 | // BeautifyHeaders beautify response headers 176 | func BeautifyHeaders(res Response) string { 177 | beautifyHeader := fmt.Sprintf("%v \n", res.Status) 178 | for _, header := range res.Headers { 179 | for key, value := range header { 180 | beautifyHeader += fmt.Sprintf("%v: %v\n", key, value) 181 | } 182 | } 183 | return beautifyHeader 184 | } 185 | 186 | // BeautifyResponse beautify response 187 | func BeautifyResponse(res Response) string { 188 | var beautifyRes string 189 | beautifyRes += fmt.Sprintf("%v \n", res.Status) 190 | 191 | for _, header := range res.Headers { 192 | for key, value := range header { 193 | beautifyRes += fmt.Sprintf("%v: %v\n", key, value) 194 | } 195 | } 196 | 197 | beautifyRes += fmt.Sprintf("\n%v\n", res.Body) 198 | return beautifyRes 199 | } 200 | -------------------------------------------------------------------------------- /core/version.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import "fmt" 4 | 5 | const ( 6 | // VERSION current version of this project 7 | VERSION = "v2.0.0" 8 | // AUTHOR of this project 9 | AUTHOR = "@j3ssiejjj" 10 | ) 11 | 12 | var DESC = fmt.Sprintf(`Metabigor - OSINT tools and more but without API key - %v by %v`, VERSION, AUTHOR) 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/j3ssie/metabigor 2 | 3 | go 1.21.6 4 | 5 | require ( 6 | github.com/PuerkitoBio/goquery v1.9.1 7 | github.com/chromedp/chromedp v0.9.5 8 | github.com/davecgh/go-spew v1.1.1 9 | github.com/fatih/color v1.16.0 10 | github.com/go-resty/resty/v2 v2.11.0 11 | github.com/json-iterator/go v1.1.12 12 | github.com/mitchellh/go-homedir v1.1.0 13 | github.com/panjf2000/ants v1.3.0 14 | github.com/pkg/errors v0.9.1 15 | github.com/projectdiscovery/asnmap v1.1.0 16 | github.com/projectdiscovery/mapcidr v1.1.16 17 | github.com/sirupsen/logrus v1.9.3 18 | github.com/spf13/cast v1.6.0 19 | github.com/spf13/cobra v1.8.0 20 | github.com/thoas/go-funk v0.9.3 21 | github.com/x-cray/logrus-prefixed-formatter v0.5.2 22 | ) 23 | 24 | require ( 25 | aead.dev/minisign v0.2.0 // indirect 26 | github.com/Masterminds/semver/v3 v3.2.1 // indirect 27 | github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057 // indirect 28 | github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809 // indirect 29 | github.com/VividCortex/ewma v1.2.0 // indirect 30 | github.com/akrylysov/pogreb v0.10.1 // indirect 31 | github.com/alecthomas/chroma v0.10.0 // indirect 32 | github.com/andybalholm/brotli v1.0.6 // indirect 33 | github.com/andybalholm/cascadia v1.3.2 // indirect 34 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect 35 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 36 | github.com/aymerick/douceur v0.2.0 // indirect 37 | github.com/charmbracelet/glamour v0.6.0 // indirect 38 | github.com/cheggaaa/pb/v3 v3.1.4 // indirect 39 | github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732 // indirect 40 | github.com/chromedp/sysutil v1.0.0 // indirect 41 | github.com/cloudflare/circl v1.3.7 // indirect 42 | github.com/dimchansky/utfbom v1.1.1 // indirect 43 | github.com/dlclark/regexp2 v1.8.1 // indirect 44 | github.com/docker/go-units v0.5.0 // indirect 45 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect 46 | github.com/gaukas/godicttls v0.0.4 // indirect 47 | github.com/go-ole/go-ole v1.2.6 // indirect 48 | github.com/gobwas/httphead v0.1.0 // indirect 49 | github.com/gobwas/pool v0.2.1 // indirect 50 | github.com/gobwas/ws v1.3.2 // indirect 51 | github.com/golang/protobuf v1.5.3 // indirect 52 | github.com/golang/snappy v0.0.4 // indirect 53 | github.com/google/go-github/v30 v30.1.0 // indirect 54 | github.com/google/go-querystring v1.1.0 // indirect 55 | github.com/gorilla/css v1.0.0 // indirect 56 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 57 | github.com/josharian/intern v1.0.0 // indirect 58 | github.com/klauspost/compress v1.16.7 // indirect 59 | github.com/klauspost/pgzip v1.2.5 // indirect 60 | github.com/logrusorgru/aurora v2.0.3+incompatible // indirect 61 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 62 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect 63 | github.com/mailru/easyjson v0.7.7 // indirect 64 | github.com/mattn/go-colorable v0.1.13 // indirect 65 | github.com/mattn/go-isatty v0.0.20 // indirect 66 | github.com/mattn/go-runewidth v0.0.14 // indirect 67 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect 68 | github.com/mholt/archiver/v3 v3.5.1 // indirect 69 | github.com/microcosm-cc/bluemonday v1.0.25 // indirect 70 | github.com/miekg/dns v1.1.56 // indirect 71 | github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 // indirect 72 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 73 | github.com/modern-go/reflect2 v1.0.2 // indirect 74 | github.com/muesli/reflow v0.3.0 // indirect 75 | github.com/muesli/termenv v0.15.1 // indirect 76 | github.com/nwaples/rardecode v1.1.3 // indirect 77 | github.com/olekukonko/tablewriter v0.0.5 // indirect 78 | github.com/pierrec/lz4/v4 v4.1.2 // indirect 79 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect 80 | github.com/projectdiscovery/blackrock v0.0.1 // indirect 81 | github.com/projectdiscovery/fastdialer v0.0.59 // indirect 82 | github.com/projectdiscovery/gologger v1.1.12 // indirect 83 | github.com/projectdiscovery/hmap v0.0.40 // indirect 84 | github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 // indirect 85 | github.com/projectdiscovery/networkpolicy v0.0.7 // indirect 86 | github.com/projectdiscovery/retryabledns v1.0.57 // indirect 87 | github.com/projectdiscovery/retryablehttp-go v1.0.48 // indirect 88 | github.com/projectdiscovery/utils v0.0.81 // indirect 89 | github.com/quic-go/quic-go v0.37.7 // indirect 90 | github.com/refraction-networking/utls v1.5.4 // indirect 91 | github.com/rivo/uniseg v0.4.4 // indirect 92 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect 93 | github.com/shirou/gopsutil/v3 v3.23.7 // indirect 94 | github.com/shoenig/go-m1cpu v0.1.6 // indirect 95 | github.com/spf13/pflag v1.0.5 // indirect 96 | github.com/syndtr/goleveldb v1.0.0 // indirect 97 | github.com/tidwall/btree v1.6.0 // indirect 98 | github.com/tidwall/buntdb v1.3.0 // indirect 99 | github.com/tidwall/gjson v1.14.4 // indirect 100 | github.com/tidwall/grect v0.1.4 // indirect 101 | github.com/tidwall/match v1.1.1 // indirect 102 | github.com/tidwall/pretty v1.2.1 // indirect 103 | github.com/tidwall/rtred v0.1.2 // indirect 104 | github.com/tidwall/tinyqueue v0.1.1 // indirect 105 | github.com/tklauser/go-sysconf v0.3.11 // indirect 106 | github.com/tklauser/numcpus v0.6.0 // indirect 107 | github.com/ulikunitz/xz v0.5.11 // indirect 108 | github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6 // indirect 109 | github.com/weppos/publicsuffix-go v0.30.1-0.20230422193905-8fecedd899db // indirect 110 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect 111 | github.com/yl2chen/cidranger v1.0.2 // indirect 112 | github.com/yuin/goldmark v1.5.4 // indirect 113 | github.com/yuin/goldmark-emoji v1.0.1 // indirect 114 | github.com/yusufpapurcu/wmi v1.2.3 // indirect 115 | github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 // indirect 116 | github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968 // indirect 117 | go.etcd.io/bbolt v1.3.7 // indirect 118 | go.uber.org/multierr v1.11.0 // indirect 119 | golang.org/x/crypto v0.19.0 // indirect 120 | golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect 121 | golang.org/x/mod v0.12.0 // indirect 122 | golang.org/x/net v0.21.0 // indirect 123 | golang.org/x/oauth2 v0.11.0 // indirect 124 | golang.org/x/sys v0.17.0 // indirect 125 | golang.org/x/term v0.17.0 // indirect 126 | golang.org/x/text v0.14.0 // indirect 127 | golang.org/x/tools v0.13.0 // indirect 128 | google.golang.org/appengine v1.6.7 // indirect 129 | google.golang.org/protobuf v1.31.0 // indirect 130 | gopkg.in/djherbis/times.v1 v1.3.0 // indirect 131 | gopkg.in/yaml.v3 v3.0.1 // indirect 132 | ) 133 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk= 2 | aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ= 3 | cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= 4 | github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= 5 | github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= 6 | github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057 h1:KFac3SiGbId8ub47e7kd2PLZeACxc1LkiiNoDOFRClE= 7 | github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057/go.mod h1:iLB2pivrPICvLOuROKmlqURtFIEsoJZaMidQfCG1+D4= 8 | github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809 h1:ZbFL+BDfBqegi+/Ssh7im5+aQfBRx6it+kHnC7jaDU8= 9 | github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809/go.mod h1:upgc3Zs45jBDnBT4tVRgRcgm26ABpaP7MoTSdgysca4= 10 | github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI= 11 | github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY= 12 | github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= 13 | github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= 14 | github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w= 15 | github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI= 16 | github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= 17 | github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= 18 | github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 19 | github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= 20 | github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 21 | github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= 22 | github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= 23 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= 24 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 25 | github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= 26 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= 27 | github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 28 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= 29 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= 30 | github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= 31 | github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 32 | github.com/bits-and-blooms/bloom/v3 v3.5.0 h1:AKDvi1V3xJCmSR6QhcBfHbCN4Vf8FfxeWkMNQfmAGhY= 33 | github.com/bits-and-blooms/bloom/v3 v3.5.0/go.mod h1:Y8vrn7nk1tPIlmLtW2ZPV+W7StdVMor6bC1xgpjMZFs= 34 | github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= 35 | github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= 36 | github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo= 37 | github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA= 38 | github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732 h1:XYUCaZrW8ckGWlCRJKCSoh/iFwlpX316a8yY9IFEzv8= 39 | github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= 40 | github.com/chromedp/chromedp v0.9.5 h1:viASzruPJOiThk7c5bueOUY91jGLJVximoEMGoH93rg= 41 | github.com/chromedp/chromedp v0.9.5/go.mod h1:D4I2qONslauw/C7INoCir1BJkSwBYMyZgx8X276z3+Y= 42 | github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= 43 | github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= 44 | github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= 45 | github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= 46 | github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 47 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 48 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 49 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 50 | github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= 51 | github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= 52 | github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= 53 | github.com/dlclark/regexp2 v1.8.1 h1:6Lcdwya6GjPUNsBct8Lg/yRPwMhABj269AAzdGSiR+0= 54 | github.com/dlclark/regexp2 v1.8.1/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 55 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 56 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 57 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= 58 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= 59 | github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 60 | github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= 61 | github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 62 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 63 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 64 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 65 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 66 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 67 | github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= 68 | github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= 69 | github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= 70 | github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 71 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 72 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 73 | github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= 74 | github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= 75 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= 76 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 77 | github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= 78 | github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= 79 | github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= 80 | github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= 81 | github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q= 82 | github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= 83 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 84 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 85 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 86 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 87 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 88 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 89 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 90 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 91 | github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 92 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 93 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 94 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 95 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 96 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 97 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 98 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 99 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 100 | github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo= 101 | github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8= 102 | github.com/google/go-github/v50 v50.1.0/go.mod h1:Ev4Tre8QoKiolvbpOSG3FIi4Mlon3S2Nt9W5JYqKiwA= 103 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 104 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 105 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 106 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 107 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= 108 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 109 | github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= 110 | github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= 111 | github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM= 112 | github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 113 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 114 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 115 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 116 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 117 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 118 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 119 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 120 | github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= 121 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 122 | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 123 | github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 124 | github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= 125 | github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 126 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 127 | github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= 128 | github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 129 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 130 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 131 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 132 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 133 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 134 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 135 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 136 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 137 | github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= 138 | github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= 139 | github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= 140 | github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= 141 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 142 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 143 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= 144 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 145 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 146 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 147 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 148 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 149 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 150 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 151 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 152 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 153 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 154 | github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= 155 | github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 156 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= 157 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 158 | github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= 159 | github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= 160 | github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= 161 | github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= 162 | github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= 163 | github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= 164 | github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= 165 | github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 h1:yRZGarbxsRytL6EGgbqK2mCY+Lk5MWKQYKJT2gEglhc= 166 | github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM= 167 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 168 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 169 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 170 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 171 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 172 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 173 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 174 | github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= 175 | github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= 176 | github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= 177 | github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= 178 | github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= 179 | github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs= 180 | github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ= 181 | github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 182 | github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= 183 | github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 184 | github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= 185 | github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= 186 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 187 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 188 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 189 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 190 | github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= 191 | github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 192 | github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= 193 | github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= 194 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 195 | github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= 196 | github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= 197 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 198 | github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= 199 | github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= 200 | github.com/panjf2000/ants v1.3.0 h1:8pQ+8leaLc9lys2viEEr8md0U4RN6uOSUCE9bOYjQ9M= 201 | github.com/panjf2000/ants v1.3.0/go.mod h1:AaACblRPzq35m1g3enqYcxspbbiOJJYaxU2wMpm1cXY= 202 | github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= 203 | github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 204 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 205 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 206 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 207 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 208 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= 209 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 210 | github.com/projectdiscovery/asnmap v1.1.0 h1:ynvbLB5cNpyQ2+k9IP0Rpla+0JmCJpd3mw6KLAW13m0= 211 | github.com/projectdiscovery/asnmap v1.1.0/go.mod h1:QNjBnGLxUBEZAgaYk/Av5cjKKWFY3i/FOfoIWCUApoY= 212 | github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k0VbGJyft6LQ= 213 | github.com/projectdiscovery/blackrock v0.0.1/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss= 214 | github.com/projectdiscovery/fastdialer v0.0.59 h1:5D0ws7JsYYMC8lm2VKgf91q0kAk6dzqoSHmLIuA2mww= 215 | github.com/projectdiscovery/fastdialer v0.0.59/go.mod h1:VdYQimFCHafSPP3c+OXgu1DP+FNLJq+U6Xk/ybfJ0/A= 216 | github.com/projectdiscovery/gologger v1.1.12 h1:uX/QkQdip4PubJjjG0+uk5DtyAi1ANPJUvpmimXqv4A= 217 | github.com/projectdiscovery/gologger v1.1.12/go.mod h1:DI8nywPLERS5mo8QEA9E7gd5HZ3Je14SjJBH3F5/kLw= 218 | github.com/projectdiscovery/hmap v0.0.40 h1:WGAIXXMY2vbV0ep7Q8s27Up/ejs8Wo1hh5AEhynLfmw= 219 | github.com/projectdiscovery/hmap v0.0.40/go.mod h1:5JkQW9t/UNK95YEY6irOXHqrS/xVBUtrYEhtq61ttII= 220 | github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 h1:ZScLodGSezQVwsQDtBSMFp72WDq0nNN+KE/5DHKY5QE= 221 | github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983/go.mod h1:3G3BRKui7nMuDFAZKR/M2hiOLtaOmyukT20g88qRQjI= 222 | github.com/projectdiscovery/mapcidr v1.1.16 h1:rjj1w5D6hbTsUQXYClLcGdfBEy9bryclgi70t0vBggo= 223 | github.com/projectdiscovery/mapcidr v1.1.16/go.mod h1:rGqpBhStdwOQ2uS62QM9qPsybwMwIhT7CTd2bxoHs8Q= 224 | github.com/projectdiscovery/networkpolicy v0.0.7 h1:AwHqBRXBqDQgnWzBMuoJtHBNEYBw+NFp/4qIK688x7o= 225 | github.com/projectdiscovery/networkpolicy v0.0.7/go.mod h1:CK0CnFoLF1Nou6mY7P4WODSAxhPN8g8g7XpapgEP8tI= 226 | github.com/projectdiscovery/retryabledns v1.0.57 h1:+DOL9xYSIx74FRrOIKKHVp5R9ci53xHQN3jnncWVds4= 227 | github.com/projectdiscovery/retryabledns v1.0.57/go.mod h1:qIigOcmO9d0Ce/z6mHzLl0Aiz2WJcNk2gUGhRcCQ1k4= 228 | github.com/projectdiscovery/retryablehttp-go v1.0.48 h1:/f1JPQyti7NQOVUI44IjP514q39+6RR1NDAUr9QhqkA= 229 | github.com/projectdiscovery/retryablehttp-go v1.0.48/go.mod h1:iJwvFiUBA8DmVIk0dP8r9+kqLvnXEe3W+g/hcZofkWY= 230 | github.com/projectdiscovery/utils v0.0.81 h1:Cqz6uFncCKWRLqpVHWlnHXaRE3whzH32yZJa/1zOEzU= 231 | github.com/projectdiscovery/utils v0.0.81/go.mod h1:pTGvF08EXa07e2OM+tu8IcnxTeAT34bzAhSW/Efcens= 232 | github.com/quic-go/quic-go v0.37.7 h1:AgKsQLZ1+YCwZd2GYhBUsJDYZwEkA5gENtAjb+MxONU= 233 | github.com/quic-go/quic-go v0.37.7/go.mod h1:YsbH1r4mSHPJcLF4k4zruUkLBqctEMBDR6VPvcYjIsU= 234 | github.com/refraction-networking/utls v1.5.4 h1:9k6EO2b8TaOGsQ7Pl7p9w6PUhx18/ZCeT0WNTZ7Uw4o= 235 | github.com/refraction-networking/utls v1.5.4/go.mod h1:SPuDbBmgLGp8s+HLNc83FuavwZCFoMmExj+ltUHiHUw= 236 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 237 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 238 | github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= 239 | github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 240 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 241 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 242 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 243 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= 244 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= 245 | github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= 246 | github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4= 247 | github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= 248 | github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= 249 | github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= 250 | github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= 251 | github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 252 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 253 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 254 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 255 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 256 | github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= 257 | github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 258 | github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= 259 | github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= 260 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 261 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 262 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 263 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 264 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 265 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 266 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 267 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 268 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 269 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 270 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 271 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 272 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 273 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 274 | github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= 275 | github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= 276 | github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw= 277 | github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= 278 | github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= 279 | github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8= 280 | github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= 281 | github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= 282 | github.com/tidwall/buntdb v1.3.0 h1:gdhWO+/YwoB2qZMeAU9JcWWsHSYU3OvcieYgFRS0zwA= 283 | github.com/tidwall/buntdb v1.3.0/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU= 284 | github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 285 | github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= 286 | github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 287 | github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg= 288 | github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= 289 | github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8= 290 | github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= 291 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 292 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 293 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 294 | github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= 295 | github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 296 | github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= 297 | github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= 298 | github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= 299 | github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= 300 | github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= 301 | github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= 302 | github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= 303 | github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= 304 | github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 305 | github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 306 | github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= 307 | github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 308 | github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6 h1:TtyC78WMafNW8QFfv3TeP3yWNDG+uxNkk9vOrnDu6JA= 309 | github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6/go.mod h1:h8272+G2omSmi30fBXiZDMkmHuOgonplfKIKjQWzlfs= 310 | github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= 311 | github.com/weppos/publicsuffix-go v0.30.1-0.20230422193905-8fecedd899db h1:/WcxBne+5CbtbgWd/sV2wbravmr4sT7y52ifQaCgoLs= 312 | github.com/weppos/publicsuffix-go v0.30.1-0.20230422193905-8fecedd899db/go.mod h1:aiQaH1XpzIfgrJq3S1iw7w+3EDbRP7mF5fmwUhWyRUs= 313 | github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= 314 | github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= 315 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= 316 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= 317 | github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU= 318 | github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g= 319 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 320 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 321 | github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 322 | github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= 323 | github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 324 | github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= 325 | github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= 326 | github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= 327 | github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 328 | github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= 329 | github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 h1:Nzukz5fNOBIHOsnP+6I79kPx3QhLv8nBy2mfFhBRq30= 330 | github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= 331 | github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= 332 | github.com/zmap/zcertificate v0.0.1/go.mod h1:q0dlN54Jm4NVSSuzisusQY0hqDWvu92C+TWveAxiVWk= 333 | github.com/zmap/zcrypto v0.0.0-20201128221613-3719af1573cf/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= 334 | github.com/zmap/zcrypto v0.0.0-20201211161100-e54a5822fb7e/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= 335 | github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968 h1:YOQ1vXEwE4Rnj+uQ/3oCuJk5wgVsvUyW+glsndwYuyA= 336 | github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968/go.mod h1:xIuOvYCZX21S5Z9bK1BMrertTGX/F8hgAPw7ERJRNS0= 337 | github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8= 338 | go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= 339 | go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= 340 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 341 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 342 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 343 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 344 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 345 | golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 346 | golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 347 | golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 348 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 349 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 350 | golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 351 | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 352 | golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 353 | golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= 354 | golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= 355 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 356 | golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo= 357 | golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 358 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 359 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 360 | golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= 361 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 362 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 363 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 364 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 365 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 366 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 367 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 368 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 369 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 370 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 371 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 372 | golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 373 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 374 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 375 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 376 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 377 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 378 | golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= 379 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 380 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 381 | golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= 382 | golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= 383 | golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= 384 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 385 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 386 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 387 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 388 | golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= 389 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 390 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 391 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 392 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 393 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 394 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 395 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 396 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 397 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 398 | golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 399 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 400 | golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 401 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 402 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 403 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 404 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 405 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 406 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 407 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 408 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 409 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 410 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 411 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 412 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 413 | golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 414 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 415 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 416 | golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= 417 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 418 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 419 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 420 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 421 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 422 | golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 423 | golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= 424 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 425 | golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= 426 | golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= 427 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 428 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 429 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 430 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 431 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 432 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 433 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 434 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 435 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 436 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 437 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 438 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 439 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 440 | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= 441 | golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 442 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 443 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 444 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 445 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 446 | golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= 447 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 448 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 449 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 450 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 451 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 452 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 453 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 454 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 455 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 456 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 457 | google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= 458 | google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 459 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 460 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 461 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 462 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 463 | gopkg.in/djherbis/times.v1 v1.3.0 h1:uxMS4iMtH6Pwsxog094W0FYldiNnfY/xba00vq6C2+o= 464 | gopkg.in/djherbis/times.v1 v1.3.0/go.mod h1:AQlg6unIsrsCEdQYhTzERy542dz6SFdQFZFv6mUY0P8= 465 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 466 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 467 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 468 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 469 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 470 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 471 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 472 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 473 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/j3ssie/metabigor/cmd" 5 | ) 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /modules/asn.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "compress/gzip" 7 | _ "embed" 8 | "fmt" 9 | "io" 10 | "math/big" 11 | "net" 12 | "sort" 13 | "strconv" 14 | "strings" 15 | 16 | "net/netip" 17 | ) 18 | 19 | // Most of the file literally copied from @thebl4ckturtle code 20 | 21 | type ASInfo struct { 22 | Amount int 23 | Number int 24 | CountryCode string 25 | Description string 26 | CIDR string 27 | } 28 | 29 | //go:embed static/ip2asn-combined.tsv.gz 30 | var ip2asnData string 31 | 32 | func GetAsnMap() (AsnMap, error) { 33 | //var asnAsnMap AsnMap 34 | //f, err := pkger.Open("/static/ip2asn-combined.tsv.gz") 35 | //if err != nil { 36 | // return AsnMap{}, err 37 | //} 38 | f := strings.NewReader(ip2asnData) 39 | asnAsnMap, err := GenAsnData(f) 40 | if err != nil { 41 | return AsnMap{}, err 42 | } 43 | return *asnAsnMap, nil 44 | } 45 | 46 | type IPInfo struct { 47 | AS int 48 | CIDR string 49 | } 50 | 51 | type AsnMap struct { 52 | asName map[int]string 53 | asCountry map[int]string 54 | asDesc map[string]int 55 | recs []rec 56 | } 57 | 58 | type rec struct { 59 | startIP, endIP netip.Addr 60 | asn int 61 | } 62 | 63 | func (m *AsnMap) ASName(as int) string { return m.asName[as] } 64 | func (m *AsnMap) ASCountry(as int) string { return m.asCountry[as] } 65 | func (m *AsnMap) ASDesc(name string) (AsnNum []int) { 66 | name = strings.ToLower(name) 67 | for k, v := range m.asDesc { 68 | if strings.Contains(strings.ToLower(k), name) { 69 | AsnNum = append(AsnNum, v) 70 | } 71 | } 72 | return AsnNum 73 | } 74 | 75 | // ASInfo returns 0 on unknown. 76 | func (m *AsnMap) ASInfo(asnNum int) []ASInfo { 77 | var asnInfos []ASInfo 78 | if asnNum == 0 { 79 | return asnInfos 80 | } 81 | 82 | for _, rec := range m.recs { 83 | if rec.asn == asnNum { 84 | var asnInfo ASInfo 85 | as := IPInfo{AS: rec.asn, CIDR: Range2CIDR(net.ParseIP(rec.startIP.String()), net.ParseIP(rec.endIP.String())).String()} 86 | asnInfo.Number = asnNum 87 | asnInfo.CIDR = as.CIDR 88 | asnInfo.CountryCode = m.ASCountry(asnNum) 89 | asnInfo.Description = m.ASName(asnNum) 90 | asnInfos = append(asnInfos, asnInfo) 91 | } 92 | } 93 | return asnInfos 94 | } 95 | 96 | // ASofIP returns 0 on unknown. 97 | func (m *AsnMap) ASofIP(ip netip.Addr) IPInfo { 98 | cand := sort.Search(len(m.recs), func(i int) bool { 99 | return ip.Less(m.recs[i].startIP) 100 | }) 101 | return m.recIndexHasIP(cand-1, ip) 102 | } 103 | 104 | // recIndexHasIP returns the AS number of m.rec[i] if i is in range and 105 | // the record contains the given IP address. 106 | func (m *AsnMap) recIndexHasIP(i int, ip netip.Addr) (as IPInfo) { 107 | if i < 0 { 108 | return IPInfo{AS: 0} 109 | } 110 | rec := &m.recs[i] 111 | if rec.endIP.Less(ip) { 112 | return IPInfo{AS: 0} 113 | } 114 | if ip.Less(rec.startIP) { 115 | return IPInfo{AS: 0} 116 | } 117 | return IPInfo{AS: rec.asn, CIDR: Range2CIDR(net.ParseIP(rec.startIP.String()), net.ParseIP(rec.endIP.String())).String()} 118 | } 119 | 120 | func GenAsnData(r io.Reader) (*AsnMap, error) { 121 | br := bufio.NewReader(r) 122 | magic, err := br.Peek(2) 123 | if err != nil { 124 | return nil, err 125 | } 126 | if string(magic) == "\x1f\x8b" { 127 | zr, err := gzip.NewReader(br) 128 | if err != nil { 129 | return nil, err 130 | } 131 | br = bufio.NewReader(zr) 132 | } 133 | m := &AsnMap{ 134 | asName: map[int]string{}, 135 | asCountry: map[int]string{}, 136 | asDesc: map[string]int{}, 137 | } 138 | for { 139 | line, err := br.ReadSlice('\n') 140 | if err == io.EOF { 141 | return m, nil 142 | } 143 | if err != nil { 144 | return nil, err 145 | } 146 | var startIPB, endIPB, asnB, country, desc []byte 147 | if err := parseTSV(line, &startIPB, &endIPB, &asnB, &country, &desc); err != nil { 148 | return nil, err 149 | } 150 | if string(desc) == "Not routed" { 151 | continue 152 | } 153 | as64, err := strconv.ParseInt(string(asnB), 10, 64) 154 | if err != nil { 155 | return nil, fmt.Errorf("bogus ASN %q for line %q", asnB, line) 156 | } 157 | as := int(as64) 158 | if _, ok := m.asName[as]; !ok { 159 | m.asName[as] = string(desc) 160 | m.asCountry[as] = string(country) 161 | m.asDesc[string(desc)] = as 162 | } 163 | 164 | startIP, err := netip.ParseAddr(string(startIPB)) // TODO: add ParseIPBytes 165 | if err != nil { 166 | return nil, fmt.Errorf("bogus IP %q for line %q", startIPB, line) 167 | } 168 | endIP, err := netip.ParseAddr(string(endIPB)) // TODO: add ParseIPBytes 169 | if err != nil { 170 | return nil, fmt.Errorf("bogus IP %q for line %q", endIPB, line) 171 | } 172 | m.recs = append(m.recs, rec{startIP, endIP, as}) 173 | } 174 | } 175 | 176 | func parseTSV(line []byte, dsts ...*[]byte) error { 177 | if len(line) > 0 && line[len(line)-1] == '\n' { 178 | line = line[:len(line)-1] 179 | } 180 | for i, dst := range dsts { 181 | last := i == len(dsts)-1 182 | tab := bytes.IndexByte(line, '\t') 183 | if tab == -1 && !last { 184 | return fmt.Errorf("short line: %q", line) 185 | } 186 | if tab != -1 { 187 | *dst, line = line[:tab], line[tab+1:] 188 | } else { 189 | *dst = line 190 | } 191 | } 192 | return nil 193 | } 194 | 195 | // Range2CIDR turns an IP range into a CIDR. 196 | func Range2CIDR(first, last net.IP) *net.IPNet { 197 | startip, m := ipToInt(first) 198 | endip, _ := ipToInt(last) 199 | newip := big.NewInt(1) 200 | mask := big.NewInt(1) 201 | one := big.NewInt(1) 202 | 203 | if startip.Cmp(endip) == 1 { 204 | return nil 205 | } 206 | 207 | max := uint(m) 208 | var bits uint = 1 209 | newip.Set(startip) 210 | tmp := new(big.Int) 211 | for bits < max { 212 | tmp.Rsh(startip, bits) 213 | tmp.Lsh(tmp, bits) 214 | 215 | newip.Or(startip, mask) 216 | if newip.Cmp(endip) == 1 || tmp.Cmp(startip) != 0 { 217 | bits-- 218 | mask.Rsh(mask, 1) 219 | break 220 | } 221 | 222 | bits++ 223 | tmp.Lsh(mask, 1) 224 | mask.Add(tmp, one) 225 | } 226 | 227 | cidrstr := first.String() + "/" + strconv.Itoa(int(max-bits)) 228 | _, ipnet, _ := net.ParseCIDR(cidrstr) 229 | 230 | return ipnet 231 | } 232 | 233 | func ipToInt(ip net.IP) (*big.Int, int) { 234 | val := big.NewInt(1) 235 | 236 | val.SetBytes([]byte(ip)) 237 | if IsIPv4(ip) { 238 | return val, 32 239 | } else if IsIPv6(ip) { 240 | return val, 128 241 | } 242 | 243 | return val, 0 244 | } 245 | 246 | // IsIPv4 returns true when the provided net.IP address is an IPv4 address. 247 | func IsIPv4(ip net.IP) bool { 248 | return strings.Count(ip.String(), ":") < 2 249 | } 250 | 251 | // IsIPv6 returns true when the provided net.IP address is an IPv6 address. 252 | func IsIPv6(ip net.IP) bool { 253 | return strings.Count(ip.String(), ":") >= 2 254 | } 255 | -------------------------------------------------------------------------------- /modules/cert.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "strings" 7 | 8 | "github.com/PuerkitoBio/goquery" 9 | "github.com/j3ssie/metabigor/core" 10 | jsoniter "github.com/json-iterator/go" 11 | "github.com/thoas/go-funk" 12 | ) 13 | 14 | // CrtSHOrg get IPInfo from https://crt.sh 15 | func CrtSHOrg(org string, options core.Options) []string { 16 | crtURL := fmt.Sprintf(`https://crt.sh/?O=%v`, url.QueryEscape(org)) 17 | var result []string 18 | var content string 19 | var err error 20 | 21 | core.InforF("Fetching data from: %v", crtURL) 22 | if content, err = core.GetResponse(crtURL, options); err != nil { 23 | // simple retry if something went wrong 24 | content, err = core.GetResponse(crtURL, options) 25 | if err != nil || content == "" { 26 | core.ErrorF("Error sending request to: %v", crtURL) 27 | return result 28 | } 29 | } 30 | 31 | // change the query 32 | if strings.Contains(content, "None found") { 33 | crtURL = fmt.Sprintf(`https://crt.sh/?q=%v`, url.QueryEscape(org)) 34 | core.InforF("No data found, change the query to: %v", crtURL) 35 | 36 | if content, err = core.GetResponse(crtURL, options); err != nil { 37 | core.ErrorF("Error sending request to: %v", crtURL) 38 | return result 39 | } 40 | } 41 | 42 | infos := ParseCertSH(content, options) 43 | for _, info := range infos { 44 | var data string 45 | if options.JsonOutput { 46 | if options.JsonOutput { 47 | if data, err := jsoniter.MarshalToString(info); err == nil { 48 | result = append(result, data) 49 | 50 | if !options.Quiet { 51 | fmt.Println(data) 52 | } 53 | } 54 | continue 55 | } 56 | } 57 | 58 | if options.Verbose { 59 | data = fmt.Sprintf("%s ;; %s ;; %s", info.Domain, info.Org, info.CertInfo) 60 | } else { 61 | data = info.Domain 62 | } 63 | result = append(result, data) 64 | } 65 | result = funk.UniqString(result) 66 | 67 | if !options.Quiet { 68 | fmt.Println(strings.Join(result, "\n")) 69 | } 70 | return result 71 | } 72 | 73 | type CertData struct { 74 | Domain string 75 | CertInfo string 76 | Org string 77 | WildCard bool 78 | } 79 | 80 | func ParseCertSH(content string, options core.Options) []CertData { 81 | var results []CertData 82 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) 83 | if err != nil { 84 | core.ErrorF("Error parsing body: %v", err) 85 | return results 86 | } 87 | 88 | doc.Find("tr").Each(func(i int, s *goquery.Selection) { 89 | var certInfo CertData 90 | s.Find("td").Each(func(j int, td *goquery.Selection) { 91 | if j == 4 { 92 | certInfo.Domain = strings.TrimSpace(td.Text()) 93 | } 94 | if j == 5 { 95 | certInfo.Org = strings.TrimSpace(td.Text()) 96 | } 97 | if j == 6 { 98 | certInfo.CertInfo = strings.TrimSpace(td.Text()) 99 | } 100 | }) 101 | // remove some noise 102 | if strings.Contains(certInfo.Domain, ".") && !strings.Contains(certInfo.Domain, " ") { 103 | if options.Cert.Clean { 104 | certInfo.Domain = strings.ReplaceAll(certInfo.Domain, "*.", "") 105 | results = append(results, certInfo) 106 | } else if options.Cert.OnlyWildCard { 107 | if strings.Contains(certInfo.Domain, "*") { 108 | certInfo.WildCard = true 109 | results = append(results, certInfo) 110 | } 111 | } else { 112 | results = append(results, certInfo) 113 | } 114 | } 115 | 116 | }) 117 | 118 | return results 119 | } 120 | -------------------------------------------------------------------------------- /modules/cert_test.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | "github.com/j3ssie/metabigor/core" 6 | "testing" 7 | ) 8 | 9 | func TestParseCertSH(t *testing.T) { 10 | var options core.Options 11 | raw := core.GetFileContent("/tmp/tll") 12 | result := ParseCertSH(raw, options) 13 | fmt.Println(result) 14 | if len(result) == 0 { 15 | t.Errorf("Error TestParseCertSH") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /modules/ga.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | func ExtractUAID(content string) (results []string) { 10 | regex := regexp.MustCompile(`UA-[0-9]+-[0-9]+`) 11 | ua := regex.FindAllStringSubmatch(content, -1) 12 | for _, id := range ua { 13 | results = append(results, id[0]) 14 | } 15 | 16 | return results 17 | } 18 | 19 | func ExtractGoogleTagManger(content string) (results []string) { 20 | // regex 1 21 | regex := regexp.MustCompile(`www\.googletagmanager\.com/ns\.html\?id=[A-Z0-9\-]+`) 22 | data := regex.FindStringSubmatch(content) 23 | 24 | if len(data) > 0 { 25 | gtm := strings.Split(data[0], "id=")[1] 26 | results = append(results, fmt.Sprintf("https://www.googletagmanager.com/gtm.js?id=%s", gtm)) 27 | return results 28 | } 29 | 30 | // regex 2 31 | regex = regexp.MustCompile("GTM-[A-Z0-9]+") 32 | data = regex.FindStringSubmatch(content) 33 | if len(data) > 0 { 34 | results = append(results, fmt.Sprintf("https://www.googletagmanager.com/gtm.js?id=%s", data[0])) 35 | return results 36 | } 37 | 38 | // regex 3 39 | //regex = regexp.MustCompile(`UA-[0-9]+-[0-9]+`) 40 | //ua := regex.FindAllStringSubmatch(content, -1) 41 | //for _, id := range ua { 42 | // results = append(results, id[0]) 43 | //} 44 | return results 45 | } 46 | -------------------------------------------------------------------------------- /modules/ip.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "strings" 9 | 10 | "github.com/j3ssie/metabigor/core" 11 | ) 12 | 13 | var tr = &http.Transport{ 14 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 15 | } 16 | var client = &http.Client{Transport: tr} 17 | 18 | func InternetDB(IP string) string { 19 | ipURL := fmt.Sprintf("https://internetdb.shodan.io/%s", IP) 20 | core.DebugF("Getting information from: %s", ipURL) 21 | resp, err := client.Get(ipURL) 22 | if err != nil { 23 | return "" 24 | } 25 | defer resp.Body.Close() 26 | 27 | // Create a buffer to store response body 28 | var bodyBuilder strings.Builder 29 | 30 | // Read response body into the buffer 31 | _, err = io.Copy(&bodyBuilder, resp.Body) 32 | if err != nil { 33 | return "" 34 | } 35 | 36 | return bodyBuilder.String() 37 | } 38 | -------------------------------------------------------------------------------- /modules/netblock.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net" 7 | "strings" 8 | 9 | jsoniter "github.com/json-iterator/go" 10 | 11 | "github.com/PuerkitoBio/goquery" 12 | "github.com/j3ssie/metabigor/core" 13 | "github.com/thoas/go-funk" 14 | ) 15 | 16 | func getAsnNum(raw string) string { 17 | if strings.HasPrefix(strings.ToLower(raw), "as") { 18 | return raw[2:] 19 | } 20 | return raw 21 | } 22 | 23 | // RangeInfo infor about range IP 24 | type RangeInfo struct { 25 | Cidr string `json:"cidr"` 26 | Desc string `json:"desc"` 27 | Asn string `json:"asn"` 28 | Country string `json:"country"` 29 | } 30 | 31 | // GetIPInfo get CIDR from ASN 32 | func GetIPInfo(options core.Options) []string { 33 | asn := getAsnNum(options.Net.Asn) 34 | url := fmt.Sprintf(`https://ipinfo.io/AS%v`, asn) 35 | var result []string 36 | core.InforF("Fetching data from: %v", url) 37 | content := core.RequestWithChrome(url, "ipTabContent", options.Timeout) 38 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) 39 | if err != nil { 40 | return result 41 | } 42 | 43 | var country string 44 | doc.Find(".flag").Each(func(i int, s *goquery.Selection) { 45 | href, ok := s.Attr("href") 46 | if ok { 47 | if strings.HasPrefix(href, "/countries/") { 48 | country = s.Text() 49 | return 50 | } 51 | } 52 | }) 53 | 54 | // searching for data 55 | doc.Find("tr").Each(func(i int, s *goquery.Selection) { 56 | s.Find("address").First() 57 | if !strings.Contains(s.Text(), "Netblock") { 58 | data := strings.Split(strings.TrimSpace(s.Text()), " ") 59 | cidr := strings.TrimSpace(data[0]) 60 | desc := strings.TrimSpace(data[len(data)-1]) 61 | if len(data) > 2 { 62 | desc = strings.TrimSpace(data[1]) 63 | } 64 | 65 | if options.JsonOutput { 66 | output := RangeInfo{ 67 | Cidr: cidr, 68 | Desc: desc, 69 | Asn: asn, 70 | Country: country, 71 | } 72 | if out, err := jsoniter.MarshalToString(output); err == nil { 73 | core.InforF(out) 74 | result = append(result, out) 75 | } 76 | } else { 77 | core.InforF(fmt.Sprintf("%s - %s", cidr, desc)) 78 | result = append(result, fmt.Sprintf("%s", cidr)) 79 | } 80 | } 81 | }) 82 | return result 83 | } 84 | 85 | // IPv4Info get CIDR from ASN via ipv4info.com 86 | func IPv4Info(options core.Options) []string { 87 | asn := getAsnNum(options.Net.Asn) 88 | url := fmt.Sprintf(`http://ipv4info.com/?act=check&ip=AS%v`, asn) 89 | var result []string 90 | 91 | core.InforF("Fetching data from: %v", url) 92 | content := core.SendGET(url, options) 93 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) 94 | if err != nil { 95 | return result 96 | } 97 | 98 | // finding ID of block 99 | var ASNLink []string 100 | doc.Find("a").Each(func(i int, s *goquery.Selection) { 101 | href, ok := s.Attr("href") 102 | if ok { 103 | if strings.HasPrefix(href, "/org/") { 104 | ASNLink = append(ASNLink, href) 105 | } 106 | } 107 | }) 108 | 109 | // searching for data 110 | ASNLink = funk.Uniq(ASNLink).([]string) 111 | for _, link := range ASNLink { 112 | core.InforF("Fetching data from: %v", link) 113 | URL := fmt.Sprintf(`http://ipv4info.com%v`, link) 114 | core.InforF("Fetching data from: %v", URL) 115 | content := core.SendGET(url, options) 116 | // finding ID of block 117 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) 118 | if err != nil { 119 | return result 120 | } 121 | 122 | doc.Find("td").Each(func(i int, s *goquery.Selection) { 123 | style, _ := s.Attr("style") 124 | class, _ := s.Attr("class") 125 | if style == "padding: 0 0 0 0;" && class == "bold" { 126 | data := s.Text() 127 | result = append(result, data) 128 | } 129 | 130 | }) 131 | } 132 | core.InforF("\n%v", strings.Join(result, "\n")) 133 | return result 134 | } 135 | 136 | // ASNBgpDotNet get ASN infor from bgp.net 137 | func ASNBgpDotNet(options core.Options) []string { 138 | asn := getAsnNum(options.Net.Asn) 139 | url := fmt.Sprintf(`https://bgp.he.net/AS%v#_prefixes`, asn) 140 | core.InforF("Fetching data from: %v", url) 141 | var result []string 142 | content := core.RequestWithChrome(url, "prefixes", options.Timeout*4) 143 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) 144 | if err != nil { 145 | return result 146 | } 147 | // searching for data 148 | doc.Find("tr").Each(func(i int, s *goquery.Selection) { 149 | data := strings.Split(strings.TrimSpace(s.Text()), " ") 150 | cidr := strings.TrimSpace(data[0]) 151 | if !strings.Contains(cidr, "Prefix") { 152 | desc := strings.TrimSpace(data[len(data)-1]) 153 | core.InforF(fmt.Sprintf("%s - %s", cidr, desc)) 154 | result = append(result, fmt.Sprintf("%s", cidr)) 155 | } 156 | }) 157 | return result 158 | } 159 | 160 | // ASNSpyse get ASN infor from spyse.com 161 | func ASNSpyse(options core.Options) []string { 162 | asn := getAsnNum(options.Net.Asn) 163 | url := fmt.Sprintf(`https://spyse.com/target/as/%v#c-domain__anchor--3--%v`, asn, asn) 164 | var result []string 165 | core.InforF("Fetching data from: %v", url) 166 | content := core.RequestWithChrome(url, "asn-ipv4-ranges", options.Timeout) 167 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) 168 | if err != nil { 169 | return result 170 | } 171 | // searching for data 172 | doc.Find("tr").Each(func(i int, s *goquery.Selection) { 173 | data := strings.Split(strings.TrimSpace(s.Text()), " ") 174 | cidr := strings.TrimSpace(data[0]) 175 | if !strings.Contains(cidr, "CIDR") { 176 | desc := strings.Split(data[len(data)-2], "\n") 177 | realDesc := desc[len(desc)-1] 178 | core.InforF(fmt.Sprintf("%s - %s", cidr, realDesc)) 179 | result = append(result, fmt.Sprintf("%s", cidr)) 180 | } 181 | }) 182 | return result 183 | } 184 | 185 | /* Get IP range from Organization */ 186 | 187 | // OrgBgpDotNet get Org infor from bgp.net 188 | func OrgBgpDotNet(options core.Options) []string { 189 | org := options.Net.Org 190 | url := fmt.Sprintf(`https://bgp.he.net/search?search%%5Bsearch%%5D=%v&commit=Search`, org) 191 | core.InforF("Fetching data from: %v", url) 192 | var result []string 193 | content := core.RequestWithChrome(url, "search", options.Timeout) 194 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) 195 | if err != nil { 196 | return result 197 | } 198 | 199 | // searching for data 200 | doc.Find("tr").Each(func(i int, s *goquery.Selection) { 201 | if !strings.Contains(s.Text(), "Result") && !strings.Contains(s.Text(), "Description") { 202 | data := strings.Split(strings.TrimSpace(s.Text()), " ")[0] 203 | realdata := strings.Split(data, "\n") 204 | cidr := strings.Trim(strings.TrimSpace(realdata[0]), "Route") 205 | desc := strings.TrimSpace(realdata[len(realdata)-1]) 206 | core.InforF(fmt.Sprintf("%s - %s", cidr, desc)) 207 | result = append(result, fmt.Sprintf("%s", cidr)) 208 | } 209 | }) 210 | return result 211 | } 212 | 213 | // OrgBgbView get Org infor from bgpview.io 214 | func OrgBgbView(options core.Options) []string { 215 | org := options.Net.Org 216 | url := fmt.Sprintf(`https://bgpview.io/search/%v`, org) 217 | core.InforF("Fetching data from: %v", url) 218 | var result []string 219 | content := core.RequestWithChrome(url, "results-tabs", options.Timeout) 220 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) 221 | if err != nil { 222 | return result 223 | } 224 | 225 | // searching for data 226 | doc.Find("a").Each(func(i int, s *goquery.Selection) { 227 | href, _ := s.Attr("href") 228 | // https://bgpview.io/prefix/176.96.254.0/24 229 | if strings.Contains(href, "https://bgpview.io/prefix/") { 230 | //cidr := strings.Replace(href, "https://bgpview.io/prefix/", "", -1) 231 | cidr := s.Text() 232 | result = append(result, fmt.Sprintf("%s", cidr)) 233 | } 234 | }) 235 | return result 236 | } 237 | 238 | // ASNLookup get Org CIDR from asnlookup 239 | func ASNLookup(options core.Options) []string { 240 | org := options.Net.Org 241 | url := fmt.Sprintf(`http://asnlookup.com/api/lookup?org=%v`, org) 242 | core.InforF("Fetching data from: %v", url) 243 | data := core.SendGET(url, options) 244 | var result []string 245 | if data == "" { 246 | return result 247 | } 248 | err := json.Unmarshal([]byte(data), &result) 249 | if err != nil { 250 | return result 251 | } 252 | 253 | for _, item := range result { 254 | core.InforF(item) 255 | } 256 | return result 257 | } 258 | 259 | // ASNFromIP get ip or domain from ultratools.com 260 | func ASNFromIP(options core.Options) []string { 261 | var result []string 262 | ip := options.Net.IP 263 | // resolve IP 264 | if ip == "" && options.Net.Domain != "" { 265 | if resolved, err := net.LookupHost(options.Net.Domain); err == nil { 266 | ip = resolved[0] 267 | } 268 | } 269 | url := fmt.Sprintf(`https://www.ultratools.com/tools/asnInfoResult?domainName=%v`, ip) 270 | core.InforF("Fetching data from: %v", url) 271 | content := core.SendGET(url, options) 272 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) 273 | if err != nil { 274 | return result 275 | } 276 | 277 | // searching for data 278 | asn := doc.Find(".tool-results-heading").Text() 279 | if asn != "" { 280 | result = append(result, strings.TrimSpace(asn)) 281 | } 282 | 283 | return result 284 | } 285 | -------------------------------------------------------------------------------- /modules/netblock_test.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/j3ssie/metabigor/core" 7 | ) 8 | 9 | func TestIPInfo(t *testing.T) { 10 | var options core.Options 11 | options.Net.Asn = "AS714" 12 | result := GetIPInfo(options) 13 | if len(result) == 0 { 14 | t.Errorf("Error IPInfo") 15 | } 16 | } 17 | 18 | func TestIPv4Info(t *testing.T) { 19 | var options core.Options 20 | options.Net.Asn = "AS6432" 21 | result := IPv4Info(options) 22 | if len(result) == 0 { 23 | t.Errorf("Error IPv4Info") 24 | } 25 | } 26 | 27 | func TestASNBgpDotNet(t *testing.T) { 28 | var options core.Options 29 | options.Net.Asn = "AS62830" 30 | result := ASNBgpDotNet(options) 31 | if len(result) == 0 { 32 | t.Errorf("Error TestASNBgpDotNet") 33 | } 34 | } 35 | 36 | func TestASNSpyse(t *testing.T) { 37 | var options core.Options 38 | options.Net.Asn = "714" 39 | result := ASNSpyse(options) 40 | if len(result) == 0 { 41 | t.Errorf("Error TestASNSpyse") 42 | } 43 | } 44 | 45 | func TestOrgBgpDotNet(t *testing.T) { 46 | var options core.Options 47 | options.Net.Org = "riot" 48 | result := OrgBgpDotNet(options) 49 | if len(result) == 0 { 50 | t.Errorf("Error TestOrgBgpDotNet") 51 | } 52 | } 53 | 54 | func TestASNLookup(t *testing.T) { 55 | var options core.Options 56 | options.Net.Org = "riot" 57 | result := ASNLookup(options) 58 | if len(result) == 0 { 59 | t.Errorf("Error ASNLookup") 60 | } 61 | } 62 | 63 | func TestASNFromIP(t *testing.T) { 64 | var options core.Options 65 | options.Net.IP = "168.120.1.1" 66 | result := ASNFromIP(options) 67 | if len(result) == 0 { 68 | t.Errorf("Error ASNLookup") 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /modules/nmap.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "encoding/xml" 5 | "fmt" 6 | "github.com/PuerkitoBio/goquery" 7 | "github.com/j3ssie/metabigor/core" 8 | "regexp" 9 | "strings" 10 | ) 11 | 12 | // NmapRuns nmap multiple scan XML to struct 13 | type NmapRuns struct { 14 | XMLName xml.Name `xml:"nmaprun"` 15 | Text string `xml:",chardata"` 16 | Scanner string `xml:"scanner,attr"` 17 | Args string `xml:"args,attr"` 18 | Start string `xml:"start,attr"` 19 | Startstr string `xml:"startstr,attr"` 20 | Version string `xml:"version,attr"` 21 | Xmloutputversion string `xml:"xmloutputversion,attr"` 22 | Scaninfo struct { 23 | Text string `xml:",chardata"` 24 | Type string `xml:"type,attr"` 25 | Protocol string `xml:"protocol,attr"` 26 | Numservices string `xml:"numservices,attr"` 27 | Services string `xml:"services,attr"` 28 | } `xml:"scaninfo"` 29 | Verbose struct { 30 | Text string `xml:",chardata"` 31 | Level string `xml:"level,attr"` 32 | } `xml:"verbose"` 33 | Debugging struct { 34 | Text string `xml:",chardata"` 35 | Level string `xml:"level,attr"` 36 | } `xml:"debugging"` 37 | Taskbegin []struct { 38 | Text string `xml:",chardata"` 39 | Task string `xml:"task,attr"` 40 | Time string `xml:"time,attr"` 41 | } `xml:"taskbegin"` 42 | Taskend []struct { 43 | Text string `xml:",chardata"` 44 | Task string `xml:"task,attr"` 45 | Time string `xml:"time,attr"` 46 | Extrainfo string `xml:"extrainfo,attr"` 47 | } `xml:"taskend"` 48 | Host []struct { 49 | Text string `xml:",chardata"` 50 | Starttime string `xml:"starttime,attr"` 51 | Endtime string `xml:"endtime,attr"` 52 | Status struct { 53 | Text string `xml:",chardata"` 54 | State string `xml:"state,attr"` 55 | Reason string `xml:"reason,attr"` 56 | ReasonTtl string `xml:"reason_ttl,attr"` 57 | } `xml:"status"` 58 | Address struct { 59 | Text string `xml:",chardata"` 60 | Addr string `xml:"addr,attr"` 61 | Addrtype string `xml:"addrtype,attr"` 62 | } `xml:"address"` 63 | Hostnames struct { 64 | Text string `xml:",chardata"` 65 | Hostname struct { 66 | Text string `xml:",chardata"` 67 | Name string `xml:"name,attr"` 68 | Type string `xml:"type,attr"` 69 | } `xml:"hostname"` 70 | } `xml:"hostnames"` 71 | Ports struct { 72 | Text string `xml:",chardata"` 73 | Extraports struct { 74 | Text string `xml:",chardata"` 75 | State string `xml:"state,attr"` 76 | Count string `xml:"count,attr"` 77 | Extrareasons []struct { 78 | Text string `xml:",chardata"` 79 | Reason string `xml:"reason,attr"` 80 | Count string `xml:"count,attr"` 81 | } `xml:"extrareasons"` 82 | } `xml:"extraports"` 83 | Port []struct { 84 | Text string `xml:",chardata"` 85 | Protocol string `xml:"protocol,attr"` 86 | Portid string `xml:"portid,attr"` 87 | State struct { 88 | Text string `xml:",chardata"` 89 | State string `xml:"state,attr"` 90 | Reason string `xml:"reason,attr"` 91 | ReasonTtl string `xml:"reason_ttl,attr"` 92 | } `xml:"state"` 93 | Service struct { 94 | Text string `xml:",chardata"` 95 | Name string `xml:"name,attr"` 96 | Tunnel string `xml:"tunnel,attr"` 97 | Method string `xml:"method,attr"` 98 | Conf string `xml:"conf,attr"` 99 | Product string `xml:"product,attr"` 100 | Devicetype string `xml:"devicetype,attr"` 101 | Servicefp string `xml:"servicefp,attr"` 102 | Cpe string `xml:"cpe"` 103 | } `xml:"service"` 104 | Script struct { 105 | Text string `xml:",chardata"` 106 | ID string `xml:"id,attr"` 107 | Output string `xml:"output,attr"` 108 | Elem []struct { 109 | Text string `xml:",chardata"` 110 | Key string `xml:"key,attr"` 111 | } `xml:"elem"` 112 | } `xml:"script"` 113 | } `xml:"port"` 114 | } `xml:"ports"` 115 | Times struct { 116 | Text string `xml:",chardata"` 117 | Srtt string `xml:"srtt,attr"` 118 | Rttvar string `xml:"rttvar,attr"` 119 | To string `xml:"to,attr"` 120 | } `xml:"times"` 121 | } `xml:"host"` 122 | Taskprogress []struct { 123 | Text string `xml:",chardata"` 124 | Task string `xml:"task,attr"` 125 | Time string `xml:"time,attr"` 126 | Percent string `xml:"percent,attr"` 127 | Remaining string `xml:"remaining,attr"` 128 | Etc string `xml:"etc,attr"` 129 | } `xml:"taskprogress"` 130 | Runstats struct { 131 | Text string `xml:",chardata"` 132 | Finished struct { 133 | Text string `xml:",chardata"` 134 | Time string `xml:"time,attr"` 135 | Timestr string `xml:"timestr,attr"` 136 | Elapsed string `xml:"elapsed,attr"` 137 | Summary string `xml:"summary,attr"` 138 | Exit string `xml:"exit,attr"` 139 | } `xml:"finished"` 140 | Hosts struct { 141 | Text string `xml:",chardata"` 142 | Up string `xml:"up,attr"` 143 | Down string `xml:"down,attr"` 144 | Total string `xml:"total,attr"` 145 | } `xml:"hosts"` 146 | } `xml:"runstats"` 147 | } 148 | 149 | // NmapRun nmap single scan XML to struct 150 | type NmapRun struct { 151 | XMLName xml.Name `xml:"nmaprun"` 152 | Text string `xml:",chardata"` 153 | Scanner string `xml:"scanner,attr"` 154 | Args string `xml:"args,attr"` 155 | Start string `xml:"start,attr"` 156 | Startstr string `xml:"startstr,attr"` 157 | Version string `xml:"version,attr"` 158 | Xmloutputversion string `xml:"xmloutputversion,attr"` 159 | Scaninfo struct { 160 | Text string `xml:",chardata"` 161 | Type string `xml:"type,attr"` 162 | Protocol string `xml:"protocol,attr"` 163 | Numservices string `xml:"numservices,attr"` 164 | Services string `xml:"services,attr"` 165 | } `xml:"scaninfo"` 166 | Verbose struct { 167 | Text string `xml:",chardata"` 168 | Level string `xml:"level,attr"` 169 | } `xml:"verbose"` 170 | Debugging struct { 171 | Text string `xml:",chardata"` 172 | Level string `xml:"level,attr"` 173 | } `xml:"debugging"` 174 | Taskbegin []struct { 175 | Text string `xml:",chardata"` 176 | Task string `xml:"task,attr"` 177 | Time string `xml:"time,attr"` 178 | } `xml:"taskbegin"` 179 | Taskend []struct { 180 | Text string `xml:",chardata"` 181 | Task string `xml:"task,attr"` 182 | Time string `xml:"time,attr"` 183 | Extrainfo string `xml:"extrainfo,attr"` 184 | } `xml:"taskend"` 185 | Taskprogress []struct { 186 | Text string `xml:",chardata"` 187 | Task string `xml:"task,attr"` 188 | Time string `xml:"time,attr"` 189 | Percent string `xml:"percent,attr"` 190 | Remaining string `xml:"remaining,attr"` 191 | Etc string `xml:"etc,attr"` 192 | } `xml:"taskprogress"` 193 | 194 | Host struct { 195 | Text string `xml:",chardata"` 196 | Starttime string `xml:"starttime,attr"` 197 | Endtime string `xml:"endtime,attr"` 198 | Status struct { 199 | Text string `xml:",chardata"` 200 | State string `xml:"state,attr"` 201 | Reason string `xml:"reason,attr"` 202 | ReasonTtl string `xml:"reason_ttl,attr"` 203 | } `xml:"status"` 204 | Address struct { 205 | Text string `xml:",chardata"` 206 | Addr string `xml:"addr,attr"` 207 | Addrtype string `xml:"addrtype,attr"` 208 | } `xml:"address"` 209 | Hostnames struct { 210 | Text string `xml:",chardata"` 211 | Hostname struct { 212 | Text string `xml:",chardata"` 213 | Name string `xml:"name,attr"` 214 | Type string `xml:"type,attr"` 215 | } `xml:"hostname"` 216 | } `xml:"hostnames"` 217 | Ports struct { 218 | Text string `xml:",chardata"` 219 | Extraports struct { 220 | Text string `xml:",chardata"` 221 | State string `xml:"state,attr"` 222 | Count string `xml:"count,attr"` 223 | Extrareasons []struct { 224 | Text string `xml:",chardata"` 225 | Reason string `xml:"reason,attr"` 226 | Count string `xml:"count,attr"` 227 | } `xml:"extrareasons"` 228 | } `xml:"extraports"` 229 | Port []struct { 230 | Text string `xml:",chardata"` 231 | Protocol string `xml:"protocol,attr"` 232 | Portid string `xml:"portid,attr"` 233 | State struct { 234 | Text string `xml:",chardata"` 235 | State string `xml:"state,attr"` 236 | Reason string `xml:"reason,attr"` 237 | ReasonTtl string `xml:"reason_ttl,attr"` 238 | } `xml:"state"` 239 | Service struct { 240 | Text string `xml:",chardata"` 241 | Name string `xml:"name,attr"` 242 | Product string `xml:"product,attr"` 243 | Devicetype string `xml:"devicetype,attr"` 244 | Method string `xml:"method,attr"` 245 | Conf string `xml:"conf,attr"` 246 | Version string `xml:"version,attr"` 247 | Extrainfo string `xml:"extrainfo,attr"` 248 | Ostype string `xml:"ostype,attr"` 249 | Servicefp string `xml:"servicefp,attr"` 250 | Cpe []string `xml:"cpe"` 251 | } `xml:"service"` 252 | Script struct { 253 | Text string `xml:",chardata"` 254 | ID string `xml:"id,attr"` 255 | Output string `xml:"output,attr"` 256 | Elem []struct { 257 | Text string `xml:",chardata"` 258 | Key string `xml:"key,attr"` 259 | } `xml:"elem"` 260 | } `xml:"script"` 261 | } `xml:"port"` 262 | } `xml:"ports"` 263 | Times struct { 264 | Text string `xml:",chardata"` 265 | Srtt string `xml:"srtt,attr"` 266 | Rttvar string `xml:"rttvar,attr"` 267 | To string `xml:"to,attr"` 268 | } `xml:"times"` 269 | } `xml:"host"` 270 | Runstats struct { 271 | Text string `xml:",chardata"` 272 | Finished struct { 273 | Text string `xml:",chardata"` 274 | Time string `xml:"time,attr"` 275 | Timestr string `xml:"timestr,attr"` 276 | Elapsed string `xml:"elapsed,attr"` 277 | Summary string `xml:"summary,attr"` 278 | Exit string `xml:"exit,attr"` 279 | } `xml:"finished"` 280 | Hosts struct { 281 | Text string `xml:",chardata"` 282 | Up string `xml:"up,attr"` 283 | Down string `xml:"down,attr"` 284 | Total string `xml:"total,attr"` 285 | } `xml:"hosts"` 286 | } `xml:"runstats"` 287 | } 288 | 289 | type Host struct { 290 | IPAddress string 291 | Hostname string 292 | Ports []Port 293 | } 294 | 295 | type Port struct { 296 | Protocol string 297 | PortID string 298 | State string 299 | Service struct { 300 | Name string 301 | Product string 302 | Cpe string 303 | } 304 | Script struct { 305 | ID string 306 | Output string 307 | } 308 | } 309 | 310 | // ParseNmapXML parse nmap XML result 311 | func ParseNmapXML(raw string) NmapRun { 312 | // parsing content 313 | nmapRun := NmapRun{} 314 | err := xml.Unmarshal([]byte(raw), &nmapRun) 315 | if err != nil { 316 | core.ErrorF("Failed to parse Nmap XML file: %v", err) 317 | return nmapRun 318 | } 319 | return nmapRun 320 | } 321 | 322 | // ParseMultipleNmapXML parse nmap XML result 323 | func ParseMultipleNmapXML(raw string) NmapRuns { 324 | nmapRuns := NmapRuns{} 325 | err := xml.Unmarshal([]byte(raw), &nmapRuns) 326 | if err != nil { 327 | core.ErrorF("Failed to parse Nmap XML file: %v", err) 328 | return nmapRuns 329 | } 330 | return nmapRuns 331 | } 332 | 333 | // GetHosts parse nmap XML and return mutilehost object 334 | func GetHosts(raw string) []Host { 335 | var hosts []Host 336 | if strings.Count(raw, " 0 { 352 | for _, port := range nmapHost.Ports.Port { 353 | var item Port 354 | item.PortID = port.Portid 355 | item.Protocol = port.Protocol 356 | item.State = port.State.State 357 | // service 358 | item.Service.Name = port.Service.Name 359 | item.Service.Product = port.Service.Product 360 | //item.Service.Cpe = port.Service.Cpe 361 | item.Script.ID = port.Script.ID 362 | item.Script.Output = port.Script.Output 363 | host.Ports = append(host.Ports, item) 364 | } 365 | hosts = append(hosts, host) 366 | } 367 | } 368 | return hosts 369 | } 370 | 371 | // GetHost parse nmap XML and return host object 372 | func GetHost(raw string) Host { 373 | var host Host 374 | nmapObj := ParseNmapXML(raw) 375 | if nmapObj.Args == "" { 376 | core.ErrorF("Failed to parse Nmap XML") 377 | return host 378 | } 379 | host.IPAddress = nmapObj.Host.Address.Addr 380 | host.Hostname = nmapObj.Host.Hostnames.Hostname.Name 381 | core.DebugF("Parse XML for: %v", host.IPAddress) 382 | if len(nmapObj.Host.Ports.Port) > 0 { 383 | for _, port := range nmapObj.Host.Ports.Port { 384 | var item Port 385 | item.PortID = port.Portid 386 | item.Protocol = port.Protocol 387 | item.State = port.State.State 388 | // service 389 | item.Service.Name = port.Service.Name 390 | item.Service.Product = port.Service.Product 391 | //item.Service.Cpe = port.Service.Cpe 392 | item.Script.ID = port.Script.ID 393 | item.Script.Output = port.Script.Output 394 | 395 | host.Ports = append(host.Ports, item) 396 | } 397 | } 398 | 399 | return host 400 | } 401 | 402 | // ParsingNmapWithGoquery parse result from nmap XML format using goquery 403 | func ParsingNmapWithGoquery(raw string, options core.Options) map[string][]string { 404 | result := make(map[string][]string) 405 | 406 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(raw)) 407 | if err != nil { 408 | return result 409 | } 410 | doc.Find("host").Each(func(i int, h *goquery.Selection) { 411 | ip, _ := h.Find("address").First().Attr("addr") 412 | 413 | h.Find("port").Each(func(j int, s *goquery.Selection) { 414 | service, _ := s.Find("service").First().Attr("name") 415 | product, ok := s.Find("service").First().Attr("product") 416 | if !ok { 417 | product = "" 418 | } 419 | port, _ := s.Attr("portid") 420 | info := fmt.Sprintf("%v/%v/%v", port, service, product) 421 | result[ip] = append(result[ip], strings.TrimSpace(info)) 422 | }) 423 | 424 | if options.Scan.NmapScripts != "" { 425 | h.Find("script").Each(func(j int, s *goquery.Selection) { 426 | id, _ := s.Attr("id") 427 | scriptOutput, _ := s.Attr("output") 428 | 429 | if scriptOutput != "" { 430 | // grep script output with grepString 431 | if options.Scan.GrepString != "" { 432 | var vulnerable bool 433 | if strings.Contains(scriptOutput, options.Scan.GrepString) { 434 | vulnerable = true 435 | } else { 436 | r, err := regexp.Compile(options.Scan.GrepString) 437 | if err == nil { 438 | matches := r.FindStringSubmatch(scriptOutput) 439 | if len(matches) > 0 { 440 | vulnerable = true 441 | } 442 | } 443 | } 444 | if vulnerable { 445 | vul := fmt.Sprintf("/vulnerable|%v", id) 446 | result[ip] = append(result[ip], strings.TrimSpace(vul)) 447 | } 448 | } 449 | 450 | scriptOutput = strings.Replace(scriptOutput, "\n", "\\n", -1) 451 | info := fmt.Sprintf("/script|%v;;out|%v", id, scriptOutput) 452 | result[ip] = append(result[ip], strings.TrimSpace(info)) 453 | } 454 | }) 455 | } 456 | }) 457 | 458 | return result 459 | } 460 | -------------------------------------------------------------------------------- /modules/nmap_test.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davecgh/go-spew/spew" 6 | "github.com/j3ssie/metabigor/core" 7 | jsoniter "github.com/json-iterator/go" 8 | "testing" 9 | ) 10 | 11 | func TestParseNmapXML(t *testing.T) { 12 | raw := core.GetFileContent("/tmp/nn/vuln.xml") 13 | nmapRun := ParseNmapXML(raw) 14 | spew.Dump(nmapRun.Host) 15 | fmt.Println("------------") 16 | spew.Dump(nmapRun.Host.Ports) 17 | //if len(result) == 0 { 18 | // t.Errorf("Error ParsingMasscan") 19 | //} 20 | } 21 | 22 | func TestGetHost(t *testing.T) { 23 | raw := core.GetFileContent("/var/folders/lx/q7xk40_d3vd_wvw5dpdj796r0000gn/T/mtg-log/nmap-77.111.191.237-378284373.xml") 24 | //raw := core.GetFileContent("/tmp/nn/full.xml") 25 | host := GetHost(raw) 26 | spew.Dump(host) 27 | 28 | fmt.Println("------------") 29 | 30 | // output as JSON 31 | if data, err := jsoniter.MarshalToString(host); err == nil { 32 | fmt.Println(data) 33 | } 34 | 35 | if len(host.Ports) > 0 { 36 | for _, port := range host.Ports { 37 | info := fmt.Sprintf("%v:%v/%v/%v", host.IPAddress, port.PortID, port.Protocol, port.Service.Product) 38 | fmt.Println(info) 39 | } 40 | } 41 | } 42 | 43 | func TestGetHosts(t *testing.T) { 44 | raw := core.GetFileContent("/tmp/nn/multiple.xml") 45 | hosts := GetHosts(raw) 46 | //spew.Dump(host) 47 | 48 | for _, host := range hosts { 49 | fmt.Println("------------") 50 | // output as JSON 51 | if data, err := jsoniter.MarshalToString(host); err == nil { 52 | fmt.Println(data) 53 | } 54 | 55 | if len(host.Ports) > 0 { 56 | for _, port := range host.Ports { 57 | info := fmt.Sprintf("%v:%v/%v/%v", host.IPAddress, port.PortID, port.Protocol, port.Service.Product) 58 | fmt.Println(info) 59 | } 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /modules/related.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "regexp" 7 | "strings" 8 | 9 | "github.com/PuerkitoBio/goquery" 10 | "github.com/fatih/color" 11 | "github.com/j3ssie/metabigor/core" 12 | jsoniter "github.com/json-iterator/go" 13 | "github.com/thoas/go-funk" 14 | ) 15 | 16 | // CrtSH get IPInfo from https://crt.sh 17 | func CrtSH(raw string, options core.Options) (result []core.RelatedDomain) { 18 | technique := "certificate" 19 | core.InforF("Get more related domains with technique: " + color.HiMagentaString("%v:%v", technique, raw)) 20 | 21 | targetURL := fmt.Sprintf(`https://crt.sh/?O=%v`, url.QueryEscape(raw)) 22 | core.DebugF("Fetching data from: %v", targetURL) 23 | content, err := core.GetResponse(targetURL, options) 24 | if content == "" || err != nil { 25 | core.ErrorF("Error sending request to: %v", targetURL) 26 | return result 27 | } 28 | 29 | infos := ParseCertSH(content, options) 30 | for _, info := range infos { 31 | 32 | var tldInfo core.RelatedDomain 33 | if data, err := jsoniter.MarshalToString(info); err == nil { 34 | tldInfo.RawData = data 35 | } 36 | tldInfo.Domain = info.Domain 37 | tldInfo.Technique = technique 38 | tldInfo.Source = "https://crt.sh" 39 | tldInfo.Output = fmt.Sprintf("[%s] %s ;; %s ;; %s", technique, info.Domain, info.Org, info.CertInfo) 40 | result = append(result, tldInfo) 41 | } 42 | return result 43 | } 44 | 45 | func ReverseWhois(raw string, options core.Options) (result []core.RelatedDomain) { 46 | technique := "whois" 47 | core.InforF("Get more related domains with technique: " + color.HiMagentaString("%v:%v", technique, raw)) 48 | 49 | targetURL := fmt.Sprintf(`https://viewdns.info/reversewhois/?q=%v`, url.QueryEscape(raw)) 50 | core.DebugF("Fetching data from: %v", targetURL) 51 | content, err := core.GetResponse(targetURL, options) 52 | if content == "" || err != nil { 53 | core.ErrorF("Error sending request to: %v", targetURL) 54 | return result 55 | } 56 | 57 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) 58 | doc.Find("tr").Each(func(i int, s *goquery.Selection) { 59 | var tldInfo core.RelatedDomain 60 | s.Find("td").Each(func(j int, td *goquery.Selection) { 61 | if strings.Count(td.Text(), "\n") > 1 || len(td.Text()) > 1000 { 62 | return 63 | } 64 | 65 | if j == 0 { 66 | tldInfo.Domain = strings.TrimSpace(td.Text()) 67 | } 68 | 69 | tldInfo.Technique = technique 70 | tldInfo.Source = "https://viewdns.info" 71 | 72 | var createdDate, description string 73 | if j == 1 { 74 | createdDate = strings.TrimSpace(td.Text()) 75 | } 76 | if j == 2 { 77 | description = strings.TrimSpace(td.Text()) 78 | } 79 | tldInfo.RawData = fmt.Sprintf("%s ;; %s ;; %s", tldInfo.Domain, createdDate, description) 80 | tldInfo.Output = fmt.Sprintf("[%s] %s ;; %s ;; %s", technique, tldInfo.Domain, createdDate, description) 81 | }) 82 | 83 | if strings.Contains(tldInfo.Domain, ".") { 84 | result = append(result, tldInfo) 85 | } 86 | }) 87 | return result 88 | } 89 | 90 | func GoogleAnalytic(targetURL string, options core.Options) (result []core.RelatedDomain) { 91 | if strings.HasPrefix(targetURL, "UA-") { 92 | return BuiltwithUA(targetURL, options) 93 | } 94 | 95 | if !strings.HasPrefix(targetURL, "http") { 96 | targetURL = fmt.Sprintf("https://%s", strings.TrimSpace(targetURL)) 97 | } 98 | core.InforF("Extract Google Analytics ID from: %s", targetURL) 99 | content, err := core.GetResponse(targetURL, options) 100 | if content == "" || err != nil { 101 | return result 102 | } 103 | 104 | core.DebugF("Extracting UA-ID from: %v", targetURL) 105 | UAIds := ExtractUAID(content) 106 | 107 | core.DebugF("Extracting GoogleTagManager from: %v", targetURL) 108 | gtms := ExtractGoogleTagManger(content) 109 | for _, gtm := range gtms { 110 | core.DebugF("Extracting UA-ID from: %v", gtm) 111 | content, err := core.GetResponse(gtm, options) 112 | if err == nil { 113 | UAIds = append(UAIds, ExtractUAID(content)...) 114 | } 115 | } 116 | 117 | UAIds = funk.UniqString(UAIds) 118 | 119 | // clean up UA IDs 120 | var cleanedUAIds []string 121 | for _, rawUAId := range UAIds { 122 | UAId := strings.Join(strings.Split(rawUAId, "-")[:2], "-") 123 | cleanedUAIds = append(cleanedUAIds, UAId) 124 | } 125 | 126 | cleanedUAIds = funk.UniqString(cleanedUAIds) 127 | for _, uaId := range cleanedUAIds { 128 | result = append(result, BuiltwithUA(uaId, options)...) 129 | } 130 | 131 | return result 132 | } 133 | 134 | func BuiltwithUA(UAID string, options core.Options) (result []core.RelatedDomain) { 135 | technique := "google-analytic" 136 | core.InforF("Get more related domains with technique: " + color.HiMagentaString("%v:%v", technique, UAID)) 137 | dataURL := fmt.Sprintf(`https://builtwith.com/relationships/tag/%v`, url.QueryEscape(UAID)) 138 | core.DebugF("Fetching data from: %v", dataURL) 139 | content, err := core.GetResponse(dataURL, options) 140 | // simple retry if something went wrong 141 | if err != nil { 142 | content, err = core.GetResponse(dataURL, options) 143 | } 144 | if content == "" { 145 | core.ErrorF("Error sending request to: %v", dataURL) 146 | return result 147 | } 148 | 149 | regex := regexp.MustCompile(`/relationships/[a-z0-9\-\_\.]+\.[a-z]+`) 150 | rawDomains := regex.FindAllStringSubmatch(content, -1) 151 | if len(rawDomains) == 0 { 152 | core.ErrorF("Error extracting domains from: %v", dataURL) 153 | } 154 | 155 | for _, domain := range rawDomains { 156 | var tldInfo core.RelatedDomain 157 | cleanedDomain := strings.ReplaceAll(domain[0], "/relationships/", "") 158 | tldInfo.Domain = cleanedDomain 159 | tldInfo.Technique = technique 160 | tldInfo.Source = dataURL 161 | tldInfo.Output = fmt.Sprintf("[%s] %s ;; %s", technique, tldInfo.Domain, dataURL) 162 | result = append(result, tldInfo) 163 | } 164 | 165 | return result 166 | } 167 | -------------------------------------------------------------------------------- /modules/scan.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "os/user" 9 | "path/filepath" 10 | "strings" 11 | "text/template" 12 | 13 | "github.com/thoas/go-funk" 14 | 15 | jsoniter "github.com/json-iterator/go" 16 | 17 | "github.com/j3ssie/metabigor/core" 18 | ) 19 | 20 | // CurrentUser get current user 21 | func CurrentUser() string { 22 | u, err := user.Current() 23 | if err != nil { 24 | return "" 25 | } 26 | 27 | username := u.Username 28 | return username 29 | } 30 | 31 | // RunRustScan run masscan command and return list of port open 32 | func RunRustScan(input string, options core.Options) (results []string) { 33 | // rustscan --timeout 3000 -b {{.rateRustScan}} --scripts None --range {{.ports}} -a {{.inputFile}} -g >> {{.Output}}/portscan/raw-open-ports.txt" 34 | tmpOutput := options.Scan.TmpOutput 35 | tmpFile, _ := os.CreateTemp(options.Scan.TmpOutput, "rustscan-*.txt") 36 | if tmpOutput != "" { 37 | tmpFile, _ = os.CreateTemp(tmpOutput, fmt.Sprintf("rustscan-%v-*.txt", core.StripPath(input))) 38 | } 39 | tmpOutput = tmpFile.Name() 40 | 41 | ports := fmt.Sprintf("--range %v", options.Scan.Ports) 42 | if strings.Contains(options.Scan.Ports, ",") || !strings.Contains(options.Scan.Ports, "-") { 43 | ports = fmt.Sprintf("--ports %v", options.Scan.Ports) 44 | } 45 | 46 | prefix := fmt.Sprintf("rustscan -b %v", options.Scan.Rate) 47 | if options.Scan.Timeout != "" { 48 | prefix += fmt.Sprintf(" --timeout %v", options.Scan.Timeout) 49 | } else { 50 | prefix += " --timeout 3000 " 51 | } 52 | if options.Scan.Retry != "" { 53 | prefix += fmt.Sprintf(" --tries %v", options.Scan.Retry) 54 | } 55 | 56 | rustscanCmd := fmt.Sprintf("%v %v --scripts None -a %v -g >> %v", prefix, ports, input, tmpOutput) 57 | runOSCommand(rustscanCmd) 58 | 59 | core.InforF("Parsing result from: %v", tmpOutput) 60 | data := core.GetFileContent(tmpOutput) 61 | 62 | if options.PipeTheOutput { 63 | fmt.Printf(data) 64 | return results 65 | } 66 | 67 | var content []string 68 | if strings.Contains(data, "\n") { 69 | content = strings.Split(data, "\n") 70 | } 71 | 72 | for _, line := range content { 73 | // 1.2.3.4 -> [80,80,443,443] 74 | if !strings.Contains(line, " -> ") { 75 | continue 76 | } 77 | 78 | ip := strings.Split(line, " -> ")[0] 79 | rPorts := strings.Split(line, " -> ")[1] 80 | rPorts = rPorts[1 : len(rPorts)-1] 81 | 82 | if !strings.Contains(rPorts, ",") { 83 | results = append(results, fmt.Sprintf("%s:%s", ip, rPorts)) 84 | } 85 | 86 | ports := strings.Split(rPorts, ",") 87 | ports = funk.UniqString(ports) 88 | for _, port := range ports { 89 | results = append(results, fmt.Sprintf("%s:%s", ip, port)) 90 | } 91 | } 92 | 93 | return results 94 | } 95 | 96 | func RunMasscan(input string, options core.Options) []string { 97 | ports := options.Scan.Ports 98 | rate := options.Scan.Rate 99 | if ports == "" { 100 | ports = "443" 101 | } 102 | 103 | massOutput := options.Scan.TmpOutput 104 | tmpFile, _ := os.CreateTemp(options.Scan.TmpOutput, "masscan-*.txt") 105 | if massOutput != "" { 106 | tmpFile, _ = os.CreateTemp(massOutput, fmt.Sprintf("masscan-%v-*.txt", core.StripPath(input))) 107 | } 108 | massOutput = tmpFile.Name() 109 | 110 | masscanCmd := fmt.Sprintf("masscan --rate %v -p %v -oG %v %v", rate, ports, massOutput, input) 111 | if options.Scan.All { 112 | masscanCmd = fmt.Sprintf("masscan --rate %v -p %v -oG %v -iL %v", rate, ports, massOutput, input) 113 | } 114 | if CurrentUser() != "root" { 115 | masscanCmd = "sudo " + masscanCmd 116 | } 117 | runOSCommand(masscanCmd) 118 | 119 | // parse output 120 | var realResult []string 121 | result := make(map[string]string) 122 | if !core.FileExists(massOutput) { 123 | core.ErrorF("Output not found: %v", massOutput) 124 | return realResult 125 | } 126 | core.InforF("Parsing result from: %v", massOutput) 127 | data := core.GetFileContent(massOutput) 128 | rawResult := ParsingMasscan(data) 129 | 130 | if len(rawResult) == 0 { 131 | core.ErrorF("Output not found: %v", massOutput) 132 | } 133 | 134 | // get flat output for easily parse to other tools 135 | if options.Scan.Flat { 136 | for k, v := range rawResult { 137 | for _, port := range v { 138 | info := fmt.Sprintf("%v:%v", k, port) 139 | realResult = append(realResult, info) 140 | } 141 | } 142 | return realResult 143 | } 144 | 145 | // group them by host in verbose mode 146 | for k, v := range rawResult { 147 | result[k] += fmt.Sprintf("%v", strings.Join(v, ",")) 148 | } 149 | for k, v := range result { 150 | realResult = append(realResult, fmt.Sprintf("%v - %v", k, v)) 151 | } 152 | 153 | return realResult 154 | } 155 | 156 | // RunNmap run nmap command and return list of port open 157 | func RunNmap(input string, ports string, options core.Options) []string { 158 | // use nmap as overview scan 159 | if options.Scan.NmapOverview { 160 | ports = options.Scan.Ports 161 | } 162 | if ports == "" { 163 | ports = "443" 164 | } 165 | nmapOutput := options.Scan.TmpOutput 166 | tmpFile, _ := os.CreateTemp(options.Scan.TmpOutput, "nmap-*") 167 | if nmapOutput != "" { 168 | tmpFile, _ = os.CreateTemp(nmapOutput, fmt.Sprintf("nmap-%v-*", core.StripPath(input))) 169 | } 170 | nmapOutput = tmpFile.Name() 171 | 172 | // build nmap command 173 | if options.Scan.All { 174 | options.Scan.NmapTemplate = "nmap -sSV -p {{.ports}} -iL {{.input}} {{.script}} -T4 --open -oA {{.output}}" 175 | } 176 | nmapCommand := make(map[string]string) 177 | nmapCommand["output"] = nmapOutput 178 | nmapCommand["ports"] = ports 179 | nmapCommand["input"] = input 180 | if options.Scan.NmapScripts != "" { 181 | nmapCommand["script"] = fmt.Sprintf("--script %v", options.Scan.NmapScripts) 182 | } else { 183 | nmapCommand["script"] = "" 184 | } 185 | nmapCmd := ResolveData(options.Scan.NmapTemplate, nmapCommand) 186 | if CurrentUser() != "root" { 187 | nmapCmd = "sudo " + nmapCmd 188 | } 189 | 190 | // 191 | //nmapCmd := fmt.Sprintf("sudo nmap -sSV -p %v %v -T4 --open -oA %v", ports, input, nmapOutput) 192 | //if options.Scan.NmapScripts != "" { 193 | // nmapCmd = fmt.Sprintf("sudo nmap -sSV -p %v %v --script %v -T4 --open -oA %v", ports, input, options.Scan.NmapScripts, nmapOutput) 194 | //} 195 | 196 | runOSCommand(nmapCmd) 197 | var result []string 198 | realNmapOutput := nmapOutput + ".xml" 199 | if !core.FileExists(realNmapOutput) { 200 | core.ErrorF("Result not found: %v", realNmapOutput) 201 | return result 202 | } 203 | 204 | data := core.GetFileContent(realNmapOutput) 205 | result = ParseNmap(data, options) 206 | return result 207 | } 208 | 209 | // ParseNmap parse nmap XML output 210 | func ParseNmap(raw string, options core.Options) []string { 211 | var result []string 212 | var hosts []Host 213 | if strings.Count(raw, " 1 { 214 | hosts = append(hosts, GetHosts(raw)...) 215 | } else { 216 | hosts = append(hosts, GetHost(raw)) 217 | } 218 | 219 | for _, host := range hosts { 220 | //spew.Dump(host) 221 | if len(host.Ports) <= 0 { 222 | core.ErrorF("No open port found for %v", host.IPAddress) 223 | continue 224 | } 225 | if options.JsonOutput { 226 | if data, err := jsoniter.MarshalToString(host); err == nil { 227 | result = append(result, data) 228 | } 229 | continue 230 | } 231 | 232 | for _, port := range host.Ports { 233 | info := fmt.Sprintf("%v:%v/%v/%v", host.IPAddress, port.PortID, port.Protocol, port.Service.Product) 234 | result = append(result, info) 235 | } 236 | } 237 | return result 238 | } 239 | 240 | // ParsingMasscan parse result from masscan XML format 241 | func ParsingMasscan(raw string) map[string][]string { 242 | result := make(map[string][]string) 243 | data := strings.Split(raw, "\n") 244 | if len(data) == 0 { 245 | core.ErrorF("Invalid Masscan data") 246 | return result 247 | } 248 | 249 | for _, line := range data { 250 | if !strings.Contains(line, "Host: ") { 251 | continue 252 | } 253 | rawResult := strings.Split(line, " ") 254 | ip := rawResult[1] 255 | port := strings.Split(rawResult[len(rawResult)-1], "/")[0] 256 | result[ip] = append(result[ip], port) 257 | } 258 | 259 | return result 260 | } 261 | 262 | // RunZmap run masscan command and return list of port open 263 | func RunZmap(inputFile string, port string, options core.Options) []string { 264 | ports := options.Scan.Ports 265 | if ports == "" { 266 | ports = "443" 267 | } 268 | zmapOutput := options.Scan.TmpOutput 269 | tmpFile, _ := os.CreateTemp(options.Scan.TmpOutput, "out-*.txt") 270 | if zmapOutput != "" { 271 | tmpFile, _ = os.CreateTemp(zmapOutput, fmt.Sprintf("out-%v-*.txt", core.StripPath(filepath.Base(inputFile)))) 272 | } 273 | zmapOutput = tmpFile.Name() 274 | zmapCmd := fmt.Sprintf("zmap -p %v -w %v -f 'saddr,sport' -O csv -o %v", port, inputFile, zmapOutput) 275 | if CurrentUser() != "root" { 276 | zmapCmd = "sudo " + zmapCmd 277 | } 278 | runOSCommand(zmapCmd) 279 | 280 | result := ParseZmap(zmapOutput) 281 | return result 282 | } 283 | 284 | // ParseZmap parsse zmap data 285 | func ParseZmap(zmapOutput string) []string { 286 | data := core.GetFileContent(zmapOutput) 287 | var result []string 288 | if strings.TrimSpace(data) == "" { 289 | return result 290 | } 291 | 292 | raw := strings.Replace(data, ",", ":", -1) 293 | raw = strings.Replace(raw, "saddr:sport", "", -1) 294 | raw = strings.TrimSpace(raw) 295 | 296 | result = strings.Split(raw, "\n") 297 | return result 298 | } 299 | 300 | // ResolveData resolve template from signature file 301 | func ResolveData(format string, data map[string]string) string { 302 | t := template.Must(template.New("").Parse(format)) 303 | buf := &bytes.Buffer{} 304 | err := t.Execute(buf, data) 305 | if err != nil { 306 | return format 307 | } 308 | return buf.String() 309 | } 310 | 311 | func runOSCommand(cmd string) { 312 | core.DebugF("Execute: %v", cmd) 313 | command := []string{ 314 | "bash", 315 | "-c", 316 | cmd, 317 | } 318 | exec.Command(command[0], command[1:]...).CombinedOutput() 319 | } 320 | -------------------------------------------------------------------------------- /modules/scan_test.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/j3ssie/metabigor/core" 8 | ) 9 | 10 | func TestRunMasscan(t *testing.T) { 11 | var options core.Options 12 | options.Input = "103.102.128.0/24" 13 | result := RunMasscan("103.102.128.0/24", options) 14 | if len(result) == 0 { 15 | t.Errorf("Error RunMasscan") 16 | } 17 | } 18 | func TestParsingNmap(t *testing.T) { 19 | var options core.Options 20 | options.Scan.NmapScripts = "vulners.nse" 21 | // options.Input = "103.102.128.0/24" 22 | raw := core.GetFileContent("/tmp/testttt/samm.xml") 23 | result := ParseNmap(raw, options) 24 | fmt.Println(result) 25 | if len(result) == 0 { 26 | t.Errorf("Error RunMasscan") 27 | } 28 | } 29 | 30 | func TestParseMassScan(t *testing.T) { 31 | raw := core.GetFileContent("/tmp/mtt/mrp") 32 | result := ParsingMasscan(raw) 33 | fmt.Println(result) 34 | if len(result) == 0 { 35 | t.Errorf("Error ParsingMasscan") 36 | } 37 | } 38 | 39 | func TestParseZmap(t *testing.T) { 40 | result := ParseZmap("/tmp/nn/p443") 41 | fmt.Println(result) 42 | if len(result) == 0 { 43 | t.Errorf("Error ParsingMasscan") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /modules/static/ip2asn-combined.tsv.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j3ssie/metabigor/607b2c9f7352d9377f3d9aec6ffc50da0b425d3e/modules/static/ip2asn-combined.tsv.gz --------------------------------------------------------------------------------