├── .github └── workflows │ └── python-publish.yml ├── Docker ├── Dockerfile └── README.md ├── LICENSE ├── README.md ├── img ├── subdominator-v2.png └── subdominator.png ├── requirements.txt ├── setup.py └── subdominator ├── __init__.py ├── modules ├── __init__.py ├── banner │ ├── __init__.py │ └── banner.py ├── cli │ ├── __init__.py │ └── cli.py ├── config │ ├── __init__.py │ └── config.py ├── crud │ ├── __init__.py │ └── crud.py ├── handler.py ├── help │ ├── __init__.py │ └── help.py ├── logger │ ├── __init__.py │ └── logger.py ├── models │ ├── __init__.py │ └── models.py ├── notify │ ├── __init__.py │ └── notify.py ├── save │ ├── __init__.py │ └── save.py ├── shell │ ├── __init__.py │ └── shell.py ├── source │ ├── __init__.py │ └── source.py ├── subscraper │ ├── __init__.py │ ├── abuseipdb │ │ ├── __init__.py │ │ └── abuseipdb.py │ ├── alienvault │ │ ├── __init__.py │ │ └── alientvault.py │ ├── anubis │ │ ├── __init__.py │ │ └── anubis.py │ ├── arpsyndicate │ │ ├── __init__.py │ │ └── arpsyndicate.py │ ├── bevigil │ │ ├── __init__.py │ │ └── bevigil.py │ ├── binaryedge │ │ ├── __init__.py │ │ └── binaryedge.py │ ├── bufferover │ │ ├── __init__.py │ │ └── bufferover.py │ ├── builtwith │ │ ├── __init__.py │ │ └── builtwith.py │ ├── c99 │ │ ├── __init__.py │ │ └── c99.py │ ├── censys │ │ ├── __init__.py │ │ └── censys.py │ ├── certspotter │ │ ├── __init__.py │ │ └── certspotter.py │ ├── chaos │ │ ├── __init__.py │ │ └── chaos.py │ ├── coderog │ │ ├── __init__.py │ │ └── coderog.py │ ├── commoncrawl │ │ ├── __init__.py │ │ └── commoncrawl.py │ ├── crtsh │ │ ├── __init__.py │ │ └── crtsh.py │ ├── cyfare │ │ ├── __init__.py │ │ └── cyfare.py │ ├── digitalyama │ │ ├── __init__.py │ │ └── digitalyama.py │ ├── digitorus │ │ ├── __init__.py │ │ └── digitorus.py │ ├── dnsdumpster │ │ ├── __init__.py │ │ └── dnsdumpster.py │ ├── dnsrepo │ │ ├── __init__.py │ │ └── dnsrepo.py │ ├── facebook │ │ ├── __init__.py │ │ └── facebook.py │ ├── fofa │ │ ├── __init__.py │ │ └── fofa.py │ ├── fullhunt │ │ ├── __init__.py │ │ └── fullhunt.py │ ├── google │ │ ├── __init__.py │ │ └── google.py │ ├── hackertarget │ │ ├── __init__.py │ │ └── hackertarget.py │ ├── hudsonrock │ │ ├── __init__.py │ │ └── hudsonrock.py │ ├── huntermap │ │ ├── __init__.py │ │ └── huntermap.py │ ├── intelx │ │ ├── __init__.py │ │ └── intelx.py │ ├── leakix │ │ ├── __init__.py │ │ └── leakix.py │ ├── merklemap │ │ ├── __init__.py │ │ └── merklemap.py │ ├── myssl │ │ ├── __init__.py │ │ └── myssl.py │ ├── netlas │ │ ├── __init__.py │ │ └── netlas.py │ ├── odin │ │ ├── __init__.py │ │ └── odin.py │ ├── quake │ │ ├── __init__.py │ │ └── quake.py │ ├── racent │ │ ├── __init__.py │ │ └── racent.py │ ├── rapidapi │ │ ├── __init__.py │ │ └── rapidapi.py │ ├── rapiddns │ │ ├── __init__.py │ │ └── rapiddns.py │ ├── rapidfinder │ │ ├── __init__.py │ │ └── rapidfinder.py │ ├── rapidscan │ │ ├── __init__.py │ │ └── rapidscan.py │ ├── redhuntlabs │ │ ├── __init__.py │ │ └── redhuntlabs.py │ ├── rsecloud │ │ ├── __init__.py │ │ └── rsecloud.py │ ├── securitytrails │ │ ├── __init__.py │ │ └── securitytrails.py │ ├── shodan │ │ ├── __init__.py │ │ └── shodan.py │ ├── shodanx │ │ ├── __init__.py │ │ └── shodanx.py │ ├── shrewdeye │ │ ├── __init__.py │ │ ├── shrewdeye.py │ │ └── zoomeyeapi │ │ │ ├── __init__.py │ │ │ └── zoomeyeapi.py │ ├── sitedossier │ │ ├── __init__.py │ │ └── sitedossier.py │ ├── threatcrowd │ │ ├── __init__.py │ │ └── threatcrowd.py │ ├── trickest │ │ ├── __init__.py │ │ └── trickest.py │ ├── urlscan │ │ ├── __init__.py │ │ └── urlscan.py │ ├── virustotal │ │ ├── __init__.py │ │ └── virustotal.py │ ├── waybackarchive │ │ ├── __init__.py │ │ └── waybackarchive.py │ ├── whoisxml │ │ ├── __init__.py │ │ └── whoisxml.py │ └── zoomeyeapi │ │ ├── __init__.py │ │ └── zoomeyeapi.py ├── update │ ├── __init__.py │ └── update.py ├── utils │ ├── __init__.py │ └── utils.py └── version │ ├── __init__.py │ └── version.py └── subdominator.py /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | name: Release to PyPI 2 | 3 | on: 4 | 5 | release: 6 | 7 | types: 8 | - created 9 | 10 | jobs: 11 | 12 | deploy: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | 18 | - name: Check out code 19 | 20 | uses: actions/checkout@v2 21 | 22 | 23 | - name: Set up Python 24 | 25 | uses: actions/setup-python@v2 26 | 27 | with: 28 | 29 | python-version: 3.13.2 30 | 31 | - name: Install dependencies 32 | run: | 33 | python3 -m pip install --upgrade pip 34 | pip install setuptools wheel twine 35 | 36 | - name: Build and publish 37 | run: | 38 | python3 setup.py sdist bdist_wheel 39 | python3 -m twine upload dist/*.tar.gz 40 | env: 41 | 42 | TWINE_USERNAME: ${{ secrets.PYPI_USER }} 43 | 44 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 45 | -------------------------------------------------------------------------------- /Docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim 2 | WORKDIR /app 3 | COPY . /app 4 | 5 | RUN apt-get update && apt-get install -y git 6 | RUN pip install --no-cache-dir --upgrade pip 7 | RUN pip install --no-cache-dir tldextract aiofiles>=23.2.1 aiohttp>=3.9.4 appdirs>=1.4.4 httpx>=0.27.2 art>=6.1 beautifulsoup4>=4.11.1 colorama>=0.4.6 fake_useragent>=1.5.0 PyYAML>=6.0.1 Requests>=2.31.0 rich>=13.7.1 urllib3>=1.26.18 8 | RUN pip install git+https://github.com/RevoltSecurities/Subdominator 9 | 10 | # Clean up 11 | RUN apt-get clean && rm -rf /var/lib/apt/lists/* 12 | 13 | ENTRYPOINT ["subdominator"] -------------------------------------------------------------------------------- /Docker/README.md: -------------------------------------------------------------------------------- 1 | # Subdominator - Docker Usage 2 | This README provides instructions for using the **Subdominator** tool inside a Docker container for subdomain enumeration. 3 | 4 | ## Installation 5 | To build the Docker image, run the following command: 6 | 7 | ```bash 8 | sudo docker build -t subdominator . 9 | ``` 10 | This will create the `subdominator` image using the `Dockerfile` in the current directory. 11 | 12 | ## Docker Commands 13 | 14 | ### 1. **Subdomain Enumeration for a Single Domain** 15 | To perform subdomain enumeration for a **single domain** and save the output to a file, use: 16 | 17 | ```bash 18 | sudo docker run --rm -it -v $(pwd):/output subdominator -d redacted.com --output /output/outfile.txt 19 | ``` 20 | 21 | Explanation: 22 | - **`-d redacted.com`**: Specifies the domain for subdomain enumeration. 23 | - **`--output /output/outfile.txt`**: Saves the output to a file. The `$(pwd)` mounts the current working directory to `/output` inside the container. 24 | 25 | ### 2. **Subdomain Enumeration for a List of Domains** 26 | 27 | If you have a **list of root domains** in a file (e.g., `input.txt`) and want to enumerate subdomains for all domains, use the following command: 28 | 29 | ```bash 30 | sudo docker run --rm -v /home/pugal/tools/Subdominator/input.txt:/input.txt -v /home/pugal/tools/Subdominator/output-directory:/output-directory subdominator -dL /input.txt -oD /output-directory 31 | ``` 32 | 33 | Explanation: 34 | - **`-dL /input.txt`**: Specifies the file containing the list of domains. 35 | - **`-oD /output-directory`**: Specifies the directory to save the output. The directory is mounted from your local machine to the Docker container. 36 | 37 | ### 3. **Subdomain Enumeration with Output in JSON Format** 38 | 39 | If you want to save the output in **JSON format**, use the `-oJ` flag: 40 | 41 | ```bash 42 | sudo docker run --rm -it -v $(pwd):/output subdominator -d thx.com -oJ /output/outfile.json 43 | ``` 44 | 45 | Explanation: 46 | - **`-d thx.com`**: Specifies the domain for subdomain enumeration. 47 | - **`-oJ /output/outfile.json`**: Saves the output in JSON format. 48 | 49 | ## Example with Custom Configuration 50 | 51 | If you want to specify a custom configuration file for API keys, you can mount the configuration file into the container: 52 | 53 | ```bash 54 | sudo docker run --rm -v /path/to/config:/custom-config subdominator -cp /custom-config/config.yaml -d example.com --output /output/outfile.txt 55 | ``` 56 | 57 | ### Explanation: 58 | - **`-cp /custom-config/config.yaml`**: Specifies the custom path for the configuration file. 59 | 60 | ## Notes 61 | - Replace `/path/to/config` and `/home/pugal/tools/Subdominator/` with the actual paths on your system. 62 | - Ensure the output directory (`/output` or `/output-directory`) exists on your local machine or Docker will create it inside the container. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 RevoltSecurities 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Subdominator - Unleash the Power of Subdomain Enumeration 2 | 3 |

4 | Subdominator 5 |
6 |

7 | 8 |
9 | 10 | **lightweight , fast and more than a passive subdomain enumeration tool** 11 | 12 |
13 | 14 | 15 |

16 | Features | 17 | Usage | 18 | Installation | 19 | Documentation 20 |

21 | 22 |
23 | 24 | ![GitHub last commit](https://img.shields.io/github/last-commit/RevoltSecurities/Subdominator) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/RevoltSecurities/Subdominator) [![GitHub license](https://img.shields.io/github/license/RevoltSecurities/Subdominator)](https://github.com/RevoltSecurities/Subdominator/blob/main/LICENSE) 25 | 26 |
27 | 28 | 29 | Subdominator is a powerful tool for passive subdomain enumeration during bug hunting and reconnaissance processes. It is designed to help researchers and cybersecurity professionals discover potential security vulnerabilities by efficiently enumerating subdomains some various free passive resources. 30 | 31 | ### Features: 32 | --- 33 | 34 |

35 | 36 |
37 |

38 | 39 | - fast and powerfull to enumerate subdomains. 40 | - 50+ passive results to enumerate subdomains. 41 | - configurable API keys setup 42 | - integrated notification system 43 | - local Database support to store data 44 | - supports multiple output format (txt|html|pdf|json) 45 | 46 | ### Yaml Updates: 47 | We request existing user to update their config yaml file with new resources by opening the config file in : ```$HOME/.config/Subdominator/provider-config.yaml``` and add the below resources: 48 | 49 | ```yaml 50 | builwith: 51 | - your-api-key1 52 | - your-api-key2 53 | 54 | passivetotal: 55 | - user-mail1:api-key1 56 | - user-mail2:api-key2 57 | 58 | trickest: 59 | - your-api-key1 60 | - your-api-key2 61 | ``` 62 | by these your config yaml file will get updated or else check your yaml file that matches the below mentioned resources with *, The new users will required to update in next version if any new resources added 63 | in Subdominator. 64 | 65 | 66 | ### Usage: 67 | --- 68 | ```code 69 | subdominator -h 70 | ``` 71 | ```yaml 72 | | | _) | 73 | __| | | __ \ _` | _ \ __ `__ \ | __ \ _` | __| _ \ __| 74 | \__ \ | | | | ( | ( | | | | | | | ( | | ( | | 75 | ____/ \__,_| _.__/ \__,_| \___/ _| _| _| _| _| _| \__,_| \__| \___/ _| 76 | 77 | 78 | @RevoltSecurities 79 | 80 | 81 | 82 | [DESCRIPTION]: Subdominator a passive subdomain enumeration that discovers subdomains for your targets using with passive and open source resources 83 | 84 | [USAGE]: 85 | 86 | subdominator [flags] 87 | 88 | [FLAGS]: 89 | 90 | [INPUT]: 91 | 92 | -d, --domain : Target domain name for subdomain enumeration. 93 | -dL, --domain-list : File containing multiple domains for bulk enumeration. 94 | stdin/stdout : Supports input/output redirection. 95 | 96 | [OUTPUT]: 97 | 98 | -o, --output : Save results to a file. 99 | -oD, --output-directory : Directory to save results (useful when -dL is specified). 100 | -json, --json : Output results in JSON format. 101 | 102 | [MODE]: 103 | 104 | -shell, --shell : Enable interactive shell mode to work with subdominator Database,generate report and etc. 105 | 106 | [OPTIMIZATION]: 107 | 108 | -t, --timeout : Set timeout value for API requests (default: 30s). 109 | -fw, --filter-wildcards : Filter out wildcard subdomains. 110 | 111 | [CONFIGURATION]: 112 | 113 | -cp, --config-path : Custom config file path for API keys (default: /home/sanjai/.config/Subdominator/provider-config.yaml). 114 | -cdp, --config-db-path : Custom database config path (default: /home/sanjai/.cache/SubdominatorDB/subdominator.db). 115 | -nt, --notify : Send notifications for found subdomains via Slack, Pushbullet. 116 | -px, --proxy : Use an HTTP proxy for debugging requests. 117 | -dork, --dork : Use a custom google dork for google resource (ex: -ir google --dork 'site:target.com -www -dev intext:secrets') 118 | 119 | [RESOURCE CONFIGURATION]: 120 | 121 | -ir, --include-resources : Specify sources to include (comma-separated). 122 | -er, --exclude-resources : Specify sources to exclude (comma-separated). 123 | -all, --all : Use all available sources for enumeration. 124 | 125 | [UPDATE]: 126 | 127 | -up, --update : Update Subdominator to the latest version (manual YAML update required). 128 | -duc, --disable-update-check : Disable automatic update checks. 129 | -sup, --show-updates : Show the latest update details. 130 | 131 | [DEBUGGING]: 132 | 133 | -h, --help : Show this help message and exit. 134 | -v, --version : Show the current version and check for updates. 135 | -s, --silent : Show only subdomains in output. 136 | -ski, --show-key-info : Show API key errors (e.g., out of credits). 137 | -sti, --show-timeout-info : Show timeout errors for sources. 138 | -nc, --no-color : Disable colorized output. 139 | -ls, --list-source : List available subdomain enumeration sources. 140 | -V, --verbose : Enable verbose output. 141 | ``` 142 | 143 | 144 | ### Subdominator Integrations: 145 | --- 146 | 147 | **The following API services used by subdominator**: 148 | 149 | - **AbuseIPDB** → [abuseipdb.com](https://abuseipdb.com) 150 | - **AlienVault** → [otx.alienvault.com](https://otx.alienvault.com) 151 | - **Anubis** → [jldc.me/anubis](https://jldc.me/anubis) 152 | - **ARP Syndicate** → [arpsyndicate.io](https://www.arpsyndicate.io/pricing.html) 153 | - **BeVigil** → [bevigil.com](https://bevigil.com/login) 154 | - **BinaryEdge** → [binaryedge.io](https://binaryedge.io) 155 | - **BufferOver** → [tls.bufferover.run](https://tls.bufferover.run/) 156 | - **BuiltWith** → [api.builtwith.com](https://api.builtwith.com/domain-api) 157 | - **C99** → [subdomainfinder.c99.nl](https://subdomainfinder.c99.nl/) 158 | - **Censys** → [censys.com](https://censys.com/) 159 | - **CertSpotter** → [sslmate.com/certspotter](https://sslmate.com/certspotter/) 160 | - **Chaos** → [chaos.projectdiscovery.io](https://chaos.projectdiscovery.io/) 161 | - **CodeRog** → [rapidapi.com/coderog](https://rapidapi.com/coderog-coderog-default/api/subdomain-finder5/pricing) 162 | - **CommonCrawl** → [index.commoncrawl.org](https://index.commoncrawl.org/) 163 | - **crt.sh** → [crt.sh](https://crt.sh) 164 | - **Cyfare** → [cyfare.net](https://cyfare.net) 165 | - **Digitorus** → [digitorus.com](https://www.digitorus.com/) 166 | - **DigitalYama** → [digitalyama.com](https://digitalyama.com/) 167 | - **DNSDumpster** → [dnsdumpster.com](https://dnsdumpster.com/) 168 | - **DNSRepo** → [dnsarchive.net](https://dnsarchive.net/) 169 | - **Fofa** → [en.fofa.info](https://en.fofa.info/) 170 | - **Facebook** → [developers.facebook.com](https://developers.facebook.com/) 171 | - **FullHunt** → [fullhunt.io](https://fullhunt.io/) 172 | - **Google** → [programmablesearchengine.google.com](https://programmablesearchengine.google.com/controlpanel/create) 173 | - **HackerTarget** → [hackertarget.com](https://hackertarget.com/) 174 | - **HudsonRock** → [cavalier.hudsonrock.com](https://cavalier.hudsonrock.com) 175 | - **HunterMap** → [hunter.how](https://hunter.how/) 176 | - **IntelX** → [intelx.io](https://intelx.io/) 177 | - **LeakIX** → [leakix.net](https://leakix.net/) 178 | - **MerkleMap** → [merklemap.com](https://www.merklemap.com) 179 | - **MySSL** → [myssl.com](https://myssl.com) 180 | - **Netlas** → [netlas.io](https://netlas.io/) 181 | - **Odin** → [odin.io](https://odin.io/) 182 | - **Quake** → [quake.360.cn](https://quake.360.cn/) 183 | - **Racent** → [face.racent.com](https://face.racent.com) 184 | - **RapidAPI** → [rapidapi.com/hub](https://rapidapi.com/hub) 185 | - **RapidDNS** → [rapiddns.io](https://rapiddns.io/) 186 | - **RedHuntLabs** → [devportal.redhuntlabs.com](https://devportal.redhuntlabs.com/) 187 | - **RSECloud** → [rsecloud.com/search](https://rsecloud.com/search) 188 | - **SecurityTrails** → [securitytrails.com](http://securitytrails.com/) 189 | - **Shodan** → [shodan.io](https://shodan.io) 190 | - **ShodanX** → [github.com/RevoltSecurities/Shodanx](https://github.com/RevoltSecurities/Shodanx) 191 | - **ShrewdEye** → [shrewdeye.app](https://shrewdeye.app/api) 192 | - **SiteDossier** → [sitedossier.com](https://sitedossier.com/) 193 | - **ThreatCrowd** → [ci-www.threatcrowd.org](http://ci-www.threatcrowd.org/) 194 | - **Trickest** → [trickest.io](https://trickest.io/) 195 | - **URLScan** → [urlscan.io](https://urlscan.io/) 196 | - **VirusTotal** → [virustotal.com](https://virustotal.com/) 197 | - **WaybackArchive** → [archive.org/wayback](https://archive.org/wayback) 198 | - **WhoisXML** → [whoisxmlapi.com](https://whoisxmlapi.com) 199 | - **ZoomEyeAPI** → [www.zoomeye.hk](https://www.zoomeye.hk/) 200 | 201 | 202 | ### Installation: 203 | --- 204 | 205 | Ensure you have **Python 3.12 or later** installed before proceeding with the installation. You can verify your Python version using: 206 | 207 | ```bash 208 | python3 --version 209 | ``` 210 | 211 | #### ✅ **Install Subdominator from PyPI** (Recommended) 212 | The easiest way to install Subdominator is via PyPI: 213 | 214 | ```bash 215 | pip install --upgrade subdominator 216 | ``` 217 | 218 | #### ✅ **Install the Latest Version from GitHub** 219 | To install the latest development version directly from GitHub: 220 | 221 | ```bash 222 | pip install --upgrade git+https://github.com/RevoltSecurities/Subdominator 223 | ``` 224 | 225 | #### ✅ **Install Using PIPX** (For Isolated Environments) 226 | To avoid dependency conflicts, you can install Subdominator using `pipx`: 227 | 228 | ```bash 229 | pipx install subdominator 230 | ``` 231 | 232 | To install the latest version from GitHub with `pipx`: 233 | 234 | ```bash 235 | pipx install git+https://github.com/RevoltSecurities/Subdominator 236 | ``` 237 | 238 | #### ✅ **Install from Git Source** (For Development) 239 | For users who want to contribute or modify the tool, clone and install directly from source: 240 | 241 | ```bash 242 | git clone https://github.com/RevoltSecurities/Subdominator.git 243 | cd Subdominator 244 | pip install --upgrade pip 245 | pip install -r requirements.txt 246 | ``` 247 | 248 | After installation, you can verify if Subdominator is installed correctly by running: 249 | 250 | ```bash 251 | subdominator --help 252 | ``` 253 | ### **SubDominator Documentation** 254 | 255 | For complete **post-installation configuration**, **shell interface tutorials**, and **detailed usage instructions**, please refer to the official SubDominator documentation: 256 | 257 | 🔗 **[SubDominator Docs](https://subdominator-docs.streamlit.app/)** 258 | 259 | ### **What You’ll Find in the Documentation:** 260 | ✅ **Post Installation Setup** – Configure dependencies, API keys, and environment variables. 261 | ✅ **Shell Interface Tutorial** – Learn how to use `subdominator -shell` for managing subdomains interactively. 262 | ✅ **Enumeration Techniques** – Understand how to efficiently discover subdomains. 263 | ✅ **Exporting & Reporting** – Generate reports in TXT, JSON, HTML, or PDF formats. 264 | ✅ **Advanced Features & Flags** – Explore additional functionalities to enhance subdomain enumeration. 265 | 266 | For the best experience, ensure you follow the latest updates in the documentation! 🚀 267 | 268 | ### Security: 269 | --- 270 | 271 | Subdominator is a promising tool that will never cause any threats to users or security researcher and its safe to use. Even without 272 | Users permissions subdominator will not update itself and I welcome everyone who are intrested contribute for Subdominator can create 273 | their issues and report it. 274 | 275 | 276 | 277 | ### License: 278 | --- 279 | Subdominator is built by [RevoltSecurities](https://github.com/RevoltSecurities) Team with ❤️ and your support will encourage us to improve the `subdominator` more and Community contributors are 280 | Welcome to contribute for subdominator and If you love the `subdominator` support it by giving a ⭐ . 281 | -------------------------------------------------------------------------------- /img/subdominator-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/img/subdominator-v2.png -------------------------------------------------------------------------------- /img/subdominator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/img/subdominator.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiofiles>=24.1.0 2 | aiohttp>=3.10.11 3 | appdirs>=1.4.4 4 | art>=6.4 5 | beautifulsoup4>=4.13.3 6 | colorama>=0.4.6 7 | fake_useragent>=2.0.3 8 | httpx>=0.28.1 9 | Jinja2>=3.1.6 10 | prompt_toolkit>=3.0.50 11 | PyYAML>=6.0.2 12 | Requests>=2.32.3 13 | rich>=13.9.4 14 | setuptools>=75.6.0 15 | SQLAlchemy>=2.0.32 16 | tldextract>=5.1.2 17 | weasyprint>=65.0 18 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | with open("README.md", "r", encoding="utf-8") as streamr: 4 | long_description = streamr.read() 5 | 6 | setup( 7 | name='subdominator', 8 | version='2.1.1', 9 | author='D. Sanjai Kumar', 10 | author_email='bughunterz0047@gmail.com', 11 | description='Subdominator - An ultimate subdomain enumeration tool for Security Researchers and Bug Bounty Hunters', 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | url="https://github.com/RevoltSecurities/Subdominator", 15 | packages=find_packages(), 16 | install_requires=[ 17 | 'aiofiles>=24.1.0', 18 | 'aiohttp>=3.10.11', 19 | 'appdirs>=1.4.4', 20 | 'art>=6.4', 21 | 'beautifulsoup4>=4.13.3', 22 | 'colorama>=0.4.6', 23 | 'fake_useragent>=2.0.3', 24 | 'httpx>=0.28.1', 25 | 'Jinja2>=3.1.6', 26 | 'prompt_toolkit>=3.0.50', 27 | 'PyYAML>=6.0.2', 28 | 'Requests>=2.32.3', 29 | 'rich>=13.9.4', 30 | 'setuptools>=75.6.0', 31 | 'SQLAlchemy>=2.0.32', 32 | 'tldextract>=5.1.2', 33 | 'weasyprint>=65.0', 34 | 'aiosqlite>=0.21.0', 35 | ], 36 | entry_points={ 37 | 'console_scripts': [ 38 | 'subdominator = subdominator.subdominator:main' 39 | ] 40 | }, 41 | ) 42 | -------------------------------------------------------------------------------- /subdominator/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /subdominator/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/banner/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/banner/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/banner/banner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from art import * 3 | from subdominator.modules.logger.logger import logger, random, random_color, reset,bold,white 4 | 5 | def banner(): 6 | tool_name = "subdominator" 7 | fonts = ["big", "ogre", "shadow", "script", "graffiti", "slant"] 8 | selected_font = random.choice(fonts) 9 | banner = text2art(f"{tool_name}", font=selected_font) 10 | banner = f"""{bold}{random_color}{banner}{reset} 11 | {bold}{white}- RevoltSecurities{reset}\n""" 12 | return banner -------------------------------------------------------------------------------- /subdominator/modules/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/cli/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/cli/cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | from subdominator.modules.logger.logger import logger, bold,blue,reset 4 | from subdominator.modules.utils.utils import Exit 5 | 6 | def cli(): 7 | try: 8 | parser = argparse.ArgumentParser(add_help=False, usage=argparse.SUPPRESS,exit_on_error=False) 9 | parser.add_argument("-d", "--domain", type=str) 10 | parser.add_argument("-dL", "--domain-list", type=str) 11 | parser.add_argument("-cp", "--config-path", type=str) 12 | parser.add_argument("-o", "--output", type=str) 13 | parser.add_argument("-oD", "--output-directory", type=str) 14 | parser.add_argument("-t", "--timeout", type=float, default=30) 15 | parser.add_argument("-up", "--update", action="store_true") 16 | parser.add_argument("-nt", "--notify", action="store_true") 17 | parser.add_argument("-px", "--proxy", type=str) 18 | parser.add_argument("-h", "--help", action="store_true") 19 | parser.add_argument("-v","--version", action="store_true") 20 | parser.add_argument("-ski", "--show-key-info", action="store_true") 21 | parser.add_argument("-sti", "--show-timeout-info", action="store_true") 22 | parser.add_argument("-nc", "--no-color", action="store_true") 23 | parser.add_argument("-ls", "--list-source", action="store_true") 24 | parser.add_argument("-duc", "--disable-update-check", action="store_true") 25 | parser.add_argument("-V", "--verbose", action="store_true") 26 | parser.add_argument("-sup", "--show-updates", action="store_true") 27 | parser.add_argument("-fw", "--filter-wildcards", action="store_true") 28 | parser.add_argument("-json", "--json", action="store_true") 29 | parser.add_argument("-s", "--silent", action="store_true") 30 | parser.add_argument("-ir", "--include-resources", type=str,default=None) 31 | parser.add_argument("-er", "--exclude-resources", type=str, default=None) 32 | parser.add_argument("-all", "--all", action="store_true") 33 | parser.add_argument("-dork", "--dork", type=str,default=None) 34 | parser.add_argument("-cdp", "--config-db-path", type=str) 35 | parser.add_argument("-shell", "--shell", action="store_true") 36 | return parser.parse_args() 37 | except argparse.ArgumentError as e: 38 | logger(f"Please use the command for more infromation: {bold}{blue}subdominator -h{reset}", "warn") 39 | Exit(1) 40 | except argparse.ArgumentTypeError as e: 41 | logger(f"Please use the command for more infromation: {bold}{blue}subdominator -h{reset}" ,"warn") 42 | Exit(1) 43 | except Exception as e: 44 | logger(f"Unahandled Exception occured in the CLI module due to: {e}", "warn") 45 | Exit(1) -------------------------------------------------------------------------------- /subdominator/modules/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/config/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/config/config.py: -------------------------------------------------------------------------------- 1 | import appdirs 2 | import os 3 | import sqlite3 4 | from subdominator.modules.utils.utils import * 5 | from subdominator.modules.logger.logger import logger 6 | 7 | yamls = """arpsyndicate:[] 8 | 9 | bevigil: [] 10 | 11 | binaryedge: [] 12 | 13 | bufferover: [] 14 | 15 | builtwith: [] 16 | 17 | c99: [] 18 | 19 | censys: [] 20 | 21 | certspotter: [] 22 | 23 | chaos: [] 24 | 25 | digitalyama: [] 26 | 27 | dnsdumpster: [] 28 | 29 | dnsrepo: [] 30 | 31 | facebook: [] 32 | 33 | fofa: [] 34 | 35 | fullhunt: [] 36 | 37 | google: [] 38 | 39 | huntermap: [] 40 | 41 | intelx: [] 42 | 43 | leakix: [] 44 | 45 | merklemap: [] 46 | 47 | netlas: [] 48 | 49 | odin: [] 50 | 51 | quake: [] 52 | 53 | rapidapi: [] 54 | 55 | redhuntlabs: [] 56 | 57 | rsecloud: [] 58 | 59 | virustotal: [] 60 | 61 | securitytrails: [] 62 | 63 | shodan: [] 64 | 65 | trickest: [] 66 | 67 | whoisxmlapi: [] 68 | 69 | zoomeyeapi: [] 70 | 71 | # for notifications 72 | slack: [] 73 | 74 | pushbullet: [] 75 | 76 | """ 77 | 78 | def Username(): 79 | try: 80 | username = os.getlogin() 81 | except OSError: 82 | username = os.getenv('USER') or os.getenv('LOGNAME') or os.getenv('USERNAME') or 'User' 83 | except Exception as e: 84 | username = "User" 85 | return username 86 | 87 | def config(): 88 | try: 89 | get_config = appdirs.user_config_dir() 90 | subdominator_dir = os.path.join(get_config, "Subdominator") 91 | filename = "provider-config.yaml" 92 | config_path = os.path.join(subdominator_dir, filename) 93 | os.makedirs(subdominator_dir, exist_ok=True) 94 | 95 | if not os.path.exists(config_path): 96 | with open(config_path, "w") as w: 97 | w.write(yamls) 98 | return config_path 99 | except Exception as e: 100 | logger(f"Exception occurred at config module: {e}", "warn") 101 | 102 | def custompath(path:str, args): 103 | try: 104 | if os.path.exists(path) and os.path.isfile(path): 105 | return path 106 | else: 107 | logger(f"Please check if the config path exists", "warn", args.no_color) 108 | except KeyboardInterrupt as e: 109 | quit() 110 | 111 | def cachedir(): 112 | try: 113 | cachedir = appdirs.user_cache_dir() 114 | return cachedir 115 | except Exception as e: 116 | pass 117 | 118 | def db_config(): 119 | try: 120 | get_config = cachedir() 121 | subdominator_db_dir = os.path.join(get_config, "SubdominatorDB") 122 | db_filename = "subdominator.db" 123 | db_path = os.path.join(subdominator_db_dir, db_filename) 124 | if not os.path.exists(subdominator_db_dir): 125 | os.makedirs(subdominator_db_dir) 126 | conn = sqlite3.connect(db_path) 127 | conn.execute(""" 128 | CREATE TABLE IF NOT EXISTS subdomains ( 129 | domain TEXT PRIMARY KEY, 130 | subdomains TEXT 131 | ) 132 | """) 133 | conn.commit() 134 | conn.close() 135 | return db_path 136 | except Exception as e: 137 | logger(f"Exception occurred in db_config: {e}", "warn") 138 | return None 139 | 140 | def html_config(): 141 | """Gets the file path of the report HTML template and ensures it exists.""" 142 | try: 143 | get_config = appdirs.user_config_dir() 144 | subdominator_dir = os.path.join(get_config, "Subdominator") 145 | filename = "report_template.html" 146 | html_path = os.path.join(subdominator_dir, filename) 147 | 148 | os.makedirs(subdominator_dir, exist_ok=True) 149 | 150 | default_html = """ 151 | 152 | 153 | 154 | 155 | Subdomain Report 156 | 163 | 164 | 165 |

Subdomain Report for {domain}

166 | 167 | 168 | 169 | 170 | {subdomain_rows} 171 |
Subdomain
172 | 173 | """ 174 | 175 | if not os.path.exists(html_path): 176 | with open(html_path, "w") as f: 177 | f.write(default_html) 178 | return html_path 179 | except Exception as e: 180 | logger(f"Exception occurred in html_config: {e}", "warn") 181 | return None 182 | -------------------------------------------------------------------------------- /subdominator/modules/crud/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/crud/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/crud/crud.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.future import select 2 | from sqlalchemy.ext.asyncio import AsyncSession 3 | from subdominator.modules.models.models import Subdomain 4 | 5 | async def get_domain(db: AsyncSession, domain: str): 6 | result = await db.execute(select(Subdomain).filter(Subdomain.domain == domain)) 7 | return result.scalars().first() 8 | 9 | async def add_or_update_domain(db: AsyncSession, domain: str, subdomains: set): 10 | existing_entry = await get_domain(db, domain) 11 | if existing_entry: 12 | existing_subdomains = set(existing_entry.subdomains.split(",")) if existing_entry.subdomains else set() 13 | new_subdomains = existing_subdomains.union(subdomains) 14 | existing_entry.subdomains = ",".join(sorted(new_subdomains)) 15 | else: 16 | new_entry = Subdomain(domain=domain, subdomains=",".join(sorted(subdomains))) 17 | db.add(new_entry) 18 | await db.commit() 19 | 20 | async def get_all_domains(db: AsyncSession): 21 | result = await db.execute(select(Subdomain)) 22 | return result.scalars().all() 23 | 24 | async def get_subdomains(db: AsyncSession, domain: str): 25 | domain_entry = await get_domain(db, domain) 26 | return set(domain_entry.subdomains.split(",")) if domain_entry and domain_entry.subdomains else set() 27 | 28 | async def delete_domain(db: AsyncSession, domain: str): 29 | domain_entry = await get_domain(db, domain) 30 | if domain_entry: 31 | await db.delete(domain_entry) 32 | await db.commit() 33 | return True 34 | return False 35 | 36 | -------------------------------------------------------------------------------- /subdominator/modules/handler.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | import asyncio 5 | from rich.console import Console 6 | from rich.markdown import Markdown 7 | import httpx 8 | from colorama import Fore,Style 9 | import random 10 | import json 11 | 12 | red = Fore.RED 13 | green = Fore.GREEN 14 | magenta = Fore.MAGENTA 15 | cyan = Fore.CYAN 16 | mixed = Fore.RED + Fore.BLUE 17 | blue = Fore.BLUE 18 | yellow = Fore.YELLOW 19 | white = Fore.WHITE 20 | reset = Style.RESET_ALL 21 | bold = Style.BRIGHT 22 | colors = [ green, cyan, blue] 23 | random_color = random.choice(colors) 24 | 25 | try: 26 | from .banner.banner import banner 27 | from .cli.cli import cli 28 | from .config.config import config, custompath, Username, cachedir,db_config 29 | from .help.help import help 30 | from .source.source import sources 31 | from .update.update import * 32 | from .version.version import version 33 | from .logger.logger import logger 34 | from .utils.utils import filters,reader,split_to_list, check_directory_permission,check_file_permission, Exit 35 | from .subscraper.abuseipdb.abuseipdb import abuseipdb 36 | from .subscraper.alienvault.alientvault import alienvault 37 | from .subscraper.anubis.anubis import anubis 38 | from .subscraper.arpsyndicate.arpsyndicate import arpsyndicate 39 | from .subscraper.bevigil.bevigil import bevigil 40 | from .subscraper.binaryedge.binaryedge import binaryedge 41 | from .subscraper.bufferover.bufferover import bufferover 42 | from .subscraper.builtwith.builtwith import builtwith 43 | from .subscraper.c99.c99 import c99 44 | from .subscraper.censys.censys import censys 45 | from .subscraper.certspotter.certspotter import certspotter 46 | from .subscraper.chaos.chaos import chaos 47 | from .subscraper.coderog.coderog import coderog 48 | from .subscraper.commoncrawl.commoncrawl import commoncrawl 49 | from .subscraper.crtsh.crtsh import crtsh 50 | from .subscraper.cyfare.cyfare import cyfare 51 | from .subscraper.digitorus.digitorus import digitorus 52 | from .subscraper.dnsdumpster.dnsdumpster import dnsdumpster 53 | from .subscraper.dnsrepo.dnsrepo import dnsrepo 54 | from .subscraper.facebook.facebook import facebook 55 | from .subscraper.fullhunt.fullhunt import fullhunt 56 | from .subscraper.fofa.fofa import fofa 57 | from .subscraper.google.google import google 58 | from .subscraper.hackertarget.hackertarget import hackertarget 59 | from .subscraper.huntermap.huntermap import huntermap 60 | from .subscraper.intelx.intelx import intelx 61 | from .subscraper.leakix.leakix import leakix 62 | from .subscraper.merklemap.merklemap import merklemap 63 | from .subscraper.myssl.myssl import myssl 64 | from .subscraper.netlas.netlas import netlas 65 | from .subscraper.quake.quake import quake 66 | from .subscraper.racent.racent import racent 67 | from .subscraper.rapidapi.rapidapi import rapidapi 68 | from .subscraper.rapidfinder.rapidfinder import rapidfinder 69 | from .subscraper.rapidscan.rapidscan import rapidscan 70 | from .subscraper.rapiddns.rapiddns import rapiddns 71 | from .subscraper.redhuntlabs.redhuntlabs import redhuntlabs 72 | from .subscraper.rsecloud.rsecloud import rsecloud 73 | from .subscraper.securitytrails.securitytrails import securitytrails 74 | from .subscraper.shodan.shodan import shodan 75 | from .subscraper.shodanx.shodanx import shodanx 76 | from .subscraper.shrewdeye.shrewdeye import shrewdeye 77 | from .subscraper.sitedossier.sitedossier import sitedossier 78 | from .subscraper.trickest.trickest import trickest 79 | from .subscraper.urlscan.urlscan import urlscan 80 | from .subscraper.virustotal.virustotal import virustotal 81 | from .subscraper.waybackarchive.waybackarchive import waybackarchive 82 | from .subscraper.whoisxml.whoisxml import whoisxml 83 | from .subscraper.zoomeyeapi.zoomeyeapi import zoomeyeapi 84 | from .subscraper.digitalyama.digitalyama import digitalyama 85 | from .subscraper.odin.odin import odin 86 | from .subscraper.hudsonrock.hudsonrock import hudsonrock 87 | from .subscraper.threatcrowd.threatcrowd import threatcrowd 88 | from .save.save import file, dir, jsonsave 89 | from .notify.notify import notify 90 | except ImportError as e: 91 | print(f"[{bold}{red}INFO{reset}]: {bold}{white}Import Error occured in Subdominator Module imports due to: {e}{reset}", file=sys.stderr) 92 | print(f"[{bold}{blue}INFO{reset}]: {bold}{white}If you are encountering this issue more than a time please report the issues in Subdominator Github page.. {reset}", file=sys.stderr) 93 | sys.exit(1) 94 | 95 | args = cli() 96 | if not args.config_path: 97 | configpath = config() 98 | else: 99 | 100 | configpath = custompath(args.config_path,args) 101 | if not configpath: 102 | logger(f"Unable to load the custom provider config file, please check the file exist", "warn", args.no_color) 103 | Exit(1) 104 | 105 | 106 | if not args.config_db_path: 107 | dbpath = db_config() 108 | else: 109 | dbpath = custompath(args.config_db_path,args) 110 | if not dbpath: 111 | logger(f"Unable to load the custom subdominator DB , please check the DB exist", "warn", args.no_color) 112 | Exit(1) 113 | 114 | from .models.models import AsyncSessionLocal 115 | from .crud.crud import get_all_domains,get_domain,get_subdomains,add_or_update_domain 116 | from .shell.shell import SubDominatorShell 117 | 118 | banners = banner() 119 | username = Username() 120 | 121 | async def __initiate__(domain): 122 | try: 123 | async with httpx.AsyncClient(proxy=args.proxy, verify=False) as session: 124 | tasks = [abuseipdb(domain, session, args), 125 | alienvault(domain, session, args), 126 | anubis(domain, session, args), 127 | arpsyndicate(domain, session, configpath,args), 128 | bevigil(domain, session, configpath, username, args), 129 | binaryedge(domain, session, configpath, username, args), 130 | bufferover(domain, session, configpath, username, args), 131 | builtwith(domain, session, configpath, username, args), 132 | c99(domain, session, configpath, username, args), 133 | censys(domain, session, configpath, username, args), 134 | certspotter(domain, session, configpath, args), 135 | chaos(domain, session, configpath, args), 136 | coderog(domain, session, configpath, args), 137 | commoncrawl(domain, args), 138 | crtsh(domain, session, args), 139 | cyfare(domain, session, args), 140 | digitorus(domain, session, args), 141 | fofa(domain,session, configpath, username, args), 142 | dnsdumpster(domain, session, configpath,args), 143 | dnsrepo(domain,session, configpath, username,args), 144 | facebook(domain, session, configpath, username, args), 145 | fullhunt(domain, session, configpath, username, args), 146 | google(domain, session, configpath, username, args), 147 | hackertarget(domain, session, args), 148 | huntermap(domain, session, configpath, username, args), 149 | intelx(domain, session, configpath, username, args), 150 | leakix(domain, session, configpath, username, args), 151 | merklemap(domain, session,configpath,username,args), 152 | myssl(domain, session, args), 153 | netlas(domain, session, configpath, username, args), 154 | quake(domain, session, configpath, username, args), 155 | rapidapi(domain, session, configpath, args), 156 | rapiddns(domain, session, args), 157 | rapidfinder(domain, session, configpath, username, args), 158 | rapidscan(domain, session, configpath, username, args), 159 | redhuntlabs(domain, session, configpath, username, args) , 160 | racent(domain, session, args) , 161 | rsecloud(domain, session, configpath, username, args) , 162 | securitytrails(domain, session, configpath, username, args), 163 | shodan(domain, session, configpath, username, args), 164 | shodanx(domain, session, args), 165 | shrewdeye(domain, session, args), 166 | sitedossier(domain, session, args), 167 | trickest(domain, configpath, args), 168 | urlscan(domain, session, args), 169 | virustotal(domain, session, configpath, username, args), 170 | waybackarchive(domain, args), 171 | whoisxml(domain, session, configpath, username, args), 172 | zoomeyeapi(domain, session, configpath, username, args), 173 | digitalyama(domain, session, configpath, username, args), 174 | odin(domain, session,configpath,username,args), 175 | hudsonrock(domain, session, args), 176 | threatcrowd(domain, session, args) 177 | ] 178 | results = await asyncio.gather(*tasks) 179 | return results 180 | except Exception as e: 181 | if args.verbose: 182 | logger(f"Exception handler sources: {e}, {type(e)}", "warn", args.no_color) 183 | 184 | def gitversion(): 185 | try: 186 | latest = version() 187 | current = "v2.1.1" 188 | if latest == current: 189 | print(f"[{blue}{bold}version{reset}]:{bold}{white}subdominator current version {current} ({green}latest{reset}{bold}{white}){reset}", file=sys.stderr) 190 | else: 191 | print(f"[{blue}{bold}version{reset}]: {bold}{white}subdominator current version {current} ({red}outdated{reset}{bold}{white}){reset}", file=sys.stderr) 192 | except KeyboardInterrupt as e: 193 | Exit(1) 194 | 195 | 196 | def update_handler(): 197 | try: 198 | if args.show_updates: 199 | updatelog() 200 | Exit() 201 | 202 | current = "v2.1.1" 203 | pypiold = "2.1.1" 204 | git = version() 205 | 206 | if current == git: 207 | logger(f"Hey {username} subdominator is already in new version", "info", args.no_color) 208 | Exit() 209 | 210 | zipurl = getzip() 211 | if not zipurl: 212 | logger(f"Hey {username} failed to update subdominator please try manually from github", "warn" , args.no_color) 213 | Exit() 214 | cache = cachedir() 215 | if cache: 216 | launch(zipurl, cache) 217 | else: 218 | launch(zipurl, os.getcwd()) 219 | pypiversion = getverify("subdominator") 220 | 221 | if pypiold == pypiversion: 222 | logger(f"Hey {username} failed to update subdominator please try manually from github", "warn" , args.no_color) 223 | Exit() 224 | 225 | logger(f"Verified the latest version of subdominator from pypi and updated from {current} --> {git}", "info" , args.no_color) 226 | updatelog() 227 | Exit() 228 | 229 | except KeyboardInterrupt as e: 230 | Exit() 231 | 232 | def show_sources(): 233 | try: 234 | resources = sources() 235 | logger(f"Current Available passive resources: [{len(resources)}]", "info" , args.no_color) 236 | 237 | logger(f"Sources marked with an * needs API key(s) or token(s) configuration to works", "info" , args.no_color) 238 | 239 | logger(f"Hey {username} you can config your api keys or token here {configpath} to work", "info" , args.no_color) 240 | console = Console() 241 | for sourced in resources: 242 | console.print(Markdown(sourced)) 243 | Exit() 244 | except KeyboardInterrupt as e: 245 | Exit(1) 246 | 247 | async def _domain_handler_(domain): 248 | try: 249 | start = time.time() 250 | results = await __initiate__(domain) 251 | filtered = filters(results) 252 | final= set() 253 | 254 | for subdomain in filtered: 255 | if subdomain.endswith(f".{domain}") and subdomain not in final: 256 | if args.filter_wildcards: 257 | if subdomain.startswith("*."): 258 | subdomain = subdomain[2:] 259 | final.add(subdomain) 260 | 261 | for subdomain in final: 262 | if args.json: 263 | output = {"domain":f"{domain}", "subdomain": f"{subdomain}"} 264 | output = json.dumps(output, indent=2) 265 | else: 266 | output = subdomain 267 | 268 | print(output) 269 | if args.output: 270 | file(output, domain, args) 271 | elif args.output_directory: 272 | dir(output, domain, args) 273 | 274 | async with AsyncSessionLocal() as db: 275 | await add_or_update_domain(db, domain, final) 276 | 277 | if args.notify: 278 | await notify(domain, final, configpath, username, args) 279 | 280 | end = time.time() 281 | total_time = end - start 282 | if not args.silent: 283 | logger(f"Total {len(final)} subdomains found for {domain} in {total_time:.2f} seconds{reset}", "info" , args.no_color) 284 | logger(f"Happy Hacking {username} ☠️ 🔥 🚀{reset}", "info" , args.no_color) 285 | except KeyboardInterrupt as e: 286 | Exit(1) 287 | 288 | async def handler(): 289 | try: 290 | if args.help: 291 | print(banners, file=sys.stderr) 292 | help(configpath, dbpath) 293 | Exit() 294 | 295 | if args.shell: 296 | print(banners, file=sys.stderr) 297 | Shell = SubDominatorShell() 298 | await Shell.cmdloop() 299 | Exit(0) 300 | 301 | if not args.silent: 302 | print(banners, file=sys.stderr) 303 | if not args.disable_update_check: 304 | gitversion() 305 | if args.list_source: 306 | show_sources() 307 | if args.update or args.show_updates: 308 | update_handler() 309 | 310 | if args.include_resources: 311 | args.include_resources = split_to_list(args.include_resources) 312 | 313 | if args.exclude_resources: 314 | args.exclude_resources = split_to_list(args.exclude_resources) 315 | 316 | if args.output: 317 | perm = await check_file_permission(args.output, args) 318 | if not perm: 319 | Exit(1) 320 | 321 | if args.output_directory: 322 | perm = await check_directory_permission(args.output_directory, args) 323 | if not perm: 324 | Exit(1) 325 | 326 | if args.domain: 327 | if not args.silent: 328 | logger(f"Loading provider configuration file from {configpath}", "info" , args.no_color) 329 | 330 | logger(f"Enumerating subdomain for {args.domain}", "info" , args.no_color) 331 | await _domain_handler_(args.domain) 332 | Exit() 333 | 334 | if args.domain_list: 335 | if not args.silent: 336 | logger(f"Loading provider configuration file from {configpath}", "info" , args.no_color) 337 | domains = await reader(args.domain_list, args) 338 | if domains: 339 | for domain in domains: 340 | if not args.silent: 341 | logger(f"Enumerating subdomain for {domain}", "info" , args.no_color) 342 | 343 | await _domain_handler_(domain) 344 | Exit() 345 | 346 | if sys.stdin.isatty(): 347 | logger(f"subdominator exits due to no inputs provided, please use subdominator -h for more details", "warn", args.no_color) 348 | Exit() 349 | else: 350 | if not args.silent: 351 | logger(f"Loading provider configuration file from {configpath}", "info" , args.no_color) 352 | for domain in sys.stdin: 353 | if domain: 354 | domain = domain.strip() 355 | if not args.silent: 356 | logger(f"Enumerating subdomain for {domain}", "info" , args.no_color) 357 | await _domain_handler_(domain) 358 | Exit() 359 | except KeyboardInterrupt as e: 360 | Exit() 361 | 362 | 363 | def main_handler(): 364 | try: 365 | asyncio.run(handler()) 366 | except KeyboardInterrupt as e: 367 | sys.exit(1) 368 | -------------------------------------------------------------------------------- /subdominator/modules/help/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/help/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/help/help.py: -------------------------------------------------------------------------------- 1 | from subdominator.modules.utils.utils import bold, white, blue, reset, Exit 2 | 3 | def help(path, dbpath): 4 | print(f""" 5 | {bold}{white}[{reset}{bold}{blue}DESCRIPTION{reset}{bold}{white}]{reset}: 6 | 7 | {bold}{white}Subdominator is a passive subdomain enumeration tool that discovers subdomains for your targets using passive and open-source resources.{reset} 8 | 9 | {bold}{white}[{reset}{bold}{blue}USAGE{reset}{bold}{white}]{reset}: 10 | 11 | {bold}{white}subdominator [flags]{reset} 12 | 13 | {bold}{white}[{reset}{bold}{blue}FLAGS{reset}{bold}{white}]{reset}: 14 | 15 | {bold}{white}[{reset}{bold}{blue}INPUT{reset}{bold}{white}]{reset}: 16 | 17 | {bold}{white}-d, --domain : Target domain name for subdomain enumeration. 18 | -dL, --domain-list : File containing multiple domains for bulk enumeration. 19 | stdin/stdout : Supports input/output redirection. 20 | 21 | {bold}{white}[{reset}{bold}{blue}OUTPUT{reset}{bold}{white}]{reset}: 22 | 23 | {bold}{white}-o, --output : Save results to a file. 24 | -oD, --output-directory : Directory to save results (useful when -dL is specified). 25 | -json, --json : Output results in JSON format.{reset} 26 | 27 | {bold}{white}[{reset}{bold}{blue}MODE{reset}{bold}{white}]{reset}: 28 | 29 | {bold}{white}-shell, --shell : Enable interactive shell mode to work with subdominator Database,generate report and etc.{reset} 30 | 31 | {bold}{white}[{reset}{bold}{blue}OPTIMIZATION{reset}{bold}{white}]{reset}: 32 | 33 | {bold}{white}-t, --timeout : Set timeout value for API requests (default: 30s). 34 | -fw, --filter-wildcards : Filter out wildcard subdomains.{reset} 35 | 36 | {bold}{white}[{reset}{bold}{blue}CONFIGURATION{reset}{bold}{white}]{reset}: 37 | 38 | {bold}{white}-cp, --config-path : Custom config file path for API keys (default: {path}). 39 | -cdp, --config-db-path : Custom database config path (default: {dbpath}). 40 | -nt, --notify : Send notifications for found subdomains via Slack, Pushbullet. 41 | -px, --proxy : Use an HTTP proxy for debugging requests. 42 | -dork, --dork : Use a custom google dork for google resource (ex: -ir google --dork 'site:target.com -www -dev intext:secrets'){reset} 43 | 44 | {bold}{white}[{reset}{bold}{blue}RESOURCE CONFIGURATION{reset}{bold}{white}]{reset}: 45 | 46 | {bold}{white}-ir, --include-resources : Specify sources to include (comma-separated). 47 | -er, --exclude-resources : Specify sources to exclude (comma-separated). 48 | -all, --all : Use all available sources for enumeration.{reset} 49 | 50 | {bold}{white}[{reset}{bold}{blue}UPDATE{reset}{bold}{white}]{reset}: 51 | 52 | {bold}{white}-up, --update : Update Subdominator to the latest version (manual YAML update required). 53 | -duc, --disable-update-check : Disable automatic update checks. 54 | -sup, --show-updates : Show the latest update details.{reset} 55 | 56 | {bold}{white}[{reset}{bold}{blue}DEBUGGING{reset}{bold}{white}]{reset}: 57 | 58 | {bold}{white}-h, --help : Show this help message and exit. 59 | -v, --version : Show the current version and check for updates. 60 | -s, --silent : Show only subdomains in output. 61 | -ski, --show-key-info : Show API key errors (e.g., out of credits). 62 | -sti, --show-timeout-info : Show timeout errors for sources. 63 | -nc, --no-color : Disable colorized output. 64 | -ls, --list-source : List available subdomain enumeration sources. 65 | -V, --verbose : Enable verbose output.{reset} 66 | """) 67 | Exit() -------------------------------------------------------------------------------- /subdominator/modules/logger/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/logger/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/logger/logger.py: -------------------------------------------------------------------------------- 1 | from colorama import Fore,Style,init 2 | import sys 3 | import random 4 | init() 5 | 6 | red = Fore.RED 7 | green = Fore.GREEN 8 | magenta = Fore.MAGENTA 9 | cyan = Fore.CYAN 10 | mixed = Fore.RED + Fore.BLUE 11 | blue = Fore.BLUE 12 | yellow = Fore.YELLOW 13 | white = Fore.WHITE 14 | reset = Style.RESET_ALL 15 | bold = Style.BRIGHT 16 | colors = [ green, cyan, blue] 17 | random_color = random.choice(colors) 18 | 19 | 20 | def logger(message: str, level="info", nocolored=False): 21 | if level == "info": 22 | if nocolored: 23 | leveler = "INFO" 24 | else: 25 | leveler = f"{bold}{blue}INFO{reset}" 26 | 27 | elif level == "warn": 28 | if nocolored: 29 | leveler = "WRN" 30 | else: 31 | leveler = f"{bold}{red}WRN{reset}" 32 | 33 | elif level == "error": 34 | if nocolored: 35 | leveler = "ERR" 36 | else: 37 | leveler = f"{bold}{red}ERR{reset}" 38 | 39 | elif level == "verbose": 40 | if nocolored: 41 | leveler = "VRB" 42 | else: 43 | leveler = f"{bold}{green}VRB{reset}" 44 | 45 | elif level == "debug": 46 | if nocolored: 47 | leveler = "DBG" 48 | else: 49 | leveler = f"{bold}{cyan}DBG{reset}" 50 | 51 | else: 52 | if nocolored: 53 | leveler = f"{level.upper()}" 54 | else: 55 | leveler = f"{bold}{green}{level.upper()}{reset}" 56 | 57 | if nocolored: 58 | print(f"[{leveler}]: {message}", file=sys.stderr) 59 | else: 60 | print(f"[{bold}{blue}{leveler}{reset}]: {bold}{white}{message}{reset}", file=sys.stderr) 61 | 62 | def bannerlog(banner: str): 63 | print(banner, file=sys.stderr) 64 | 65 | def stdinlog(message): 66 | print(message) -------------------------------------------------------------------------------- /subdominator/modules/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/models/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/models/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import sessionmaker 4 | from sqlalchemy import Column, String 5 | from subdominator.modules.handler import dbpath 6 | 7 | Base = declarative_base() 8 | 9 | class Subdomain(Base): 10 | __tablename__ = "subdomains" 11 | domain = Column(String, primary_key=True) 12 | subdomains = Column(String, default="") 13 | 14 | DBURL = f"sqlite+aiosqlite:///{dbpath}" 15 | 16 | async_engine = create_async_engine(DBURL, echo=False) 17 | AsyncSessionLocal = sessionmaker( 18 | bind=async_engine, 19 | class_=AsyncSession, 20 | expire_on_commit=False 21 | ) -------------------------------------------------------------------------------- /subdominator/modules/notify/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/notify/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/notify/notify.py: -------------------------------------------------------------------------------- 1 | import aiohttp 2 | from subdominator.modules.logger.logger import logger, bold,red,white,reset 3 | from subdominator.modules.utils.utils import singlekeyloader 4 | 5 | async def slack(domain, subdomains, url, session, args): 6 | try: 7 | results = '\n'.join(subdomains) 8 | payloads = {"text": f"Total subdomains {len(subdomains)} found for {domain}:\n{results}"} 9 | async with session.post(url, json=payloads) as response: 10 | pass 11 | except aiohttp.ClientConnectionError as e: 12 | pass 13 | except TimeoutError as e: 14 | pass 15 | except Exception as e: 16 | if args.sec_deb: 17 | logger(f"Exception in slack notify pusher module due to : {e}, {type(e)}","warn",args.no_color) 18 | 19 | 20 | async def pushbullet(domain, subdomains, randomkey, session, args): 21 | try: 22 | url = f'https://api.pushbullet.com/v2/pushes' 23 | results = '\n'.join(subdomains) 24 | headers = { 25 | 'Access-Token': randomkey, 26 | 27 | 'Content-Type': 'application/json' 28 | } 29 | jdata = { 30 | 'type': 'note', 31 | 'title': f"Total subdomains {len(subdomains)} found for {domain}:", 32 | 'body': f"{results}" 33 | } 34 | async with session.post(url, headers=headers, json=jdata) as response: 35 | pass 36 | 37 | except aiohttp.ClientConnectionError as e: 38 | pass 39 | except TimeoutError as e: 40 | pass 41 | except Exception as e: 42 | if args.sec_deb: 43 | logger(f"Exception in pushbullet module due to : {e}, {type(e)}","warn",args.no_color) 44 | 45 | async def notify(domain, subdomains, configpath, username, args): 46 | try: 47 | url = await singlekeyloader(configpath,"slack") 48 | randomkey = await singlekeyloader(configpath,"pushbullet") 49 | async with aiohttp.ClientSession() as session: 50 | if url: 51 | await slack(domain, subdomains, url,session, args) 52 | if randomkey: 53 | await pushbullet(domain, subdomains, randomkey, session, args) 54 | except Exception as e: 55 | if args.sec_deb: 56 | logger(f"[{bold}{red}WRN{reset}]: {bold}{white}exception in notify: {e}, {type(e)}","warn",args.no_color) -------------------------------------------------------------------------------- /subdominator/modules/save/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/save/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/save/save.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import aiofiles 4 | from subdominator.modules.logger.logger import logger 5 | 6 | def file(subdomain, domain, args): 7 | try: 8 | if args.output: 9 | if os.path.isdir(args.output): 10 | filename = os.path.join(args.output, f"{domain}.subdomains.txt") 11 | else: 12 | filename = args.output 13 | 14 | with open(filename, "a") as w: 15 | w.write(subdomain + '\n') 16 | except KeyboardInterrupt as e: 17 | SystemExit 18 | 19 | except Exception as e: 20 | pass 21 | 22 | 23 | def dir(subdomain, domain, args): 24 | try: 25 | if args.json: 26 | ext = "json" 27 | else: 28 | ext = "txt" 29 | 30 | if os.path.isdir(args.output_directory): 31 | if os.path.exists(args.output_directory): 32 | filename = f"{args.output_directory}/{domain}.{ext}" 33 | else: 34 | os.makedirs(args.output_directory) 35 | filename = f"{args.output_directory}/{domain}.{ext}" 36 | else: 37 | currentdir = os.getcwd() 38 | filename = f"{currentdir}/{domain}.{ext}" 39 | 40 | with open(filename, "a") as w: 41 | w.write(subdomain + '\n') 42 | except KeyboardInterrupt as e: 43 | SystemExit 44 | except Exception as e: 45 | pass 46 | 47 | async def jsonsave(domain, subdomains, filename, args): 48 | try: 49 | if args.output_json: 50 | if os.path.isdir(args.output_json): 51 | filename = os.path.join(args.output_json, f"{domain}.subdomains.json") 52 | else: 53 | filename = args.output_json 54 | results = [] 55 | for subdomain in subdomains: 56 | results.append({ 57 | "subdomain": subdomain, 58 | "domain": domain 59 | }) 60 | async with aiofiles.open(filename, "a") as streamw: 61 | for output in results: 62 | await streamw.write(json.dumps(output)+"\n") 63 | except Exception as e: 64 | print(f"Excepiton occured in json output writer due to: {e}", "warn", args.no_color) -------------------------------------------------------------------------------- /subdominator/modules/shell/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/shell/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/shell/shell.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | from prompt_toolkit import PromptSession 4 | from prompt_toolkit.patch_stdout import patch_stdout 5 | from subdominator.modules.models.models import AsyncSessionLocal 6 | from subdominator.modules.crud.crud import get_all_domains, get_subdomains, get_domain, add_or_update_domain, delete_domain 7 | from subdominator.modules.config.config import html_config 8 | from subdominator.modules.utils.utils import Exit 9 | from rich.console import Console 10 | from rich.table import Table 11 | from jinja2 import Environment, FileSystemLoader 12 | from weasyprint import HTML 13 | 14 | console = Console() 15 | 16 | class SubDominatorShell: 17 | def __init__(self): 18 | self.prompt = f"[subdominator]$ " 19 | self.session = PromptSession() 20 | self.db_connected = False 21 | 22 | async def cmdloop(self): 23 | while True: 24 | try: 25 | with patch_stdout(): 26 | user_input = await self.session.prompt_async(self.prompt) 27 | 28 | if not user_input.strip(): 29 | continue 30 | 31 | if user_input.startswith("help"): 32 | await self.do_help() 33 | continue 34 | 35 | if user_input.lower() == "exit": 36 | Exit(0) 37 | 38 | if user_input.lower() == "connect db": 39 | self.db_connected = True 40 | console.print("[bold green]Connected to SubDominator Database.[/bold green]") 41 | continue 42 | 43 | if not self.db_connected: 44 | console.print("[bold red]Error: Database not connected. Use 'connect db' first.[/bold red]") 45 | continue 46 | 47 | if user_input.startswith("show domains"): 48 | await self.async_show_domains() 49 | elif user_input.startswith("show subdomain"): 50 | domain = user_input[len("show subdomain"):].strip() 51 | await self.async_show_subdomain(domain) 52 | elif user_input.startswith("add domain"): 53 | args = user_input[len("add domain"):].strip().split() 54 | if len(args) == 2: 55 | await self.async_add_domain(args[0], args[1]) 56 | else: 57 | console.print("[bold yellow]Usage: add domain [/bold yellow]") 58 | elif user_input.startswith("update"): 59 | args = user_input[len("update"):].strip().split() 60 | if len(args) == 2: 61 | await self.async_update_domain(args[0], args[1]) 62 | else: 63 | console.print("[bold yellow]Usage: update [/bold yellow]") 64 | elif user_input.startswith("delete"): 65 | domain = user_input[len("delete"):].strip() 66 | if domain: 67 | await self.async_delete_domain(domain) 68 | else: 69 | console.print("[bold yellow]Usage: delete [/bold yellow]") 70 | elif user_input.startswith("export"): 71 | args = user_input[len("export"):].strip().split() 72 | if len(args) == 3: 73 | await self.async_export_data(args[0], args[1], args[2]) 74 | else: 75 | console.print("[bold yellow]Usage: export [/bold yellow]") 76 | elif user_input.startswith("report"): 77 | args = user_input[len("report"):].strip().split() 78 | if len(args) == 3: 79 | await self.generate_report(args[0], args[1], args[2]) 80 | else: 81 | console.print("[bold yellow]Usage: report [/bold yellow]") 82 | else: 83 | os.system(user_input.lower()) 84 | except KeyboardInterrupt: 85 | console.print("\n[bold red]Exiting SubDominator Shell...[/bold red]") 86 | break 87 | except Exception as e: 88 | console.print(f"[bold red]Error: {e}[/bold red]") 89 | 90 | async def async_show_domains(self): 91 | async with AsyncSessionLocal() as session: 92 | domains = await get_all_domains(session) 93 | if not domains: 94 | console.print("[bold yellow]No records exist.[/bold yellow]") 95 | return 96 | 97 | table = Table(title="[bold cyan]DB Stored Domains[/bold cyan]") 98 | table.add_column("[bold]Domain[/bold]", style="cyan", justify="left") 99 | 100 | for domain in domains: 101 | table.add_row(domain.domain) 102 | console.print(table) 103 | 104 | async def async_show_subdomain(self, domain): 105 | async with AsyncSessionLocal() as session: 106 | subdomains = await get_subdomains(session, domain) 107 | if not subdomains: 108 | console.print(f"[bold yellow]No records exist for {domain}.[/bold yellow]") 109 | return 110 | 111 | table = Table(title=f"[bold cyan]Subdomains of {domain}[/bold cyan]") 112 | table.add_column("[bold]Subdomain[/bold]", style="cyan", justify="left") 113 | 114 | for subdomain in sorted(subdomains): 115 | table.add_row(subdomain) 116 | console.print(table) 117 | 118 | async def async_add_domain(self, domain, subdomains_file): 119 | subdomains = self.load_subdomains_from_file(subdomains_file) 120 | if not subdomains: 121 | console.print("[bold red]Error: No valid subdomains found in file.[/bold red]") 122 | return 123 | 124 | async with AsyncSessionLocal() as session: 125 | await add_or_update_domain(session, domain, subdomains) 126 | console.print(f"[bold green]Domain {domain} added successfully![/bold green]") 127 | 128 | async def async_update_domain(self, domain, subdomains_file): 129 | subdomains = self.load_subdomains_from_file(subdomains_file) 130 | if not subdomains: 131 | console.print("[bold red]Error: No valid subdomains found in file.[/bold red]") 132 | return 133 | 134 | async with AsyncSessionLocal() as session: 135 | existing_entry = await get_domain(session, domain) 136 | if not existing_entry: 137 | console.print(f"[bold yellow]Domain {domain} does not exist.[/bold yellow]") 138 | return 139 | 140 | await add_or_update_domain(session, domain, subdomains) 141 | console.print(f"[bold green]Domain {domain} updated successfully![/bold green]") 142 | 143 | async def async_delete_domain(self, domain): 144 | async with AsyncSessionLocal() as session: 145 | success = await delete_domain(session, domain) 146 | if success: 147 | console.print(f"[bold red]Domain {domain} and all its records deleted successfully![/bold red]") 148 | else: 149 | console.print(f"[bold yellow]Domain {domain} does not exist.[/bold yellow]") 150 | 151 | async def async_export_data(self, domain, filename, format_type): 152 | async with AsyncSessionLocal() as session: 153 | subdomains = await get_subdomains(session, domain) 154 | if not subdomains: 155 | console.print(f"[bold yellow]No records exist for {domain}.[/bold yellow]") 156 | return 157 | 158 | if format_type.lower() == "txt": 159 | with open(filename, "w") as file: 160 | for subdomain in sorted(subdomains): 161 | file.write(f"{subdomain}\n") 162 | elif format_type.lower() == "json": 163 | with open(filename, "w") as file: 164 | json.dump({"domain": domain, "subdomains": sorted(subdomains)}, file, indent=4) 165 | else: 166 | console.print("[bold red]Error: Format must be 'txt' or 'json'.[/bold red]") 167 | return 168 | console.print(f"[bold green]Data exported to {filename} successfully![/bold green]") 169 | 170 | async def generate_report(self,domain,output_file,format_type): 171 | htmlpath = html_config() 172 | if not htmlpath: 173 | console.print(f"[bold yellow]Unable to get the html configuration template,please check the html template file exist.[/bold yellow]") 174 | return 175 | async with AsyncSessionLocal() as session: 176 | subdomains = await get_subdomains(session,domain) 177 | if not subdomains: 178 | console.print(f"[bold yellow]No records exist for {domain} to generate report.[/bold yellow]") 179 | return 180 | 181 | report_data = { 182 | "domain": domain, 183 | "subdomains": sorted(subdomains) 184 | } 185 | 186 | html_report = self.generate_html_report(report_data=report_data, template_path=htmlpath) 187 | 188 | if format_type.lower() == "html": 189 | with open(output_file, "w") as f: 190 | f.write(html_report) 191 | console.print(f"[bold green]HTML report saved as {output_file}[/bold green]") 192 | elif format_type.lower() == "pdf": 193 | self.generate_pdf_report(html_report, output_file) 194 | console.print(f"[bold green]PDF report saved as {output_file}[/bold green]") 195 | else: 196 | console.print(f"[bold yellow]Report generation only supports pdf/html format, please use a valid report generation format.[/bold yellow]") 197 | return 198 | def generate_html_report(self, report_data, template_path): 199 | env = Environment(loader=FileSystemLoader(os.path.dirname(template_path))) 200 | template = env.get_template(os.path.basename(template_path)) 201 | return template.render(domain=report_data["domain"], subdomains=report_data["subdomains"]) 202 | 203 | def generate_pdf_report(self,html_report, output_file): 204 | HTML(string=html_report).write_pdf(output_file) 205 | 206 | def load_subdomains_from_file(self, filename): 207 | if not os.path.exists(filename): 208 | console.print(f"[bold red]Error: File '{filename}' not found.[/bold red]") 209 | return None 210 | 211 | with open(filename, "r") as file: 212 | subdomains = {line.strip() for line in file if line.strip()} 213 | return subdomains if subdomains else None 214 | 215 | async def do_help(self): 216 | console.print( 217 | """ 218 | [bold][blue]Available Commands:[/blue][/bold] 219 | [bold][green]connect db[/green][/bold] [bold]- Connect to Subdominator DataBase[/bold] 220 | [bold][green]show domains[/green][/bold] [bold]- Show all domains[/bold] 221 | [bold][green]show subdomain [/green][/bold] [bold]- Show subdomains of a domain[/bold] 222 | [bold][green]add domain [/green][/bold] [bold]- Add a domain[/bold] 223 | [bold][green]update [/green][/bold] [bold]- Update an existing domain[/bold] 224 | [bold][green]delete [/green][/bold] [bold]- Delete a domain and all subdomains[/bold] 225 | [bold][green]export [/green][/bold] [bold]- Export data to txt/json[/bold] 226 | [bold][green]report [/green][/bold] [bold]- Generate subdomains report in html/pdf format[/bold] 227 | [bold][green]exit[/green][/bold] [bold]- Exit the interactive shell[/bold] 228 | [bold][green]help[/green][/bold] [bold]- Show this help menu[/bold] 229 | [bold][green]System Commands[/green][/bold] [bold]- ls, clear, pwd, cd, cat, echo, mkdir, rm, cp, mv[/bold] 230 | """ 231 | ) -------------------------------------------------------------------------------- /subdominator/modules/source/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/source/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/source/source.py: -------------------------------------------------------------------------------- 1 | def sources(): 2 | source = ["[***abuseipDB***](https://abuseipdb.com)", 3 | "[***alienvault***](https://otx.alienvault.com)", 4 | "[***anubis***](https://jldc.me/anubis)", 5 | "[***arpsyndicate****](https://www.arpsyndicate.io/pricing.html)", 6 | "[***bevigil****](https://bevigil.com/login)", 7 | "[***binaryedge****](https://binaryedge.io)", 8 | "[***bufferover****](https://tls.bufferover.run/)", 9 | "[***builtwith****](https://api.builtwith.com/domain-api)", 10 | "[***c99****](https://subdomainfinder.c99.nl/)", 11 | "[***censys****](https://censys.com/)", 12 | "[***certspotter****](https://sslmate.com/certspotter/)", 13 | "[***chaos****](https://chaos.projectdiscovery.io/)", 14 | "[***coderog****](https://rapidapi.com/coderog-coderog-default/api/subdomain-finder5/pricing)", 15 | "[***commoncrawl***](https://index.commoncrawl.org/)", 16 | "[***crtsh***](https://crt.sh)", "[***cyfare***](https://cyfare.net)", 17 | "[***digitorus***](https://www.digitorus.com/)", 18 | "[***digitalyama****](https://digitalyama.com/)", 19 | "[***dnsdumpster****](https://dnsdumpster.com/)", 20 | "[***dnsrepo****](https://dnsarchive.net/)", 21 | "[***fofa****](https://en.fofa.info/)", 22 | "[***facebook****](https://developers.facebook.com/)", 23 | "[***fullhunt****](https://fullhunt.io/)", 24 | "[***google****](https://programmablesearchengine.google.com/controlpanel/create)", 25 | "[***hackertarget***](https://hackertarget.com/)", 26 | "[***hudsonrock***](https://cavalier.hudsonrock.com)", 27 | "[***huntermap****](https://hunter.how/)", 28 | "[***intelx****](https://intelx.io/)", 29 | "[***leakix****](https://leakix.net/)", 30 | "[***merklemap****](https://www.merklemap.com)", 31 | "[***myssl***](https://myssl.com)", 32 | "[***netlas****](https://netlas.io/)", 33 | "[***odin****](https://odin.io/)", 34 | "[***quake****](https://quake.360.cn/)", 35 | "[***racent***](https://face.racent.com)", 36 | "[***rapidapi****](https://rapidapi.com/hub)", 37 | "[***rapiddns***](https://rapiddns.io/)", 38 | "[***redhuntlabs****](https://devportal.redhuntlabs.com/)", 39 | "[***rsecloud****](https://rsecloud.com/search)", 40 | "[***securitytrails****](http://securitytrails.com/)", 41 | "[***shodan****](https://shodan.io)", 42 | "[***shodanx***](https://github.com/RevoltSecurities/Shodanx)", 43 | "[***shrewdeye***](https://shrewdeye.app/api)", 44 | "[***sitedossier***](https://sitedossier.com/)", 45 | "[***threatcrowd***](http://ci-www.threatcrowd.org/)", 46 | "[***trickest****](https://trickest.io/)","[***urlscan***](https://urlscan.io/)", 47 | "[***virustotal****](https://virustotal.com/)", 48 | "[***waybackarchive***](https://archive.org/wayback)", 49 | "[***whoisxml****](https://whoisxmlapi.com)", 50 | "[***zoomeyeapi****](https://www.zoomeye.hk/)", 51 | "[***rapidfinder****](https://rapidapi.com/Glavier/api/subdomain-finder3/pricing)", 52 | "[***rapidscan****](https://rapidapi.com/sedrakpc/api/subdomain-scan1/pricing)",] 53 | return source 54 | -------------------------------------------------------------------------------- /subdominator/modules/subscraper/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/abuseipdb/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/abuseipdb/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/abuseipdb/abuseipdb.py: -------------------------------------------------------------------------------- 1 | import re 2 | from subdominator.modules.utils.utils import check_subdomain,UserAgents 3 | import httpx 4 | from subdominator.modules.logger.logger import logger 5 | abuseipdbs = [] 6 | 7 | async def abuseipdb(domain: str, session: httpx.AsyncClient, args) -> list[str]: 8 | try: 9 | 10 | if args.include_resources and "abuseipdb" not in args.include_resources and not args.all: 11 | return abuseipdbs 12 | 13 | if args.exclude_resources and "abuseipdb" in args.exclude_resources: 14 | return abuseipdbs 15 | 16 | parsed_domain = check_subdomain(domain) 17 | if parsed_domain.subdomain: 18 | return abuseipdbs 19 | url = f"https://www.abuseipdb.com/whois/{domain}" 20 | headers = { 21 | "User-Agent": UserAgents(), 22 | "Cookie": "abuseipdb_session=" 23 | } 24 | response: httpx.Response = await session.request("GET", url, timeout=args.timeout, headers=headers) 25 | data = response.text 26 | if response.status_code != 200: 27 | return abuseipdbs 28 | tags = re.findall(r'
  • \w.*
  • ', data) 29 | subdomains = [re.sub("", "", tag) + f".{domain}" for tag in tags] 30 | abuseipdbs.extend(subdomains) 31 | except httpx.TimeoutException as e: 32 | if args.show_timeout_info: 33 | logger(f"Timeout exception occurred in the AbusiIpdb API due to: {e}", "warn", args.no_color) 34 | except Exception as e: 35 | if args.verbose: 36 | logger(f"Exception error occurred in AbuseIpdb module due to: {e}, {type(e)}", "warn", args.no_color) 37 | finally: 38 | if args.verbose: 39 | logger(f"Total Subdomains found by Abuseipdb API: {len(abuseipdbs)}", "info") 40 | return abuseipdbs -------------------------------------------------------------------------------- /subdominator/modules/subscraper/alienvault/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/alienvault/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/alienvault/alientvault.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents 4 | alienvaults = [] 5 | 6 | async def alienvault(domain: str, session: httpx.AsyncClient,args) -> list[str]: 7 | try: 8 | if args.include_resources and "alienvault" not in args.include_resources and not args.all: 9 | return alienvaults 10 | 11 | if args.exclude_resources and "alienvault" in args.exclude_resources: 12 | return alienvaults 13 | 14 | headers = {"User-Agents": UserAgents()} 15 | url = f"https://otx.alienvault.com/api/v1/indicators/hostname/{domain}/passive_dns" 16 | response: httpx.Response = await session.request("GET",url, timeout=args.timeout, headers=headers) 17 | if response.status_code != 200: 18 | return alienvaults 19 | data = response.json() 20 | for entries in data['passive_dns']: 21 | subdomain = entries['hostname'] 22 | if subdomain.endswith(f".{domain}"): 23 | alienvaults.append(subdomain) 24 | except httpx.TimeoutException as e: 25 | if args.show_timeout_info: 26 | logger(f"Timeout reached for Alienvault API, due to: {e}", "warn", args.no_color) 27 | except Exception as e: 28 | if args.verbose: 29 | logger(f"Exception error occurred in Alienvalut module due to: {e}, {type(e)}", "warn", args.no_color) 30 | finally: 31 | if args.verbose: 32 | logger(f"Total Subdomains found by Alienvault API: {len(alienvaults)}", "info") 33 | return alienvaults -------------------------------------------------------------------------------- /subdominator/modules/subscraper/anubis/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/anubis/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/anubis/anubis.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents 4 | anubiss = [] 5 | 6 | async def anubis(domain: str, session: httpx.AsyncClient, args): 7 | try: 8 | 9 | if args.include_resources and "anubis" not in args.include_resources and not args.all: 10 | return anubiss 11 | 12 | if args.exclude_resources and "anubis" in args.exclude_resources: 13 | return anubiss 14 | 15 | headers = {"User-Agent": UserAgents()} 16 | url = f"https://anubisdb.com/anubis/subdomains/{domain}" 17 | response: httpx.Response = await session.get(url, timeout=args.timeout,headers=headers, follow_redirects=True) 18 | if response.status_code != 200: 19 | return anubiss 20 | data = response.json() 21 | for subdomain in data: 22 | if subdomain.endswith(f".{domain}") : 23 | anubiss.append(subdomain) 24 | except httpx.TimeoutException as e: 25 | if args.show_timeout_info: 26 | logger(f"Timeout reached for Anubis API, due to: {e}", "warn", args.no_color) 27 | except Exception as e: 28 | if args.verbose: 29 | logger(f"Exception error occurred in Anubis API module due to: {e}, {type(e)}", "warn", args.no_color) 30 | finally: 31 | if args.verbose: 32 | logger(f"Total Subdomains found by Anubis API: {len(anubiss)}", "info", args.no_color) 33 | return anubiss -------------------------------------------------------------------------------- /subdominator/modules/subscraper/arpsyndicate/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/arpsyndicate/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/arpsyndicate/arpsyndicate.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import singlekeyloader 4 | from urllib.parse import unquote 5 | arpsyndicates = [] 6 | 7 | async def arpsyndicate(domain: str, session: httpx.AsyncClient, configs: str,args): 8 | try: 9 | if args.include_resources and "arpsyndicate" not in args.include_resources and not args.all: 10 | return arpsyndicates 11 | 12 | if args.exclude_resources and "arpsyndicate" in args.exclude_resources: 13 | return arpsyndicates 14 | 15 | randomkey = await singlekeyloader(configs,"arpsyndicate") 16 | if randomkey is None: 17 | return arpsyndicates 18 | 19 | url = f"https://api.subdomain.center/beta/?domain={domain}&auth={randomkey}" 20 | response: httpx.Response = await session.request("GET",url,timeout=args.timeout) 21 | if response.status_code != 200: 22 | return [] 23 | data = response.json() 24 | arpsyndicates.extend(data) 25 | for subdomain in data: 26 | if "%" in subdomain: 27 | subdomain = unquote(subdomain) 28 | arpsyndicates.append(subdomain) 29 | except httpx.TimeoutException as e: 30 | if args.show_timeout_info: 31 | logger(f"Timeout reached for Arpsyndicate due to: {e}", "warn", args.no_color) 32 | except Exception as e: 33 | if args.verbose: 34 | logger(f"Exception in Arpsyndicate API module due to: {e}, {type(e)}", "warn", args.no_color) 35 | finally: 36 | if args.verbose: 37 | logger(f"Total Subdomains found by Arpsyndicate: {len(arpsyndicates)}", "info", args.no_color) 38 | return arpsyndicates -------------------------------------------------------------------------------- /subdominator/modules/subscraper/bevigil/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/bevigil/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/bevigil/bevigil.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents,singlekeyloader 4 | bevigils = [] 5 | 6 | async def bevigil(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "bevigil" not in args.include_resources and not args.all: 9 | return bevigils 10 | 11 | if args.exclude_resources and "bevigil" in args.exclude_resources: 12 | return bevigils 13 | 14 | randomkey = await singlekeyloader(configs,"bevigil") 15 | 16 | if randomkey is None: 17 | return bevigils 18 | 19 | url = f"https://osint.bevigil.com/api/{domain}/subdomains" 20 | headers = { 21 | 'User-Agent': UserAgents(), 22 | 'X-Access-Token': randomkey 23 | } 24 | response: httpx.Response = await session.request("GET",url, headers=headers, timeout=args.timeout) 25 | if response.status_code != 200: 26 | if args.show_key_info: 27 | logger(f"Bevigil blocking our request, {username} please check your api usage for this key: {randomkey}", "warn", args.no_color) 28 | return [] 29 | data = response.json() 30 | subdomains = data.get("subdomains", []) 31 | for subdomain in subdomains: 32 | bevigils.append(subdomain) 33 | except httpx.TimeoutException as e: 34 | if args.show_timeout_info: 35 | logger(f"Timeout reached for Bevigil API, due to: {e}", "warn", args.no_color) 36 | except Exception as e: 37 | if args.verbose: 38 | logger(f"Exception error occurred in Bevigil API module due to: {e}, {type(e)}", "warn", args.no_color) 39 | finally: 40 | if args.verbose: 41 | logger(f"Total Subdomins found by Bevigil API: {len(bevigils)}", "info", args.no_color) 42 | return bevigils -------------------------------------------------------------------------------- /subdominator/modules/subscraper/binaryedge/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/binaryedge/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/binaryedge/binaryedge.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents,singlekeyloader 4 | binaryedges = [] 5 | 6 | async def binaryget(domain: str, session: httpx.AsyncClient, randkey: str, pagenum: int, pagesize: int, args): 7 | try: 8 | url = f"https://api.binaryedge.io/v2/query/domains/subdomain/{domain}?page={pagenum}&pagesize={pagesize}" 9 | auth = { 10 | 'User-Agent': UserAgents(), 11 | 'X-Key': f'{randkey}' 12 | } 13 | response: httpx.Response = await session.request("GET",url, timeout=args.timeout, headers=auth) 14 | if response.status_code != 200: 15 | return 16 | data = response.json() 17 | subdomains = data.get("events", []) 18 | if subdomains: 19 | return subdomains 20 | except httpx.TimeoutException as e: 21 | if args.show_timeout_info: 22 | logger(f"Timeout reached for Binaryegde API, due to: {e}", "warn", args.no_color) 23 | except Exception as e: 24 | if args.verbose: 25 | logger(f"Exception occurred in the Binaryedge request module due to: {e}, {type(e)}","warn", args.no_color) 26 | 27 | async def binaryedge(domain, session, configs, username, args) -> list[str]: 28 | try: 29 | if args.include_resources and "binaryedge" not in args.include_resources and not args.all: 30 | return binaryedges 31 | if args.exclude_resources and "binaryedge" in args.exclude_resources: 32 | return binaryedges 33 | pagenum = 1 34 | pagesize = 100 35 | while True: 36 | randomkey = await singlekeyloader(configs, "binaryedge") 37 | if randomkey is None: 38 | break 39 | subdomains = await binaryget(domain, session, randomkey, pagenum, pagesize, args) 40 | if subdomains: 41 | for subdomain in subdomains: 42 | binaryedges.append(subdomain) 43 | if not subdomains: 44 | break 45 | pagenum += 1 46 | except Exception as e: 47 | if args.verbose: 48 | logger(f"Exception occurred in the Binaryedge API due to: {e}, {type(e)}", "warn", args.no_color) 49 | finally: 50 | if args.verbose: 51 | logger(f"Total Subdomains found by Binaryedge API: {len(binaryedges)}", "info", args.no_color) 52 | return binaryedges -------------------------------------------------------------------------------- /subdominator/modules/subscraper/bufferover/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/bufferover/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/bufferover/bufferover.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents,singlekeyloader 4 | bufferovers = [] 5 | 6 | async def bufferover(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "bufferover" not in args.include_resources and not args.all: 9 | return bufferovers 10 | 11 | if args.exclude_resources and "bufferover" in args.exclude_resources: 12 | return bufferovers 13 | 14 | randomkey = await singlekeyloader(configs, "bufferover") 15 | if randomkey is None: 16 | return bufferovers 17 | url = f"https://tls.bufferover.run/dns?q=.{domain}" 18 | auth = { 19 | 'User-Agent': UserAgents(), 20 | 'x-api-key': randomkey 21 | } 22 | response: httpx.Response = await session.request("GET",url, headers=auth, timeout=args.timeout) 23 | if response.status_code != 200: 24 | if args.show_key_info: 25 | logger(f"Bufferover blocking our request, {username} please check your api usage for this key: {randomkey}", "warn", args.no_color) 26 | return bufferovers 27 | 28 | data = response.json() 29 | if 'Results' in data: 30 | results = data['Results'] 31 | for result in results: 32 | elements = result.split(',') 33 | subdomain = elements[4].strip() 34 | bufferovers.append(subdomain) 35 | except httpx.TimeoutException as e: 36 | if args.show_timeout_info: 37 | logger(f"Timeout reached for Bufferover API due to: {e}, {type(e)}", "warn", args.no_color) 38 | except Exception as e: 39 | if args.verbose: 40 | logger(f"Exception occurred in Bufferover API module due to: {e}, {type(e)}", "warn", args.no_color) 41 | finally: 42 | if args.verbose: 43 | logger(f"Total Subdomains found by Bufferover API: {len(bufferovers)}", "info", args.no_color) 44 | return bufferovers -------------------------------------------------------------------------------- /subdominator/modules/subscraper/builtwith/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/builtwith/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/builtwith/builtwith.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents,singlekeyloader 4 | Builtwiths = [] 5 | 6 | async def builtwith(domain: str,session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "builtwith" not in args.include_resources and not args.all: 9 | return Builtwiths 10 | 11 | if args.exclude_resources and "builtwith" in args.exclude_resources: 12 | return Builtwiths 13 | 14 | randomkey = await singlekeyloader(configs, "builtwith") 15 | if randomkey is None: 16 | return Builtwiths 17 | url = f"https://api.builtwith.com/v21/api.json?KEY={randomkey}&HIDETEXT=yes&HIDEDL=yes&NOLIVE=yes&NOMETA=yes&NOPII=yes&NOATTR=yes&LOOKUP={domain}" 18 | headers = { 19 | "User-Agent": UserAgents() 20 | } 21 | response: httpx.Response = await session.request("GET",url, headers=headers, timeout=args.timeout) 22 | if response.status_code != 200: 23 | if args.show_key_info: 24 | logger(f"Builtwith blocking our request, {username} please check your api usage for this key: {randomkey}", "warn", args.no_color) 25 | return Builtwiths 26 | 27 | jdata = response.json() 28 | if isinstance(jdata, dict): 29 | results = jdata.get("Results", []) 30 | for result in results: 31 | for chunk in result.get("Result", {}).get("Paths", []): 32 | domain_name = chunk.get("Domain", "") 33 | subdomain = chunk.get("SubDomain", "") 34 | if domain_name and subdomain: 35 | Builtwiths.append(f"{subdomain}.{domain_name}") 36 | except httpx.TimeoutException as e: 37 | logger(f"Timeout reached for Builtwith API, due to: {e}", "warn", args.no_color) 38 | except Exception as e: 39 | if args.verbose: 40 | logger(f"Exception occurred in Builtwith API module due to: {e}, {type(e)}", "warn", args.no_color) 41 | finally: 42 | if args.verbose: 43 | logger(f"Total Subdomains found by Builtwith API: {len(Builtwiths)}", "info", args.no_color) 44 | return Builtwiths -------------------------------------------------------------------------------- /subdominator/modules/subscraper/c99/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/c99/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/c99/c99.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents,singlekeyloader 4 | C99s = [] 5 | 6 | async def c99(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "c99" not in args.include_resources and not args.all: 9 | return C99s 10 | 11 | if args.exclude_resources and "c99" in args.exclude_resources: 12 | return C99s 13 | 14 | randomkey = await singlekeyloader(configs, "c99") 15 | if randomkey is None: 16 | return C99s 17 | 18 | url = f"https://api.c99.nl/subdomainfinder?key={randomkey}&domain={domain}&json=true" 19 | headers = {"User-Agent": UserAgents()} 20 | 21 | response: httpx.Response = await session.request("GET",url, headers=headers,timeout=args.timeout) 22 | if response.status_code != 200: 23 | return C99s 24 | data = response.json() 25 | if "subdomain" in data: 26 | subs= [entry["subdomain"] for entry in data] 27 | C99s.extend(subs) 28 | except httpx.TimeoutException as e: 29 | logger(f"Timeout Reached for C99 API due to: {e}", "warn", args.no_color) 30 | except Exception as e: 31 | if args.verbose: 32 | logger(f"Exception occurred in C99 API module due to: {e}, {type(e)}", "warn", args.no_color) 33 | finally: 34 | if args.verbose: 35 | logger(f"Total Subdomains found by C99 API: {len(C99s)}", "info", args.no_color) 36 | return C99s -------------------------------------------------------------------------------- /subdominator/modules/subscraper/censys/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/censys/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/censys/censys.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import dualkeyloader 4 | censyss = [] 5 | 6 | async def censys(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | 8 | try: 9 | if args.include_resources and "censys" not in args.include_resources and not args.all: 10 | return censyss 11 | 12 | if args.exclude_resources and "censys" in args.exclude_resources: 13 | return censyss 14 | 15 | randomtoken,randomsecret = await dualkeyloader(configs, "censys", False) 16 | 17 | if randomtoken is None or randomsecret is None: 18 | return censyss 19 | 20 | url = "https://search.censys.io/api/v2/certificates/search" 21 | maxpage = 10 22 | maxdata = 100 23 | cursor = None 24 | params = {'q': domain, 'per_page': maxdata} 25 | 26 | for _ in range(maxpage+1): 27 | if cursor: 28 | params['cursor'] = cursor 29 | response: httpx.Response = await session.request("GET",url, auth=httpx.BasicAuth(randomtoken, randomsecret), params=params, timeout=args.timeout) 30 | if response.status_code != 200: 31 | if args.show_key_info: 32 | logger(f"Censys blocking our request, {username} please check your api usage for this keys: {randomsecret}, {randomtoken}", "warn", args.no_color) 33 | return censyss 34 | 35 | data = response.json() 36 | if 'result' in data and 'hits' in data['result']: 37 | for hit in data['result']['hits']: 38 | for name in hit.get('names', []): 39 | censyss.append(name) 40 | cursor = data['result']['links'].get('next') 41 | if not cursor: 42 | break 43 | except httpx.TimeoutException as e: 44 | if args.show_timeout_info: 45 | logger(f"Timeout reached for Censys API due to: {e}", "warn", args.no_color) 46 | except Exception as e: 47 | if args.verbose: 48 | logger(f"Exception occurred in Censys API module due to: {e}, {type(e)}", "warn", args.no_color) 49 | finally: 50 | if args.verbose: 51 | logger(f"Total Subdomains Found by Censys API: {len(censyss)}", "info", args.no_color) -------------------------------------------------------------------------------- /subdominator/modules/subscraper/certspotter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/certspotter/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/certspotter/certspotter.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import singlekeyloader 4 | certspotters = [] 5 | 6 | async def certspotter(domain: str, session: httpx.AsyncClient, configs: str, args): 7 | try: 8 | if not (args.all or (args.include_resources and "certspotter" in args.include_resources)): 9 | return certspotters 10 | 11 | randomkey = await singlekeyloader(configs, "certspotter") 12 | if not randomkey: 13 | return certspotters 14 | 15 | headers = {"Authorization": f"Bearer {randomkey}"} 16 | base_url = f"https://api.certspotter.com/v1/issuances?domain={domain}&include_subdomains=true&expand=dns_names" 17 | 18 | while base_url: 19 | response : httpx.Response = await session.request("GET",base_url, headers=headers, timeout=args.timeout) 20 | if response.status_code != 200: 21 | if args.verbose: 22 | logger(f"CertSpotter API returned base response status : {response.status_code}", "warn", args.no_color) 23 | return certspotters 24 | 25 | data = response.json() 26 | if not isinstance(data, list) or not data: 27 | return certspotters 28 | 29 | for entry in data: 30 | certspotters.extend(entry.get("dns_names", [])) 31 | 32 | last_id = data[-1].get("id") 33 | if last_id: 34 | base_url = f"https://api.certspotter.com/v1/issuances?domain={domain}&include_subdomains=true&expand=dns_names&after={last_id}" 35 | else: 36 | base_url = None 37 | 38 | except httpx.TimeoutException as e: 39 | if args.show_timeout_info: 40 | logger(f"Timeout reached for CertSpotter API due to: {e}", "warn", args.no_color) 41 | 42 | except Exception as e: 43 | if args.verbose: 44 | logger(f"Exception error occurred in CertSpotter API module due to: {e}, {type(e)}", "warn", args.no_color) 45 | finally: 46 | if args.verbose: 47 | logger(f"Total Subdomains found by CertSpotter API: {len(certspotters)}", "info", args.no_color) 48 | return certspotters 49 | -------------------------------------------------------------------------------- /subdominator/modules/subscraper/chaos/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/chaos/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/chaos/chaos.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import singlekeyloader 4 | chaoss = [] 5 | 6 | async def chaos(domain: str, session: httpx.AsyncClient, configs: str, args): 7 | try: 8 | if args.include_resources and "chaos" not in args.include_resources and not args.all: 9 | return chaoss 10 | 11 | if args.exclude_resources and "chaos" in args.exclude_resources: 12 | return chaoss 13 | 14 | randomkey = await singlekeyloader(configs, "chaos") 15 | if not randomkey: 16 | return chaoss 17 | 18 | url = f"https://dns.projectdiscovery.io/dns/{domain}/subdomains" 19 | headers = {"Authorization": randomkey} 20 | 21 | response: httpx.Response = await session.request("GET", url, headers=headers, timeout=args.timeout) 22 | if response.status_code != 200: 23 | if args.verbose: 24 | logger(f"Chaos API returned response status: {response.status_code}", "warn", args.no_color) 25 | return chaoss 26 | 27 | data = response.json() 28 | if "subdomains" in data: 29 | for subdomain in data["subdomains"]: 30 | if subdomain == "": 31 | continue 32 | if not subdomain.endswith(f".{domain}"): 33 | chaoss.append(f"{subdomain}.{domain}") 34 | else: 35 | chaoss.append(subdomain) 36 | except httpx.TimeoutException as e: 37 | if args.show_timeout_info: 38 | logger(f"Timeout reached for Chaos API due to: {e}", "warn", args.no_color) 39 | except Exception as e: 40 | if args.verbose: 41 | logger(f"Exception error occurred in Chaos API module due to: {e}, {type(e)}", "warn", args.no_color) 42 | finally: 43 | if args.verbose: 44 | logger(f"Total Subdomains found by Chaos API: {len(chaoss)}", "info", args.no_color) 45 | return chaoss -------------------------------------------------------------------------------- /subdominator/modules/subscraper/coderog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/coderog/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/coderog/coderog.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import singlekeyloader 4 | coderogs = [] 5 | 6 | async def coderog(domain: str, session: httpx.AsyncClient, configs: str, args): 7 | try: 8 | if args.include_resources and "coderog" not in args.include_resources and not args.all: 9 | return coderogs 10 | 11 | if args.exclude_resources and "coderog" in args.exclude_resources: 12 | return coderogs 13 | 14 | randomkey = await singlekeyloader(configs, "rapidapi") 15 | if not randomkey: 16 | return coderogs 17 | 18 | url = f"https://subdomain-finder5.p.rapidapi.com/subdomain-finder?domain={domain}" 19 | headers = { 20 | "x-rapidapi-key": randomkey, 21 | "x-rapidapi-host": "subdomain-finder5.p.rapidapi.com" 22 | } 23 | 24 | response: httpx.Response = await session.request("GET", url, headers=headers, timeout=args.timeout) 25 | if response.status_code == 403: 26 | if args.verbose: 27 | logger(f"CodeRog API blocked request. Ensure you are subscribed to the API service for key: {randomkey}", "warn", args.no_color) 28 | return coderogs 29 | 30 | if response.status_code != 200: 31 | if args.verbose: 32 | logger(f"CodeRog API returned response status: {response.status_code}. Check API usage for key: {randomkey}", "warn", args.no_color) 33 | return coderogs 34 | 35 | data = response.json() 36 | for subdomains in data.get("data", []): 37 | subdomain = subdomains["subdomain"] 38 | if subdomain.endswith(f".{domain}"): 39 | coderogs.append(subdomain) 40 | except httpx.TimeoutException: 41 | if args.show_timeout_info: 42 | logger("Timeout reached for CodeRog API due to server-side or client-side error.", "warn", args.no_color) 43 | 44 | except Exception as e: 45 | if args.verbose: 46 | logger(f"Exception occurred in CodeRog API module: {e}, {type(e)}", "warn", args.no_color) 47 | 48 | finally: 49 | if args.verbose: 50 | logger(f"Total Subdomains found by CodeRog API: {len(coderogs)}", "info", args.no_color) 51 | return coderogs 52 | -------------------------------------------------------------------------------- /subdominator/modules/subscraper/commoncrawl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/commoncrawl/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/commoncrawl/commoncrawl.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import httpx 3 | from datetime import datetime 4 | from colorama import Fore, Style 5 | from subdominator.modules.utils.utils import extracts, UserAgents, Exit 6 | from subdominator.modules.logger.logger import logger 7 | 8 | Commoncrawls = set() 9 | 10 | async def indexDB(args): 11 | try: 12 | headers = { 13 | "User-Agent": UserAgents() 14 | } 15 | async with httpx.AsyncClient(headers=headers, timeout=args.timeout, verify=False,proxy=args.proxy) as session: 16 | response = await session.request("GET", "https://index.commoncrawl.org/collinfo.json", follow_redirects=True) 17 | return response.json() 18 | except httpx.RemoteProtocolError: 19 | pass 20 | except httpx.ReadTimeout: 21 | pass 22 | except httpx.TimeoutException as e: 23 | logger(f"Timeout Reached for Commoncrawl API IndexDB due to: {e}", "warn", args.no_color) 24 | except Exception as e: 25 | if args.verbose: 26 | logger(f"Exception occurred in the Commoncrawl API IndexDB module due to: {e}", "warn", args.no_color) 27 | 28 | async def CcClient(args,searchurl: str,domain): 29 | try: 30 | headers = { 31 | "User-Agent": UserAgents() 32 | } 33 | async with httpx.AsyncClient(timeout=httpx.Timeout(read=300.0, connect=args.timeout, write=None, pool=None), headers=headers, verify=False, proxy=args.proxy) as request: 34 | async with request.stream("GET", f"{searchurl}?url=*.{domain}", follow_redirects=True) as response: 35 | async for url in response.aiter_lines(): 36 | subdomains = await extracts(url, domain) 37 | if subdomains: 38 | for subdomain in subdomains: 39 | subdomain = subdomain.lstrip("25").lstrip("2F").lstrip("40").lstrip(".") 40 | if subdomain not in Commoncrawls and not subdomain.startswith("%3D") and not subdomain.startswith("3D"): 41 | Commoncrawls.add(subdomain) 42 | except httpx.RemoteProtocolError: 43 | pass 44 | except httpx.ReadTimeout: 45 | pass 46 | except httpx.TimeoutException as e: 47 | logger(f"Timeout Reached for Commoncrawl API Client due to: {e}", "warn", args.no_color) 48 | except Exception as e: 49 | if args.verbose: 50 | logger(f"Exception occurred in the Commoncrawl API Client module due to: {e}", "warn", args.no_color) 51 | 52 | async def commoncrawl(Domain, args): 53 | try: 54 | 55 | if not (args.all or (args.include_resources and "commoncrawl" in args.include_resources)): 56 | return Commoncrawls 57 | 58 | indexurls = [] 59 | added = set() 60 | responsed = await indexDB(args) 61 | if responsed is None: 62 | return Commoncrawls 63 | ctyear = datetime.now().year 64 | years = [str(ctyear - i) for i in range(6)] 65 | for year in years: 66 | for index in responsed: 67 | if year not in added: 68 | if year in index.get("name"): 69 | indexurls.append(index.get('cdx-api')) 70 | added.add(year) 71 | for url in indexurls: 72 | await CcClient(args, url, Domain) 73 | except Exception as e: 74 | if args.verbose: 75 | logger(f"Exception occurred at the Commoncrawl API core module due to: {e}, {type(e)}", "warn", args.no_color) 76 | finally: 77 | if args.verbose: 78 | logger(f"Total Subdomains found by Commoncrawl API: {len(Commoncrawls)}") 79 | return Commoncrawls -------------------------------------------------------------------------------- /subdominator/modules/subscraper/crtsh/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/crtsh/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/crtsh/crtsh.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents 4 | crtshs = [] 5 | 6 | async def crtsh(domain: str, session: httpx.AsyncClient, args): 7 | try: 8 | if args.include_resources and "crtsh" not in args.include_resources and not args.all: 9 | return crtshs 10 | 11 | if args.exclude_resources and "crtsh" in args.exclude_resources: 12 | return crtshs 13 | 14 | url = f"https://crt.sh/?q=%25.{domain}&output=json" 15 | headers = { 16 | "User-Agent": UserAgents() 17 | } 18 | 19 | timeout = httpx.Timeout(timeout=args.timeout, connect=args.timeout, read=300, write=args.timeout) 20 | response: httpx.Response = await session.request("GET", url, headers=headers, timeout=timeout) 21 | 22 | if response.status_code != 200: 23 | if args.verbose: 24 | logger(f"crt.sh API returned bad response status: {response.status_code}.", "warn", args.no_color) 25 | return crtshs 26 | 27 | data = response.json() 28 | for domains in data: 29 | for subdomain in domains["name_value"].split("\n"): 30 | crtshs.append(subdomain) 31 | except httpx.TimeoutException as e: 32 | if args.show_timeout_info: 33 | logger(f"Timeout reached for crt.sh API due to : {e}", "warn", args.no_color) 34 | except Exception as e: 35 | if args.verbose: 36 | logger(f"Exception occurred in crt.sh API module due to: {e}, {type(e)}", "warn", args.no_color) 37 | finally: 38 | if args.verbose: 39 | logger(f"Total Subdomains found by crt.sh API: {len(crtshs)}", "info", args.no_color) 40 | return crtshs -------------------------------------------------------------------------------- /subdominator/modules/subscraper/cyfare/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/cyfare/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/cyfare/cyfare.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents 4 | 5 | cyfares = [] 6 | 7 | async def cyfare(domain: str, session: httpx.AsyncClient, args): 8 | try: 9 | if args.include_resources and "cyfare" not in args.include_resources and not args.all: 10 | return cyfares 11 | 12 | if args.exclude_resources and "cyfare" in args.exclude_resources: 13 | return cyfares 14 | 15 | url = "https://cyfare.net/apps/VulnerabilityStudio/subfind/query.php" 16 | headers = { 17 | "User-Agent": UserAgents(), 18 | "Origin": "https://cyfare.net", 19 | "Content-Type": "application/json" 20 | } 21 | json_body = {"domain": domain} 22 | 23 | response: httpx.Response = await session.request("POST", url, headers=headers, json=json_body, timeout=args.timeout) 24 | if response.status_code != 200: 25 | if args.verbose: 26 | logger(f"Cyfare API returned bad response status: {response.status_code}.", "warn", args.no_color) 27 | return cyfares 28 | 29 | data = response.json() 30 | subdomains = data.get("subdomains", []) 31 | cyfares.extend(subdomains) 32 | except httpx.TimeoutException: 33 | if args.show_timeout_info: 34 | logger(f"Timeout reached for Cyfare API due to: {e}", "warn", args.no_color) 35 | 36 | except Exception as e: 37 | if args.verbose: 38 | logger(f"Exception occurred in Cyfare API module: {e}, {type(e)}", "warn", args.no_color) 39 | 40 | finally: 41 | if args.verbose: 42 | logger(f"Total Subdomains found by Cyfare API: {len(cyfares)}", "info", args.no_color) 43 | return cyfares 44 | -------------------------------------------------------------------------------- /subdominator/modules/subscraper/digitalyama/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/digitalyama/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/digitalyama/digitalyama.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, singlekeyloader 4 | digitalyamas = [] 5 | 6 | async def digitalyama(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | 9 | if args.include_resources and "digitalyama" not in args.include_resources and not args.all: 10 | return digitalyamas 11 | 12 | if args.exclude_resources and "digitalyama" in args.exclude_resources: 13 | return digitalyamas 14 | 15 | randomkey = await singlekeyloader(configs, "digitalyama") 16 | if randomkey is None: 17 | return digitalyamas 18 | 19 | url = "https://api.digitalyama.com/subdomain_finder" 20 | params = {"domain": domain} 21 | headers = { 22 | "User-Agent": UserAgents(), 23 | "x-api-key": randomkey 24 | } 25 | response: httpx.Response = await session.request("GET", url, headers=headers, params=params, timeout=args.timeout) 26 | if response.status_code != 200: 27 | if args.show_key_info: 28 | logger(f"DigitalYama blocking our request, {username} please check your API usage for this key: {randomkey}", "warn", args.no_color) 29 | return [] 30 | 31 | data = response.json() 32 | subdomains = data.get("subdomains", []) 33 | for subdomain in subdomains: 34 | digitalyamas.append(subdomain) 35 | except httpx.TimeoutException as e: 36 | if args.show_timeout_info: 37 | logger(f"Timeout reached for DigitalYama API, due to: {e}", "warn", args.no_color) 38 | except Exception as e: 39 | if args.verbose: 40 | logger(f"Exception error occurred in DigitalYama API module due to: {e}, {type(e)}", "warn", args.no_color) 41 | finally: 42 | if args.verbose: 43 | logger(f"Total Subdomains found by DigitalYama API: {len(digitalyamas)}", "info", args.no_color) 44 | return digitalyamas -------------------------------------------------------------------------------- /subdominator/modules/subscraper/digitorus/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/digitorus/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/digitorus/digitorus.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import re 3 | from subdominator.modules.logger.logger import logger 4 | from subdominator.modules.utils.utils import UserAgents 5 | digitorus_subs = [] 6 | 7 | async def digitorus(domain: str, session: httpx.AsyncClient, args): 8 | try: 9 | if args.include_resources and "digitorus" not in args.include_resources and not args.all: 10 | return digitorus_subs 11 | 12 | if args.exclude_resources and "digitorus" in args.exclude_resources: 13 | return digitorus_subs 14 | 15 | url = f"https://certificatedetails.com/{domain}" 16 | headers = {"User-Agent": UserAgents()} 17 | response: httpx.Response = await session.get(url, headers=headers, timeout=args.timeout) 18 | if response.status_code != 200: 19 | logger(f"Digitorus API returned bad response status: {response.status_code}.", "warn", args.no_color) 20 | return digitorus_subs 21 | 22 | data = response.text 23 | filterdomain = re.escape(domain) 24 | pattern = r'(?i)(?:https?://)?([a-zA-Z0-9*_.-]+\.' + filterdomain + r')' 25 | subdomains = re.findall(pattern, data) 26 | if subdomains: 27 | digitorus_subs.extend(subdomains) 28 | 29 | except httpx.TimeoutException as e: 30 | logger(f"Timeout reached for Digitorus API due to: {e}", "warn", args.no_color) 31 | 32 | except Exception as e: 33 | logger(f"Exception occurred in Digitorus API module: {e}, {type(e)}", "warn", args.no_color) 34 | 35 | finally: 36 | if args.verbose: 37 | logger(f"Total Subdomains found by Digitorus API: {len(digitorus_subs)}", "info", args.no_color) 38 | return digitorus_subs -------------------------------------------------------------------------------- /subdominator/modules/subscraper/dnsdumpster/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/dnsdumpster/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/dnsdumpster/dnsdumpster.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import singlekeyloader 4 | dnsdumpsters = [] 5 | 6 | async def dnsdumpster(domain: str, session: httpx.AsyncClient, configs: str, args): 7 | try: 8 | if args.include_resources and "dnsdumpster" not in args.include_resources and not args.all: 9 | return dnsdumpsters 10 | 11 | if args.exclude_resources and "dnsdumpster" in args.exclude_resources: 12 | return dnsdumpsters 13 | 14 | sections = ["a", "cname", "mx", "ns"] 15 | url = f"https://api.dnsdumpster.com/domain/{domain}" 16 | page = 1 17 | 18 | while True: 19 | randomkey = await singlekeyloader(configs, "dnsdumpster") 20 | if not randomkey: 21 | break 22 | params = {"page": page} 23 | headers = {"X-API-Key": randomkey} 24 | response: httpx.Response = await session.request("GET", url, headers=headers, params=params, timeout=args.timeout) 25 | if response.status_code != 200: 26 | return dnsdumpsters 27 | data = response.json() 28 | if "error" in data: 29 | return dnsdumpsters 30 | for section in sections: 31 | if section in data: 32 | dnsdumpsters.extend( 33 | record["host"] 34 | for record in data[section] 35 | if record["host"].endswith(f".{domain}") 36 | ) 37 | page += 1 38 | except httpx.TimeoutException as e: 39 | if args.show_timeout_info: 40 | logger(f"Timeout reached for Dnsdumpster API due to: {e}", "warn", args.no_color) 41 | except Exception as e: 42 | if args.verbose: 43 | logger(f"Exception occurred in Dnsdumpster API module: {e}, {type(e)}", "warn", args.no_color) 44 | finally: 45 | if args.verbose: 46 | logger(f"Total Subdomains found by Dnsdumpster API: {len(dnsdumpsters)}", "info", args.no_color) 47 | return dnsdumpsters 48 | -------------------------------------------------------------------------------- /subdominator/modules/subscraper/dnsrepo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/dnsrepo/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/dnsrepo/dnsrepo.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, dualkeyloader 4 | dnsrepo_results = [] 5 | 6 | async def dnsrepo(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "dnsrepo" not in args.include_resources and not args.all: 9 | return dnsrepo_results 10 | 11 | if args.exclude_resources and "dnsrepo" in args.exclude_resources: 12 | return dnsrepo_results 13 | 14 | token, randomkey = await dualkeyloader(configs, "dnsrepo", False) 15 | 16 | if token is None or randomkey is None: 17 | return dnsrepo_results 18 | 19 | url = f"https://dnsarchive.net/api/?apikey={randomkey}&search={domain}" 20 | headers = { 21 | 'User-Agent': UserAgents(), 22 | 'X-API-Access': token 23 | } 24 | response: httpx.Response = await session.request("GET", url, headers=headers, timeout=args.timeout) 25 | 26 | if response.status_code != 200: 27 | if args.show_key_info: 28 | logger(f"DNSRepo API blocking our request, {username}, please check your API usage for this key: {randomkey}", "warn", args.no_color) 29 | return [] 30 | 31 | data = response.json() 32 | if not isinstance(data, list): 33 | return dnsrepo_results 34 | subdomains = [ 35 | entry["domain"].rstrip(".") for entry in data 36 | if "domain" in entry and entry["domain"].rstrip(".").endswith(f".{domain}") 37 | ] 38 | dnsrepo_results.extend(subdomains) 39 | except httpx.TimeoutException as e: 40 | if args.show_timeout_info: 41 | logger(f"Timeout reached for DNSRepo API, due to: {e}", "warn", args.no_color) 42 | except Exception as e: 43 | if args.verbose: 44 | logger(f"Exception error occurred in DNSRepo API module due to: {e}, {type(e)}", "warn", args.no_color) 45 | finally: 46 | if args.verbose: 47 | logger(f"Total Subdomains found by DNSRepo API: {len(dnsrepo_results)}", "info", args.no_color) 48 | return dnsrepo_results -------------------------------------------------------------------------------- /subdominator/modules/subscraper/facebook/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/facebook/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/facebook/facebook.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import dualkeyloader 4 | fbcerts = [] 5 | 6 | async def facebook(domain: str, session: httpx.AsyncClient, configs: str, username: str, args) -> list[str]: 7 | try: 8 | if args.include_resources and "facebook" not in args.include_resources and not args.all: 9 | return fbcerts 10 | 11 | if args.exclude_resources and "facebook" in args.exclude_resources: 12 | return fbcerts 13 | 14 | randomid , randomsecret = await dualkeyloader(configs,"facebook",False) 15 | if randomid is None or randomsecret is None: 16 | return fbcerts 17 | randomtoken = f"{randomid}|{randomsecret}" 18 | url = f"https://graph.facebook.com/v18.0/certificates?fields=domains&access_token={randomtoken}&query={domain}&limit=1000" 19 | while True: 20 | response: httpx.Response = await session.request("GET",url, timeout=args.timeout) 21 | if response.status_code != 200: 22 | return fbcerts 23 | data = response.json() 24 | for item in data['data']: 25 | subdomains = item['domains'] 26 | for subdomain in subdomains: 27 | if subdomain.endswith(f"{domain}"): 28 | fbcerts.append(subdomain) 29 | pages = data.get("paging", {}) 30 | next_page = pages.get('next') 31 | if next_page: 32 | url = next_page 33 | if not next_page: 34 | break 35 | except httpx.TimeoutException as e: 36 | if args.show_timeout_info: 37 | logger(f"Timeout reached for Facebook API due to: {e}", "warn", args.no_color) 38 | except Exception as e: 39 | if args.verbose: 40 | logger(f"Exception occurred in the Facebook API module due to: {e}, {type(e)}", "warn", args.no_color) 41 | finally: 42 | if args.verbose: 43 | logger(f"Total Subdomains found by Facebook API: {len(fbcerts)}") 44 | return fbcerts -------------------------------------------------------------------------------- /subdominator/modules/subscraper/fofa/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/fofa/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/fofa/fofa.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import base64 3 | from subdominator.modules.logger.logger import logger 4 | from subdominator.modules.utils.utils import singlekeyloader 5 | FOFA = [] 6 | 7 | 8 | async def fofa(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 9 | try: 10 | if args.include_resources and "fofa" not in args.include_resources and not args.all: 11 | return FOFA 12 | 13 | if args.exclude_resources and "fofa" in args.exclude_resources: 14 | return FOFA 15 | 16 | randomkey = await singlekeyloader(configs, "fofa") 17 | if randomkey is None: 18 | return FOFA 19 | pagenum = 1 20 | domain_encoded = f"""domain="{domain}" """.encode('utf-8') 21 | subdomains = base64.b64encode(domain_encoded).decode('utf-8') 22 | while True: 23 | url = f"https://fofa.info/api/v1/search/all?key={randomkey}&qbase64={subdomains}&page={pagenum}&full=true&size=1000" 24 | response: httpx.Response = await session.request("GET", url, timeout=args.timeout) 25 | if response.status_code != 200: 26 | return FOFA 27 | data = response.json() 28 | if "results" not in data: 29 | return FOFA 30 | for result in data.get('results', []): 31 | url = result[0] 32 | if url.startswith("https://"): 33 | url = url.replace("https://", "") 34 | elif url.startswith("http://"): 35 | url = url.replace("http://", "") 36 | subdomain = url.split(':')[0] if ':' in url else url 37 | FOFA.append(subdomain) 38 | size = data.get('size') 39 | if size < 1000: 40 | return FOFA 41 | pagenum += 1 42 | except httpx.TimeoutException as e: 43 | if args.show_timeout_info: 44 | logger(f"Timeout reached for FOFA API due to: {e}", "warn", args.no_color) 45 | except Exception as e: 46 | if args.verbose: 47 | logger(f"Exception occurred in the FOFA API module due to: {e}, {type(e)}", "warn", args.no_color) 48 | finally: 49 | if args.verbose: 50 | logger(f"Total Subdomains found by FOFA API: {len(FOFA)}", "info", args.no_color) 51 | return FOFA 52 | -------------------------------------------------------------------------------- /subdominator/modules/subscraper/fullhunt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/fullhunt/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/fullhunt/fullhunt.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, singlekeyloader 4 | fullhunts = [] 5 | 6 | async def fullhunt(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "fullhunt" not in args.include_resources and not args.all: 9 | return fullhunts 10 | 11 | if args.exclude_resources and "fullhunt" in args.exclude_resources: 12 | return fullhunts 13 | 14 | randomkey = await singlekeyloader(configs, "fullhunt") 15 | if randomkey is None: 16 | return fullhunts 17 | url = f"https://fullhunt.io/api/v1/domain/{domain}/subdomains" 18 | headers = { 19 | "User-Agent": UserAgents(), 20 | "X-API-KEY": randomkey 21 | } 22 | response: httpx.Response = await session.request("GET", url, headers=headers, timeout=args.timeout) 23 | if response.status_code != 200: 24 | if args.show_key_info: 25 | logger(f"FullHunt blocking our request, {username}, please check your API usage for this key: {randomkey}","warn",args.no_color) 26 | return fullhunts 27 | data = response.json() 28 | subdomains = data.get("hosts", []) 29 | fullhunts.extend(subdomains) 30 | except httpx.TimeoutException as e: 31 | if args.show_timeout_info: 32 | logger(f"Timeout reached for FullHunt API, due to: {e}", "warn", args.no_color) 33 | except Exception as e: 34 | if args.verbose: 35 | logger(f"Exception error occurred in FullHunt API module due to: {e}, {type(e)}", "warn", args.no_color) 36 | finally: 37 | if args.verbose: 38 | logger(f"Total subdomains found by FullHunt API: {len(fullhunts)}", "info", args.no_color) 39 | return fullhunts 40 | -------------------------------------------------------------------------------- /subdominator/modules/subscraper/google/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/google/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/google/google.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents,dualkeyloader 4 | googles = [] 5 | 6 | async def google(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "google" not in args.include_resources and not args.all: 9 | return googles 10 | 11 | if args.exclude_resources and "google" in args.exclude_resources: 12 | return googles 13 | page = 1 14 | if args.dork: 15 | dork = args.dork 16 | else: 17 | dork = f"site:*.{domain}%20-www" 18 | while True: 19 | randomcx, randomkey = await dualkeyloader(configs, "google", False) 20 | if randomcx is None or randomkey is None: 21 | return googles 22 | url = f"https://customsearch.googleapis.com/customsearch/v1?q={dork}&cx={randomcx}&num=10&start={page}&key={randomkey}&alt=json" 23 | headers = { 24 | "User-Agent": UserAgents() 25 | } 26 | response: httpx.Response = await session.request("GET", url, headers=headers, timeout=args.timeout) 27 | if response.status_code != 200: 28 | return googles 29 | data = response.json() 30 | items = data.get("items", []) 31 | if not items: 32 | return googles 33 | for item in items: 34 | subdomain = item.get("displayLink") 35 | if subdomain: 36 | googles.append(subdomain) 37 | page += 1 38 | except httpx.TimeoutException as e: 39 | if args.show_timeout_info: 40 | logger(f"Timeout reached for Google API, due to: {e}", "warn", args.no_color) 41 | except Exception as e: 42 | if args.verbose: 43 | logger(f"Exception occurred in Google API module: {e}, {type(e)}", "warn", args.no_color) 44 | finally: 45 | if args.verbose: 46 | logger(f"Total subdomains found by Google API: {len(googles)}", "info", args.no_color) 47 | return googles -------------------------------------------------------------------------------- /subdominator/modules/subscraper/hackertarget/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/hackertarget/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/hackertarget/hackertarget.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | hackertargets = [] 4 | 5 | 6 | async def hackertarget(domain: str, session: httpx.AsyncClient, args) -> list[str]: 7 | try: 8 | if args.include_resources and "hackertarget" not in args.include_resources and not args.all: 9 | return hackertargets 10 | 11 | if args.exclude_resources and "hackertarget" in args.exclude_resources: 12 | return hackertargets 13 | 14 | url = f"https://api.hackertarget.com/hostsearch/?q={domain}" 15 | response: httpx.Response = await session.request("GET", url, timeout=args.timeout) 16 | if response.status_code != 200: 17 | return hackertargets 18 | data = response.text.splitlines() 19 | for subdomain in data: 20 | if "API count exceeded - Increase Quota with Membership" in subdomain: 21 | continue 22 | subdomain = subdomain.split(",")[0] 23 | hackertargets.append(subdomain) 24 | except httpx.TimeoutException as e: 25 | if args.show_timeout_info: 26 | logger(f"Timeout reached for Hackertarget API, due to: {e}", "warn", args.no_color) 27 | except Exception as e: 28 | if args.verbose: 29 | logger(f"Exception occurred in Hackertarget API module: {e}, {type(e)}", "warn", args.no_color) 30 | finally: 31 | if args.verbose: 32 | logger(f"Total subdomains found by Hackertarget API: {len(hackertargets)}", "info", args.no_color) 33 | return hackertargets -------------------------------------------------------------------------------- /subdominator/modules/subscraper/hudsonrock/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/hudsonrock/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/hudsonrock/hudsonrock.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents 4 | from urllib.parse import urlparse 5 | hudsonrocks = [] 6 | 7 | async def hudsonrock(domain: str, session: httpx.AsyncClient, args): 8 | try: 9 | if args.include_resources and "hudsonrock" not in args.include_resources and not args.all: 10 | return hudsonrocks 11 | 12 | if args.exclude_resources and "hudsonrock" in args.exclude_resources: 13 | return hudsonrocks 14 | 15 | headers = {"User-Agent": UserAgents()} 16 | url = f"https://cavalier.hudsonrock.com/api/json/v2/osint-tools/urls-by-domain?domain={domain}" 17 | response: httpx.Response = await session.get(url, timeout=args.timeout, headers=headers) 18 | 19 | if response.status_code != 200: 20 | return hudsonrocks 21 | data = response.json() 22 | if "data" in data: 23 | employees_urls = data["data"].get("employees_urls", []) 24 | clients_urls = data["data"].get("clients_urls", []) 25 | 26 | for record in employees_urls + clients_urls: 27 | parsed_url = urlparse(record.get("url", "")) 28 | subdomain = parsed_url.netloc 29 | 30 | if subdomain.endswith(f".{domain}") and "•" not in subdomain and subdomain not in hudsonrocks: 31 | hudsonrocks.append(subdomain) 32 | except httpx.TimeoutException as e: 33 | if args.show_timeout_info: 34 | logger(f"Timeout reached for HudsonRock API, due to: {e}", "warn", args.no_color) 35 | except Exception as e: 36 | if args.verbose: 37 | logger(f"Exception error occurred in HudsonRock API module due to: {e}, {type(e)}", "warn", args.no_color) 38 | finally: 39 | if args.verbose: 40 | logger(f"Total Subdomains found by HudsonRock API: {len(hudsonrocks)}", "info", args.no_color) 41 | return hudsonrocks -------------------------------------------------------------------------------- /subdominator/modules/subscraper/huntermap/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/huntermap/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/huntermap/huntermap.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import base64 3 | from datetime import datetime, timedelta 4 | from subdominator.modules.logger.logger import logger 5 | from subdominator.modules.utils.utils import singlekeyloader 6 | import time 7 | hunterhows = [] 8 | 9 | async def huntermap(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 10 | try: 11 | if args.include_resources and "huntermap" not in args.include_resources and not args.all: 12 | return hunterhows 13 | 14 | if args.exclude_resources and "huntermap" in args.exclude_resources: 15 | return hunterhows 16 | 17 | endtime = datetime.now().strftime("%Y-%m-%d") 18 | yeartime = datetime.now() - timedelta(days=27.8 * 12) 19 | inititatetime = yeartime.strftime("%Y-%m-%d") 20 | query = base64.urlsafe_b64encode(domain.encode("utf-8")).decode("ascii") 21 | page_size = 100 22 | page = 1 23 | 24 | while True: 25 | randomapikey = await singlekeyloader(configs, "huntermap") 26 | if not randomapikey: 27 | return hunterhows 28 | time.sleep(2.5) 29 | url = f"https://api.hunter.how/search?api-key={randomapikey}&query={query}&start_time={inititatetime}&end_time={endtime}&page={page}&page_size={page_size}" 30 | response: httpx.Response = await session.request("GET", url, timeout=args.timeout) 31 | if response.status_code != 200: 32 | if args.show_key_info: 33 | logger(f"Huntermap blocking our request, {username}, check your API usage for this key: {randomapikey}", "warn", args.no_color) 34 | return hunterhows 35 | data = response.json() 36 | subdomains = data.get("data", {}).get("list", []) 37 | total = data.get("data", {}).get("total", 0) 38 | 39 | for subdomain in subdomains: 40 | subdomain = subdomain["domain"] 41 | if subdomain.endswith(f".{domain}"): 42 | hunterhows.append(subdomain) 43 | if total <= len(hunterhows): 44 | break 45 | page += 1 46 | except httpx.TimeoutException as e: 47 | if args.show_timeout_info: 48 | logger(f"Timeout reached for Huntermap API: {e}", "warn", args.no_color) 49 | except Exception as e: 50 | if args.verbose: 51 | logger(f"Exception occurred in Huntermap API module: {e}, {type(e)}", "warn", args.no_color) 52 | finally: 53 | if args.verbose: 54 | logger(f"Total subdomains found by Huntermap API: {len(hunterhows)}", "info", args.no_color) 55 | return hunterhows -------------------------------------------------------------------------------- /subdominator/modules/subscraper/intelx/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/intelx/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/intelx/intelx.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import dualkeyloader, UserAgents 4 | intelxs = [] 5 | 6 | async def getID(domain: str, session: httpx.AsyncClient, host: str, key: str, args): 7 | try: 8 | baseurl = f"https://{host}/phonebook/search?k={key}" 9 | auth = {"User-Agent": UserAgents()} 10 | reqbody = { 11 | "Maxresults": 100000, 12 | "Media": 0, 13 | "Target": 1, 14 | "Term": domain, 15 | "Terminate": None, 16 | "Timeout": 20, 17 | } 18 | response: httpx.Response = await session.post(baseurl, headers=auth, timeout=10, json=reqbody) 19 | if response.status_code != 200: 20 | return None 21 | data = response.json() 22 | return data.get("id") 23 | except Exception as e: 24 | if args.sec_deb: 25 | logger(f"Exception in IntelX getID block: {e}, {type(e)}", "warn", args.no_color) 26 | return None 27 | 28 | async def intelx(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 29 | try: 30 | if args.include_resources and "intelx" not in args.include_resources and not args.all: 31 | return intelxs 32 | 33 | if args.exclude_resources and "intelx" in args.exclude_resources: 34 | return intelxs 35 | 36 | randhost, randkey = await dualkeyloader(configs, "intelx", False) 37 | 38 | if not randhost or not randkey: 39 | return intelxs 40 | 41 | id = await getID(domain, session, randhost, randkey, args) 42 | if not id: 43 | return intelxs 44 | 45 | while True: 46 | baseurl = f"https://{randhost}/phonebook/search/result?k={randkey}&id={id}&limit=10000" 47 | headers = {"User-Agent": UserAgents()} 48 | response: httpx.Response = await session.get(baseurl, headers=headers, timeout=args.timeout) 49 | if response.status_code != 200: 50 | return intelxs 51 | data = response.json() 52 | for item in data.get("selectors", []): 53 | subdomain = item.get("selectorvalue") 54 | if subdomain: 55 | intelxs.append(subdomain) 56 | if data.get("status") not in [0, 3]: 57 | break 58 | except httpx.TimeoutException as e: 59 | if args.show_timeout_info: 60 | logger(f"Timeout reached for IntelX API: {e}", "warn", args.no_color) 61 | except Exception as e: 62 | if args.verbose: 63 | logger(f"Exception in IntelX API module: {e}, {type(e)}", "warn", args.no_color) 64 | finally: 65 | if args.verbose: 66 | logger(f"Total subdomains found by IntelX API: {len(intelxs)}", "info", args.no_color) 67 | return intelxs -------------------------------------------------------------------------------- /subdominator/modules/subscraper/leakix/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/leakix/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/leakix/leakix.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import singlekeyloader 4 | leakixs = [] 5 | 6 | async def leakix(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "leakix" not in args.include_resources and not args.all: 9 | return leakixs 10 | if args.exclude_resources and "leakix" in args.exclude_resources: 11 | return leakixs 12 | 13 | randomkey = await singlekeyloader(configs, "leakix") 14 | if not randomkey: 15 | return leakixs 16 | 17 | url = f"https://leakix.net/api/subdomains/{domain}" 18 | headers = {"accept": "application/json", "api-key": randomkey} 19 | response: httpx.Response = await session.request("GET",url, headers=headers, timeout=args.timeout) 20 | 21 | if response.status_code != 200: 22 | if args.show_key_info: 23 | logger(f"LeakIX blocking request, {username} check API usage for key: {randomkey}", "alert", args.no_color) 24 | return leakixs 25 | data = response.json() 26 | for item in data: 27 | subdomain = item.get("subdomain") 28 | if subdomain: 29 | leakixs.append(subdomain) 30 | except httpx.TimeoutException: 31 | if args.show_timeout_info: 32 | logger("Timeout reached for LeakIX API", "warn", args.no_color) 33 | except Exception as e: 34 | if args.verbose: 35 | logger(f"Exception in LeakIX API module: {e}, {type(e)}", "warn", args.no_color) 36 | finally: 37 | if args.verbose: 38 | logger(f"Total subdomains found by LeakIX API: {len(leakixs)}", "info", args.no_color) 39 | return leakixs -------------------------------------------------------------------------------- /subdominator/modules/subscraper/merklemap/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/merklemap/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/merklemap/merklemap.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import singlekeyloader 4 | merklemaps = [] 5 | 6 | async def merklemap(domain: str, session: httpx.AsyncClient, configs: str, username, args): 7 | try: 8 | if args.include_resources and "merklemap" not in args.include_resources and not args.all: 9 | return merklemaps 10 | if args.exclude_resources and "merklemap" in args.exclude_resources: 11 | return merklemaps 12 | 13 | randomkey = await singlekeyloader(configs,"merklemap") 14 | if randomkey is None: 15 | return merklemaps 16 | 17 | url = "https://api.merklemap.com/v1/search" 18 | headers = {"Authorization": f"Bearer {randomkey}"} 19 | params = {"query": f"*.{domain}", "page": 0, "type": "wildcard"} 20 | while True: 21 | response: httpx.Response = await session.request("GET",url, headers=headers, params=params, timeout=httpx.Timeout(connect=args.timeout, read=1000.0, write=None, pool=None)) 22 | 23 | if response.status_code != 200: 24 | return merklemaps 25 | 26 | data = response.json() 27 | results = data.get("results", []) 28 | if not results: 29 | break 30 | for result in results: 31 | hostname = result.get("hostname", "") 32 | common_name = result.get("subject_common_name", "") 33 | if hostname and hostname.endswith(f".{domain}"): 34 | merklemaps.append(hostname) 35 | if common_name and common_name.endswith(f".{domain}"): 36 | merklemaps.append(common_name) 37 | params["page"] += 1 38 | except httpx.TimeoutException: 39 | if args.show_timeout_info: 40 | logger("Timeout reached for Merklemap API", "warn", args.no_color) 41 | except Exception as e: 42 | if args.verbose: 43 | logger(f"Exception in Merklemap API module due to: {e}, {type(e)}", "warn", args.no_color) 44 | finally: 45 | if args.verbose: 46 | logger(f"Total subdomains found by Merklemap API: {len(merklemaps)}", "info", args.no_color) 47 | return merklemaps -------------------------------------------------------------------------------- /subdominator/modules/subscraper/myssl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/myssl/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/myssl/myssl.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents 4 | myssls = [] 5 | 6 | async def myssl(domain: str, session: httpx.AsyncClient, args): 7 | try: 8 | if args.include_resources and "myssl" not in args.include_resources and not args.all: 9 | return myssls 10 | if args.exclude_resources and "myssl" in args.exclude_resources: 11 | return myssls 12 | 13 | url = f"https://myssl.com/api/v1/discover_sub_domain?domain={domain}" 14 | headers = {"User-Agent": UserAgents()} 15 | response: httpx.Response = await session.get(url,headers=headers,timeout=args.timeout) 16 | 17 | if response.status_code != 200: 18 | return myssls 19 | 20 | data = response.json() 21 | for subdomain in data.get("data", []): 22 | sub = subdomain.get("domain") 23 | if sub and sub.endswith(f".{domain}"): 24 | myssls.append(sub) 25 | except httpx.TimeoutException: 26 | if args.show_timeout_info: 27 | logger("Timeout reached for MySSL API", "warn", args.no_color) 28 | except Exception as e: 29 | if args.verbose: 30 | logger(f"Exception occurred in MySSL API module due to: {e}, {type(e)}", "warn", args.no_color) 31 | finally: 32 | if args.verbose: 33 | logger(f"Total Subdomains found by MySSL API: {len(myssls)}", "info", args.no_color) 34 | return myssls -------------------------------------------------------------------------------- /subdominator/modules/subscraper/netlas/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/netlas/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/netlas/netlas.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import singlekeyloader 4 | netlass = [] 5 | 6 | async def netlas(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | start = 0 9 | page_size = 20 10 | if args.include_resources and "netlas" not in args.include_resources and not args.all: 11 | return netlass 12 | if args.exclude_resources and "netlas" in args.exclude_resources: 13 | return netlass 14 | 15 | randomkey = await singlekeyloader(configs, "netlas") 16 | if randomkey is None: 17 | return netlass 18 | 19 | headers = {"accept": "application/json", "X-API-Key": randomkey} 20 | while True: 21 | req_url = f"https://app.netlas.io/api/domains/?q=domain:*.{domain}+AND+NOT+domain:{domain}&source_type=include&start={start}" 22 | response: httpx.Response = await session.request( 23 | "GET", 24 | req_url, 25 | headers=headers, 26 | timeout=httpx.Timeout(timeout=args.timeout, connect=args.timeout, pool=None, write=None, read=120), 27 | ) 28 | if response.status_code != 200: 29 | if args.show_key_info: 30 | logger(f"Netlas API request failed. {username}, check API key usage: {randomkey}", "warn", args.no_color) 31 | return netlass 32 | data = response.json() 33 | items = data.get("items", []) 34 | if not items: 35 | break 36 | for item in items: 37 | netlass.append(item["data"]["domain"]) 38 | start += page_size 39 | except httpx.TimeoutException as e: 40 | if args.show_timeout_info: 41 | logger(f"Timeout reached for Netlas API: {e}", "warn", args.no_color) 42 | except Exception as e: 43 | if args.verbose: 44 | logger(f"Exception in Netlas API module: {e}, {type(e)}", "warn", args.no_color) 45 | finally: 46 | if args.verbose: 47 | logger(f"Total Subdomains found by Netlas API: {len(netlass)}", "info", args.no_color) 48 | return netlass -------------------------------------------------------------------------------- /subdominator/modules/subscraper/odin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/odin/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/odin/odin.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, singlekeyloader 4 | odin_results = [] 5 | 6 | async def odin(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | 9 | if args.include_resources and "odin" not in args.include_resources and not args.all: 10 | return odin_results 11 | 12 | if args.exclude_resources and "odin" in args.exclude_resources: 13 | return odin_results 14 | 15 | randomkey = await singlekeyloader(configs, "odin") 16 | if randomkey is None: 17 | return odin_results 18 | url = "https://api.odin.io/v1/domain/subdomain/search" 19 | headers = { 20 | "X-API-Key": randomkey, 21 | "Content-Type": "application/json", 22 | "User-Agent": UserAgents(), 23 | } 24 | limit = 1000 25 | start = None 26 | 27 | while True: 28 | payload = { 29 | "domain": domain, 30 | "limit": limit, 31 | "start": start if start else [] 32 | } 33 | response: httpx.Response = await session.request("POST", url, headers=headers, json=payload, timeout=args.timeout) 34 | if response.status_code != 200: 35 | if args.show_key_info: 36 | logger(f"Odin API request failed with status {response.status_code}. Check API key or rate limits.", "warn", args.no_color) 37 | return odin_results 38 | 39 | data = response.json() 40 | 41 | if not data.get("success"): 42 | return odin_results 43 | 44 | subdomains = data.get("data", []) 45 | odin_results.extend(subdomains) 46 | 47 | pagination = data.get("pagination", {}) 48 | start = pagination.get("last") 49 | if not start or len(subdomains) == 0: 50 | break 51 | except httpx.TimeoutException as e: 52 | if args.show_timeout_info: 53 | logger(f"Timeout reached for Odin API, due to: {e}", "warn", args.no_color) 54 | except Exception as e: 55 | if args.verbose: 56 | logger(f"Exception occurred in Odin API module: {e}, {type(e)}", "warn", args.no_color) 57 | finally: 58 | if args.verbose: 59 | logger(f"Total Subdomains found by Odin API: {len(odin_results)}", "info", args.no_color) 60 | return odin_results -------------------------------------------------------------------------------- /subdominator/modules/subscraper/quake/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/quake/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/quake/quake.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import singlekeyloader, UserAgents 4 | quakes = [] 5 | 6 | async def quake(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "quake" not in args.include_resources and not args.all: 9 | return quakes 10 | if args.exclude_resources and "quake" in args.exclude_resources: 11 | return quakes 12 | 13 | randomkey = await singlekeyloader(configs, "quake") 14 | if randomkey is None: 15 | return quakes 16 | 17 | url = "https://quake.360.net/api/v3/search/quake_service" 18 | headers = { 19 | "User-Agent": UserAgents(), 20 | "Accept": "*/*", 21 | "Accept-Language": "en", 22 | "Connection": "close", 23 | "Content-Type": "application/json", 24 | "X-Quaketoken": randomkey 25 | } 26 | data = { 27 | "query": f"domain: {domain}", 28 | "include": ["service.http.host"], 29 | "latest": True, 30 | "start": 0, 31 | "size": 500, 32 | } 33 | response: httpx.Response = await session.request("POST",url,headers=headers,json=data,timeout=args.timeout) 34 | if response.status_code != 200: 35 | if args.show_key_info: 36 | logger(f"Quake API blocking request. {username}, check API key usage: {randomkey}", "warn", args.no_color) 37 | return quakes 38 | result = response.json() 39 | for entry in result.get("data", []): 40 | subdomain = entry.get("service", {}).get("http", {}).get("host") 41 | if subdomain: 42 | quakes.append(subdomain) 43 | except httpx.TimeoutException: 44 | if args.show_timeout_info: 45 | logger("Timeout reached for Quake API", "warn", args.no_color) 46 | except Exception as e: 47 | if args.verbose: 48 | logger(f"Exception in Quake API module due to: {e}, {type(e)}", "warn", args.no_color) 49 | finally: 50 | if args.verbose: 51 | logger(f"Total Subdomains found by Quake API: {len(quakes)}", "info", args.no_color) 52 | return quakes -------------------------------------------------------------------------------- /subdominator/modules/subscraper/racent/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/racent/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/racent/racent.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents 4 | racents = [] 5 | 6 | async def racent(domain: str, session: httpx.AsyncClient, args): 7 | try: 8 | if args.include_resources and "racent" not in args.include_resources and not args.all: 9 | return racents 10 | if args.exclude_resources and "racent" in args.exclude_resources: 11 | return racents 12 | url = f"https://face.racent.com/tool/query_ctlog?keyword={domain}" 13 | headers = {"User-Agent": UserAgents()} 14 | response: httpx.Response = await session.get(url,headers=headers,timeout=args.timeout) 15 | if response.status_code != 200: 16 | return racents 17 | data = response.json() 18 | if "CTLog 查询超过限制" in data: 19 | return racents 20 | for subdomains in data.get['data']['list']: 21 | for subdomain in subdomains.get("dnsnames", []): 22 | racents.append(subdomain) 23 | except httpx.TimeoutException as e: 24 | if args.show_timeout_info: 25 | logger(f"Timeout reached for Racent API due to: {e}", "warn", args.no_color) 26 | except Exception as e: 27 | if args.verbose: 28 | logger(f"Exception at Racent API module due to: {e}, {type(e)}", "warn", args.no_color) 29 | finally: 30 | if args.verbose: 31 | logger(f"Total Subdomains found by Racent API: {len(racents)}", "info", args.no_color) 32 | return racents -------------------------------------------------------------------------------- /subdominator/modules/subscraper/rapidapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/rapidapi/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/rapidapi/rapidapi.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import singlekeyloader 4 | rapids = [] 5 | 6 | async def rapidapi(domain: str, session: httpx.AsyncClient, configs: str, args): 7 | try: 8 | if args.include_resources and "rapidapi" not in args.include_resources and not args.all: 9 | return rapids 10 | if args.exclude_resources and "rapidapi" in args.exclude_resources: 11 | return rapids 12 | 13 | rapidapi_key = await singlekeyloader(configs,"rapidapi") 14 | whoisxml_key = await singlekeyloader(configs,"whoisxmlapi") 15 | 16 | if not rapidapi_key or not whoisxml_key: 17 | return rapids 18 | 19 | url = "https://subdomains-lookup.p.rapidapi.com/api/v1" 20 | params = {"domainName": domain, "apiKey": whoisxml_key, "outputFormat": "JSON"} 21 | headers = { 22 | "X-RapidAPI-Key": rapidapi_key, 23 | "X-RapidAPI-Host": "subdomains-lookup.p.rapidapi.com" 24 | } 25 | 26 | response: httpx.Response = await session.request("GET",url,params=params,headers=headers,timeout=args.timeout) 27 | 28 | if response.status_code != 200: 29 | if args.show_key_info: 30 | logger(f"RapidAPI blocking request, check API usage for these keys: {whoisxml_key}, {rapidapi_key}", "warn", args.no_color) 31 | return rapids 32 | 33 | data = response.json() 34 | subdomains = data.get("result", {}).get("records", []) 35 | 36 | for sub in subdomains: 37 | subdomain = sub.get("domain") 38 | if subdomain: 39 | rapids.append(subdomain) 40 | except httpx.TimeoutException as e: 41 | if args.show_timeout_info: 42 | logger(f"Timeout reached for RapidAPI due to: {e}", "info", args.no_color) 43 | except Exception as e: 44 | if args.verbose: 45 | logger(f"Exception occurred in Rapid API module due to: {e}, type: {type(e)}", "warn", args.no_color) 46 | finally: 47 | if args.verbose: 48 | logger(f"Total Subdomains found by Rapid API: {len(rapids)}", "info", args.no_color) 49 | return rapids -------------------------------------------------------------------------------- /subdominator/modules/subscraper/rapiddns/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/rapiddns/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/rapiddns/rapiddns.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import re 3 | from subdominator.modules.logger.logger import logger 4 | from subdominator.modules.utils.utils import UserAgents 5 | rapiddnss = [] 6 | 7 | async def rapiddns(domain: str, session: httpx.AsyncClient, args): 8 | try: 9 | if args.include_resources and "rapiddns" not in args.include_resources and not args.all: 10 | return rapiddnss 11 | if args.exclude_resources and "rapiddns" in args.exclude_resources: 12 | return rapiddnss 13 | for pagenum in range(1, 8): 14 | url = f"https://rapiddns.io/subdomain/{domain}?page={pagenum}" 15 | headers = {"User-Agent": UserAgents()} 16 | response: httpx.Response = await session.request("GET",url,headers=headers,timeout=args.timeout) 17 | data = response.text 18 | filterdomain = re.escape(domain) 19 | pattern = rf'(?i)(?:https?://)?([a-zA-Z0-9*_.-]+\.{filterdomain})' 20 | subdomains = re.findall(pattern, data) 21 | rapiddnss.extend(subdomains) 22 | if "Next" not in data: 23 | return rapiddnss 24 | except httpx.TimeoutException as e: 25 | if args.show_timeout_info: 26 | logger(f"Timeout reached for RapidDNS due to: {e}", "warn", args.no_color) 27 | except Exception as e: 28 | if args.verbose: 29 | logger(f"Exception occurred in RapidDNS module due to: {e}, type: {type(e)}", "warn", args.no_color) 30 | finally: 31 | if args.verbose: 32 | logger(f"Total Subdomains found by RapidDNS: {len(rapiddnss)}", "info", args.no_color) 33 | return rapiddnss -------------------------------------------------------------------------------- /subdominator/modules/subscraper/rapidfinder/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/rapidfinder/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/rapidfinder/rapidfinder.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, singlekeyloader 4 | 5 | rapidfinders = [] 6 | 7 | async def rapidfinder(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 8 | try: 9 | if args.include_resources and "rapidfinder" not in args.include_resources and not args.all: 10 | return rapidfinders 11 | 12 | if args.exclude_resources and "rapidfinder" in args.exclude_resources: 13 | return rapidfinders 14 | 15 | randomkey = await singlekeyloader(configs, "rapidapi") 16 | if randomkey is None: 17 | return rapidfinders 18 | 19 | url = "https://subdomain-finder3.p.rapidapi.com/v1/subdomain-finder/" 20 | params = {"domain": domain} 21 | headers = { 22 | "User-Agent": UserAgents(), 23 | "X-RapidAPI-Key": randomkey, 24 | "X-RapidAPI-Host": "subdomain-finder3.p.rapidapi.com" 25 | } 26 | 27 | response: httpx.Response = await session.get(url, headers=headers, timeout=args.timeout, params=params) 28 | if response.status_code == 403: 29 | if args.show_key_info: 30 | logger(f"Rapidfinder blocking our request, {username} please check that you subscribed to the Rapidfinder API service: {randomkey}", "warn", args.no_color) 31 | return rapidfinders 32 | 33 | if response.status_code != 200: 34 | if args.show_key_info: 35 | logger(f"Rapidfinder blocking our request, {username} please check your API usage for this key: {randomkey}", "warn", args.no_color) 36 | return rapidfinders 37 | 38 | data = response.json() 39 | for item in data["subdomains"]: 40 | rapidfinders.append(item["subdomain"]) 41 | except httpx.TimeoutException as e: 42 | if args.show_timeout_info: 43 | logger(f"Timeout reached for Rapidfinder API, due to: {e}", "warn", args.no_color) 44 | except Exception as e: 45 | if args.verbose: 46 | logger(f"Exception error occurred in Rapidfinder API module due to: {e}, {type(e)}", "warn", args.no_color) 47 | finally: 48 | if args.verbose: 49 | logger(f"Total Subdomains found by Rapidfinder API: {len(rapidfinders)}", "info", args.no_color) 50 | return rapidfinders 51 | -------------------------------------------------------------------------------- /subdominator/modules/subscraper/rapidscan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/rapidscan/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/rapidscan/rapidscan.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, singlekeyloader 4 | rapidscans = [] 5 | 6 | async def rapidscan(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "rapidscan" not in args.include_resources and not args.all: 9 | return rapidscans 10 | 11 | if args.exclude_resources and "rapidscan" in args.exclude_resources: 12 | return rapidscans 13 | 14 | randomkey = await singlekeyloader(configs, "rapidapi") 15 | if randomkey is None: 16 | return rapidscans 17 | 18 | url = "https://subdomain-scan1.p.rapidapi.com/" 19 | params = {"domain": domain} 20 | headers = { 21 | "User-Agent": UserAgents(), 22 | "X-RapidAPI-Key": randomkey, 23 | "X-RapidAPI-Host": "subdomain-scan1.p.rapidapi.com" 24 | } 25 | 26 | response: httpx.Response = await session.get(url, headers=headers, timeout=args.timeout, params=params) 27 | 28 | if response.status_code == 403: 29 | if args.show_key_info: 30 | logger(f"Rapidscan blocking our request, {username} please check that you subscribed to the Rapidscan API service: {randomkey}", "warn", args.no_color) 31 | return rapidscans 32 | 33 | if response.status_code != 200: 34 | if args.show_key_info: 35 | logger(f"Rapidscan blocking our request, {username} please check your API usage for this key: {randomkey}", "warn", args.no_color) 36 | return rapidscans 37 | 38 | data = response.json() 39 | for subdomain in data: 40 | rapidscans.append(subdomain) 41 | except httpx.TimeoutException as e: 42 | if args.show_timeout_info: 43 | logger(f"Timeout reached for Rapidscan API, due to: {e}", "warn", args.no_color) 44 | except Exception as e: 45 | if args.verbose: 46 | logger(f"Exception error occurred in Rapidscan API module due to: {e}, {type(e)}", "warn", args.no_color) 47 | finally: 48 | if args.verbose: 49 | logger(f"Total Subdomains found by Rapidscan API: {len(rapidscans)}", "info", args.no_color) 50 | return rapidscans -------------------------------------------------------------------------------- /subdominator/modules/subscraper/redhuntlabs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/redhuntlabs/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/redhuntlabs/redhuntlabs.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, dualkeyloader 4 | redhuntlabs_subdomains = [] 5 | 6 | async def redhuntapi(url: str, domain: str, session: httpx.AsyncClient, randkey: str, pagenum: int, pagesize: int, args): 7 | try: 8 | base_url = f"{url}?domain={domain}&page_size={pagesize}&page={pagenum}" 9 | headers = {"User-Agent": UserAgents(), "X-BLOBR-KEY": randkey} 10 | response: httpx.Response = await session.get(base_url, headers=headers, timeout=args.timeout) 11 | if response.status_code != 200: 12 | return [] 13 | data = response.json() 14 | return data.get("subdomains", []) 15 | except httpx.TimeoutException: 16 | if args.show_timeout_info: 17 | logger("Timeout reached for RedHuntLabs API", "warn", args.no_color) 18 | except Exception as e: 19 | if args.verbose: 20 | logger(f"Exception in RedHuntLabs API request: {e}, {type(e)}", "warn", args.no_color) 21 | return [] 22 | 23 | 24 | async def redhuntlabs(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 25 | try: 26 | if args.include_resources and "redhuntlabs" not in args.include_resources and not args.all: 27 | return redhuntlabs_subdomains 28 | 29 | if args.exclude_resources and "redhuntlabs" in args.exclude_resources: 30 | return redhuntlabs_subdomains 31 | 32 | url,randkeys = await dualkeyloader(configs, "redhuntlabs", True) 33 | if url is None or randkeys is None: 34 | return redhuntlabs_subdomains 35 | pagenum = 1 36 | pagesize = 1000 37 | while True: 38 | subdomains = await redhuntapi(url, domain, session, randkeys, pagenum, pagesize, args) 39 | if not subdomains: 40 | break 41 | redhuntlabs_subdomains.extend(subdomains) 42 | pagenum += 1 43 | except Exception as e: 44 | if args.verbose: 45 | logger(f"Exception in RedHuntLabs API module due to: {e}, {type(e)}", "warn", args.no_color) 46 | finally: 47 | if args.verbose: 48 | logger(f"Total Subdomains found by RedHuntLabs API: {len(redhuntlabs_subdomains)}", "info", args.no_color) 49 | return redhuntlabs_subdomains -------------------------------------------------------------------------------- /subdominator/modules/subscraper/rsecloud/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/rsecloud/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/rsecloud/rsecloud.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, singlekeyloader 4 | rsecloud_subdomains = [] 5 | 6 | async def rsecloudapi(url: str, domain: str, session: httpx.AsyncClient, randkey: str, page: int, args): 7 | try: 8 | json_payload = {"domain": domain} 9 | headers = {"User-Agent": UserAgents(), "Content-Type": "application/json", "X-API-Key": randkey} 10 | url = f"{url}?page={page}" 11 | response: httpx.Response = await session.post(url, headers=headers, json=json_payload, timeout=args.timeout) 12 | if response.status_code != 200: 13 | if args.show_key_info: 14 | logger(f"RseCloud blocking request, check API usage for key: {randkey}", "warn", args.no_color) 15 | return [] 16 | data = response.json() 17 | if "error" in data: 18 | return [],1 19 | return data.get("data", []), data.get("total_pages", 1) 20 | except httpx.TimeoutException as e: 21 | if args.show_timeout_info: 22 | logger(f"Timeout reached for RseCloud API request due to: {e}", "warn", args.no_color) 23 | except Exception as e: 24 | if args.verbose: 25 | logger(f"Exception in RseCloud API request due to: {e}, {type(e)}", "warn", args.no_color) 26 | return [], 1 27 | 28 | async def rsecloud(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 29 | try: 30 | if args.include_resources and "rsecloud" not in args.include_resources and not args.all: 31 | return rsecloud_subdomains 32 | 33 | if args.exclude_resources and "rsecloud" in args.exclude_resources: 34 | return rsecloud_subdomains 35 | 36 | randomkey = await singlekeyloader(configs, "rsecloud") 37 | if randomkey is None: 38 | return rsecloud_subdomains 39 | 40 | url = "https://api.rsecloud.com/api/v1/subdomains" 41 | page = 1 42 | while True: 43 | subdomains, total_pages = await rsecloudapi(url, domain, session, randomkey, page, args) 44 | if not subdomains: 45 | break 46 | rsecloud_subdomains.extend(subdomains) 47 | if page >= total_pages: 48 | break 49 | page += 1 50 | except Exception as e: 51 | if args.verbose: 52 | logger(f"Exception in RseCloud API module: {e}, {type(e)}", "warn", args.no_color) 53 | finally: 54 | if args.verbose: 55 | logger(f"Total Subdomains found by RseCloud API: {len(rsecloud_subdomains)}", "info", args.no_color) 56 | return rsecloud_subdomains -------------------------------------------------------------------------------- /subdominator/modules/subscraper/securitytrails/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/securitytrails/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/securitytrails/securitytrails.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, singlekeyloader 4 | Securitytrails = [] 5 | 6 | async def securitytrailsapi(url: str, domain: str, session: httpx.AsyncClient, randkey: str, args): 7 | try: 8 | headers = {"User-Agent": UserAgents(), "accept": "application/json", "APIKEY": randkey} 9 | response: httpx.Response = await session.get(url, headers=headers, timeout=args.timeout) 10 | if response.status_code != 200: 11 | if args.show_key_info: 12 | logger(f"SecurityTrails blocking request, check API usage for key: {randkey}", "warn", args.no_color) 13 | return [] 14 | data = response.json() 15 | return [f"{sub}.{domain}" for sub in data.get("subdomains", [])] 16 | except httpx.TimeoutException as e: 17 | if args.show_timeout_info: 18 | logger(f"Timeout reached for SecurityTrails API due to: {e}", "warn", args.no_color) 19 | except Exception as e: 20 | if args.verbose: 21 | logger(f"Exception in SecurityTrails API request module due to: {e}, {type(e)}", "warn", args.no_color) 22 | return [] 23 | 24 | async def securitytrails(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 25 | try: 26 | if args.include_resources and "securitytrails" not in args.include_resources and not args.all: 27 | return Securitytrails 28 | 29 | if args.exclude_resources and "securitytrails" in args.exclude_resources: 30 | return Securitytrails 31 | 32 | randomkey = await singlekeyloader(configs, "securitytrails") 33 | if randomkey is None: 34 | return Securitytrails 35 | 36 | url = f"https://api.securitytrails.com/v1/domain/{domain}/subdomains?children_only=false&include_inactive=true" 37 | subdomains = await securitytrailsapi(url, domain, session, randomkey, args) 38 | Securitytrails.extend(subdomains) 39 | except Exception as e: 40 | if args.verbose: 41 | logger(f"Exception in SecurityTrails API module: {e}, {type(e)}", "warn", args.no_color) 42 | finally: 43 | if args.verbose: 44 | logger(f"Total Subdomains found by SecurityTrails API: {len(Securitytrails)}", "info", args.no_color) 45 | return Securitytrails -------------------------------------------------------------------------------- /subdominator/modules/subscraper/shodan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/shodan/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/shodan/shodan.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, singlekeyloader 4 | Shodans = [] 5 | 6 | async def shodanapi(url: str, domain: str, session: httpx.AsyncClient, randkey: str, args): 7 | try: 8 | headers = {"User-Agent": UserAgents()} 9 | response: httpx.Response = await session.get(url, headers=headers, timeout=args.timeout) 10 | if response.status_code != 200: 11 | if args.show_key_info: 12 | logger(f"Shodan blocking request, check API usage for key: {randkey}", "warn", args.no_color) 13 | return [] 14 | data = response.json() 15 | return [f"{sub}.{domain}" for sub in data.get("subdomains", [])] 16 | except httpx.TimeoutException as e: 17 | if args.show_timeout_info: 18 | logger(f"Timeout reached for Shodan API due to: {e}", "warn", args.no_color) 19 | except httpx.RequestError as e: 20 | if args.show_timeout_info: 21 | logger(f"Request error in Shodan API: {e}", "warn", args.no_color) 22 | except Exception as e: 23 | if args.verbose: 24 | logger(f"Exception in Shodan API request module due to: {e}, {type(e)}", "warn", args.no_color) 25 | return [] 26 | 27 | 28 | async def shodan(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 29 | try: 30 | if args.include_resources and "shodan" not in args.include_resources and not args.all: 31 | return Shodans 32 | 33 | if args.exclude_resources and "shodan" in args.exclude_resources: 34 | return Shodans 35 | 36 | randomkey = await singlekeyloader(configs, "shodan") 37 | if randomkey is None: 38 | return Shodans 39 | url = f"https://api.shodan.io/dns/domain/{domain}?key={randomkey}" 40 | subdomains = await shodanapi(url, domain, session, randomkey, args) 41 | Shodans.extend(subdomains) 42 | except Exception as e: 43 | if args.verbose: 44 | logger(f"Exception in Shodan API module: {e}, {type(e)}", "warn", args.no_color) 45 | finally: 46 | if args.verbose: 47 | logger(f"Total Subdomains found by Shodan API: {len(Shodans)}", "info", args.no_color) 48 | return Shodans -------------------------------------------------------------------------------- /subdominator/modules/subscraper/shodanx/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/shodanx/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/shodanx/shodanx.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import warnings 3 | from bs4 import BeautifulSoup, XMLParsedAsHTMLWarning, MarkupResemblesLocatorWarning 4 | from subdominator.modules.logger.logger import logger 5 | from subdominator.modules.utils.utils import UserAgents, check_subdomain 6 | Shodanxs = [] 7 | 8 | async def shodanx(domain: str, session: httpx.AsyncClient, args): 9 | try: 10 | if args.include_resources and "shodanx" not in args.include_resources and not args.all: 11 | return Shodanxs 12 | 13 | if args.exclude_resources and "shodanx" in args.exclude_resources: 14 | return Shodanxs 15 | 16 | parsed_domain = check_subdomain(domain) 17 | if parsed_domain.subdomain: 18 | return Shodanxs 19 | 20 | url = f"https://www.shodan.io/domain/{domain}" 21 | headers = {"User-Agent": UserAgents()} 22 | 23 | response = await session.get(url,headers=headers,timeout=args.timeout) 24 | if response.status_code != 200: 25 | return Shodanxs 26 | data = response.text 27 | with warnings.catch_warnings(): 28 | warnings.filterwarnings("ignore", category=UserWarning) 29 | warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning) 30 | warnings.filterwarnings("ignore", category=MarkupResemblesLocatorWarning) 31 | 32 | soup = BeautifulSoup(data, "lxml") 33 | ul = soup.find("ul", id="subdomains") 34 | if not ul: 35 | return Shodanxs 36 | subdomains = ul.find_all("li") 37 | for result in subdomains: 38 | subdomain = f"{result.text.strip()}.{domain}" 39 | Shodanxs.append(subdomain) 40 | except httpx.TimeoutException: 41 | if args.show_timeout_info: 42 | logger("Timeout reached for ShodanX", "warn", args.no_color) 43 | except httpx.RequestError as e: 44 | if args.show_timeout_info: 45 | logger(f"Request error in ShodanX: {e}", "warn", args.no_color) 46 | except Exception as e: 47 | if args.verbose: 48 | logger(f"Exception in ShodanX request block: {e}, {type(e)}", "warn", args.no_color) 49 | finally: 50 | if args.verbose: 51 | logger(f"Total Subdomains found by ShodanX: {len(Shodanxs)}", "info", args.no_color) 52 | return Shodanxs 53 | -------------------------------------------------------------------------------- /subdominator/modules/subscraper/shrewdeye/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/shrewdeye/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/shrewdeye/shrewdeye.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents 4 | Shrewdeyes = [] 5 | 6 | async def shrewdeye(domain: str, session: httpx.AsyncClient, args): 7 | try: 8 | if args.include_resources and "shrewdeye" not in args.include_resources and not args.all: 9 | return Shrewdeyes 10 | 11 | if args.exclude_resources and "shrewdeye" in args.exclude_resources: 12 | return Shrewdeyes 13 | 14 | url = f"https://shrewdeye.app/domains/{domain}.txt" 15 | headers = {"User-Agent":UserAgents()} 16 | response = await session.request("GET",url,timeout=args.timeout,headers=headers) 17 | if response.status_code != 200: 18 | return Shrewdeyes 19 | data = response.text.strip() 20 | if not data: 21 | return Shrewdeyes 22 | subdomains = data.split("\n") 23 | Shrewdeyes.extend(subdomains) 24 | except httpx.TimeoutException as e: 25 | if args.show_timeout_info: 26 | logger(f"Timeout reached for Shrewdeye API due to: {e}", "warn", args.no_color) 27 | except Exception as e: 28 | if args.verbose: 29 | logger(f"Exception occurred in Shrewdeye API module due to: {e}, {type(e)}", "warn", args.no_color) 30 | finally: 31 | if args.verbose: 32 | logger(f"Total Subdomains found by Shrewdeye: {len(Shrewdeyes)}", "info", args.no_color) 33 | return Shrewdeyes -------------------------------------------------------------------------------- /subdominator/modules/subscraper/shrewdeye/zoomeyeapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/shrewdeye/zoomeyeapi/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/shrewdeye/zoomeyeapi/zoomeyeapi.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, dualkeyloader 4 | zoomeyes = [] 5 | 6 | async def zoomeyeapi(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "zoomeye" not in args.include_resources and not args.all: 9 | return zoomeyes 10 | 11 | if args.exclude_resources and "zoomeye" in args.exclude_resources: 12 | return zoomeyes 13 | 14 | host,randomkey = await dualkeyloader(configs, "zoomeyeapi") 15 | if randomkey is None: 16 | return zoomeyes 17 | 18 | base_url = f"https://{host}/domain/search" 19 | page = 1 20 | 21 | while True: 22 | url = f"{base_url}?q={domain}&type=1&s=1000&page={page}" 23 | headers = { 24 | "API-KEY": randomkey, 25 | "User-Agent": UserAgents(), 26 | "Accept": "application/json", 27 | "Content-Type": "application/json", 28 | } 29 | response: httpx.Response = await session.request("GET", url, headers=headers, timeout=args.timeout) 30 | if response.status_code != 200: 31 | if args.show_key_info: 32 | logger(f"Zoomeye API blocking our request, {username} please check your API usage for this key: {randomkey}", "warn", args.no_color) 33 | return zoomeyes 34 | 35 | data = response.json() 36 | if "list" not in data: 37 | return zoomeyes 38 | subdomains = [item["name"] for item in data["list"] if "name" in item] 39 | zoomeyes.extend(subdomains) 40 | page += 1 41 | except httpx.TimeoutException as e: 42 | if args.show_timeout_info: 43 | logger(f"Timeout reached for Zoomeye API, due to: {e}", "warn", args.no_color) 44 | except Exception as e: 45 | if args.verbose: 46 | logger(f"Exception error occurred in Zoomeye API module due to: {e}, {type(e)}", "warn", args.no_color) 47 | finally: 48 | if args.verbose: 49 | logger(f"Total Subdomains found by Zoomeye API: {len(zoomeyes)}", "info", args.no_color) 50 | return zoomeyes -------------------------------------------------------------------------------- /subdominator/modules/subscraper/sitedossier/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/sitedossier/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/sitedossier/sitedossier.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import re 3 | from subdominator.modules.logger.logger import logger 4 | from subdominator.modules.utils.utils import UserAgents 5 | Sitedossiers = [] 6 | 7 | async def sitedossier(domain: str, session: httpx.AsyncClient, args): 8 | try: 9 | if args.include_resources and "sitedossier" not in args.include_resources and not args.all: 10 | return Sitedossiers 11 | 12 | if args.exclude_resources and "sitedossier" in args.exclude_resources: 13 | return Sitedossiers 14 | 15 | page = 1 16 | while True: 17 | url = f"http://www.sitedossier.com/parentdomain/{domain}/{page}" 18 | headers = {"User-Agent": UserAgents()} 19 | response = await session.request("GET",url,timeout=args.timeout, headers=headers) 20 | if response.status_code != 200: 21 | return Sitedossiers 22 | data = response.text 23 | filterdomain = re.escape(domain) 24 | pattern = rf'(?i)(?:https?://)?([a-zA-Z0-9*_.-]+\.{filterdomain})' 25 | subdomains = re.findall(pattern, data) 26 | Sitedossiers.extend(subdomains) 27 | if "Show next 100 items" not in data: 28 | return Sitedossiers 29 | page += 100 30 | except httpx.TimeoutException as e: 31 | if args.show_timeout_info: 32 | logger(f"Timeout reached for Sitedossier due to: {e}", "warn", args.no_color) 33 | except Exception as e: 34 | if args.verbose: 35 | logger(f"Exception in Sitedossier module due to: {e}, {type(e)}", "warn", args.no_color) 36 | finally: 37 | if args.verbose: 38 | logger(f"Total Subdomains found by Sitedossier: {len(Sitedossiers)}", "info", args.no_color) 39 | return Sitedossiers -------------------------------------------------------------------------------- /subdominator/modules/subscraper/threatcrowd/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/threatcrowd/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/threatcrowd/threatcrowd.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents 4 | threatcrowds = [] 5 | 6 | async def threatcrowd(domain: str, session: httpx.AsyncClient, args): 7 | try: 8 | if args.include_resources and "threatcrowd" not in args.include_resources and not args.all: 9 | return threatcrowds 10 | 11 | if args.exclude_resources and "threatcrowd" in args.exclude_resources: 12 | return threatcrowds 13 | 14 | headers = {"User-Agent": UserAgents()} 15 | url = f"http://ci-www.threatcrowd.org/searchApi/v2/domain/report/?domain={domain}" 16 | response: httpx.Response = await session.get(url, timeout=args.timeout, headers=headers) 17 | if response.status_code != 200: 18 | return threatcrowds 19 | 20 | data = response.json() 21 | if "subdomains" in data: 22 | for subdomain in data["subdomains"]: 23 | if subdomain.endswith(f".{domain}"): 24 | threatcrowds.append(subdomain) 25 | except httpx.TimeoutException as e: 26 | if args.show_timeout_info: 27 | logger(f"Timeout reached for ThreatCrowd API, due to: {e}", "warn", args.no_color) 28 | except Exception as e: 29 | if args.verbose: 30 | logger(f"Exception error occurred in ThreatCrowd API module due to: {e}, {type(e)}", "warn", args.no_color) 31 | finally: 32 | if args.verbose: 33 | logger(f"Total Subdomains found by ThreatCrowd API: {len(threatcrowds)}", "info", args.no_color) 34 | return threatcrowds -------------------------------------------------------------------------------- /subdominator/modules/subscraper/trickest/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/trickest/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/trickest/trickest.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import httpx 3 | from subdominator.modules.logger.logger import logger 4 | from subdominator.modules.utils.utils import singlekeyloader, UserAgents 5 | sem = asyncio.Semaphore(50) 6 | Trickest = set() 7 | 8 | 9 | async def get_count(session, offset, domain, args): 10 | try: 11 | url = f"https://api.trickest.io/solutions/v1/public/solution/a7cba1f1-df07-4a5c-876a-953f178996be/view?q=hostname ~ '.{domain}'&dataset_id=a0a49ca9-03bb-45e0-aa9a-ad59082ebdfc&limit=50&offset={offset}&select=hostname&orderby=hostname" 12 | response = await session.get(url, timeout=args.timeout) 13 | if response.status_code == 200: 14 | data = response.json() 15 | return data.get("total_count", 0) 16 | except httpx.TimeoutException: 17 | if args.show_timeout_info: 18 | logger("Timeout reached for Trickest API while fetching count.", "warn", args.no_color) 19 | except Exception as e: 20 | if args.verbose: 21 | logger(f"Exception in Trickest get_count: {e}, {type(e)}", "warn", args.no_color) 22 | 23 | async def fetcher(session, offset, domain, args): 24 | try: 25 | async with sem: 26 | url = f"https://api.trickest.io/solutions/v1/public/solution/a7cba1f1-df07-4a5c-876a-953f178996be/view?q=hostname ~ '.{domain}'&dataset_id=a0a49ca9-03bb-45e0-aa9a-ad59082ebdfc&limit=50&offset={offset}&select=hostname&orderby=hostname" 27 | response = await session.get(url, timeout=httpx.Timeout(read=300.0, connect=args.timeout)) 28 | if response.status_code == 200: 29 | data = response.json() 30 | for result in data.get("results", []): 31 | subdomain = result["hostname"] 32 | if subdomain.endswith(f".{domain}"): 33 | Trickest.add(subdomain) 34 | except httpx.TimeoutException: 35 | if args.show_timeout_info: 36 | logger("Timeout reached for Trickest API while fetching data.", "warn", args.no_color) 37 | except Exception as e: 38 | if args.verbose: 39 | logger(f"Exception in Trickest fetcher: {e}, {type(e)}", "warn", args.no_color) 40 | 41 | async def trickest(domain: str, configs: str,args): 42 | try: 43 | if not (args.all or (args.include_resources and "trickest" in args.include_resources)): 44 | return Trickest 45 | 46 | randomapikey = await singlekeyloader(configs, "trickest") 47 | if not randomapikey: 48 | return Trickest 49 | 50 | headers = { 51 | "User-Agent": UserAgents(), 52 | "Authorization": f"Token {randomapikey}" 53 | } 54 | tasks = [] 55 | async with httpx.AsyncClient(verify=False, proxy=args.proxy, headers=headers) as session: 56 | total = await get_count(session, 10, domain, args) 57 | if not total or total == 0: 58 | return Trickest 59 | offset = 10 60 | for _ in range(0, int(total / 10) + 1): 61 | tasks.append(fetcher(session, offset, domain, args)) 62 | offset += 10 63 | await asyncio.gather(*tasks, return_exceptions=False) 64 | except httpx.TimeoutException: 65 | if args.show_timeout_info: 66 | logger("Timeout reached while connecting to Trickest API.", "warn", args.no_color) 67 | except Exception as e: 68 | if args.verbose: 69 | logger(f"Exception in Trickest main function: {e}, {type(e)}", "warn", args.no_color) 70 | finally: 71 | if args.verbose: 72 | logger(f"Total subdomains found by Trickest: {len(Trickest)}", "info", args.no_color) 73 | return Trickest -------------------------------------------------------------------------------- /subdominator/modules/subscraper/urlscan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/urlscan/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/urlscan/urlscan.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents 4 | urlscans = [] 5 | 6 | async def urlscan(domain: str, session: httpx.AsyncClient, args): 7 | try: 8 | if args.include_resources and "urlscan" not in args.include_resources and not args.all: 9 | return urlscans 10 | 11 | if args.exclude_resources and "urlscan" in args.exclude_resources: 12 | return urlscans 13 | headers = {"User-Agent": UserAgents()} 14 | url = f"https://urlscan.io/api/v1/search/?q=page.domain:{domain}&size=10000" 15 | response: httpx.Response = await session.request("GET", url, timeout=args.timeout, headers=headers) 16 | if response.status_code != 200: 17 | return [] 18 | data = response.json() 19 | for entry in data.get("results", []): 20 | subdomain = entry["page"]["domain"] 21 | urlscans.append(subdomain) 22 | return urlscans 23 | except httpx.TimeoutException as e: 24 | if args.show_timeout_info: 25 | logger(f"Timeout reached for Urlscan due to: {e}", "warn", args.no_color) 26 | except Exception as e: 27 | if args.verbose: 28 | logger(f"Exception in Urlscan module due to: {e}, {type(e)}", "warn", args.no_color) 29 | finally: 30 | if args.verbose: 31 | logger(f"Total Subdomains found by Urlscan: {len(urlscans)}", "info", args.no_color) 32 | return urlscans 33 | -------------------------------------------------------------------------------- /subdominator/modules/subscraper/virustotal/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/virustotal/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/virustotal/virustotal.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, singlekeyloader 4 | virustotal_results = [] 5 | 6 | async def virustotal(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "virustotal" not in args.include_resources and not args.all: 9 | return virustotal_results 10 | 11 | if args.exclude_resources and "virustotal" in args.exclude_resources: 12 | return virustotal_results 13 | 14 | randomkey = await singlekeyloader(configs, "virustotal") 15 | 16 | if randomkey is None: 17 | return virustotal_results 18 | 19 | cursor = None 20 | while True: 21 | url = f"https://www.virustotal.com/api/v3/domains/{domain}/subdomains?limit=40" 22 | if cursor: 23 | url = f"{url}&cursor={cursor}" 24 | 25 | headers = { 26 | 'User-Agent': UserAgents(), 27 | 'x-apikey': randomkey 28 | } 29 | response: httpx.Response = await session.request("GET", url, headers=headers, timeout=args.timeout) 30 | if response.status_code != 200: 31 | if args.show_key_info: 32 | logger(f"VirusTotal blocking our request, {username} please check your API usage for this key: {randomkey}", "warn", args.no_color) 33 | return virustotal_results 34 | 35 | data = response.json() 36 | subdomains = [item["id"] for item in data.get("data", [])] 37 | virustotal_results.extend(subdomains) 38 | cursor = data.get("meta", {}).get("cursor", "") 39 | if not cursor: 40 | break 41 | except httpx.TimeoutException as e: 42 | if args.show_timeout_info: 43 | logger(f"Timeout reached for VirusTotal API, due to: {e}", "warn", args.no_color) 44 | except Exception as e: 45 | if args.verbose: 46 | logger(f"Exception error occurred in VirusTotal API module due to: {e}, {type(e)}", "warn", args.no_color) 47 | finally: 48 | if args.verbose: 49 | logger(f"Total Subdomains found by VirusTotal API: {len(virustotal_results)}", "info", args.no_color) 50 | return virustotal_results 51 | -------------------------------------------------------------------------------- /subdominator/modules/subscraper/waybackarchive/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/waybackarchive/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/waybackarchive/waybackarchive.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.utils.utils import extracts, UserAgents 3 | from subdominator.modules.logger.logger import logger 4 | Waybackurls = set() 5 | 6 | async def waybackarchive(domain, args): 7 | try: 8 | if not (args.all or (args.include_resources and "waybackarchive" in args.include_resources)): 9 | return Waybackurls 10 | headers = { 11 | "User-Agent": UserAgents() 12 | } 13 | async with httpx.AsyncClient(verify=False, proxy=args.proxy) as request: 14 | async with request.stream( 15 | "GET", f"https://web.archive.org/cdx/search/cdx?url=*.{domain}&collapse=urlkey&fl=original", 16 | headers=headers, 17 | timeout=httpx.Timeout(read=300.0, connect=args.timeout, write=None, pool=None), 18 | follow_redirects=True 19 | ) as response: 20 | async for url in response.aiter_lines(): 21 | subdomains = await extracts(url, domain) 22 | if subdomains: 23 | for subdomain in subdomains: 24 | subdomain = subdomain.lstrip("25").lstrip("2F").lstrip("40").lstrip(".").lstrip("B0") 25 | if subdomain not in Waybackurls and not subdomain.startswith("%3D") and not subdomain.startswith("3D"): 26 | Waybackurls.add(subdomain) 27 | except httpx.TimeoutException as e: 28 | if args.show_timeout_info: 29 | logger(f"Timeout reached for Webarchive due to: {e}", "warn", args.no_color) 30 | except Exception as e: 31 | if args.verbose: 32 | logger(f"Exception in Waybackarchive module due to: {e}, {type(e)}", "warn", args.no_color) 33 | finally: 34 | if args.verbose: 35 | logger(f"Total Subdomains found by Waybackarchive: {len(Waybackurls)}", "info", args.no_color) 36 | return Waybackurls -------------------------------------------------------------------------------- /subdominator/modules/subscraper/whoisxml/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/whoisxml/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/whoisxml/whoisxml.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, singlekeyloader 4 | 5 | whoisxml_results = [] 6 | 7 | async def whoisxml(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 8 | try: 9 | if args.include_resources and "whoisxml" not in args.include_resources and not args.all: 10 | return whoisxml_results 11 | 12 | if args.exclude_resources and "whoisxml" in args.exclude_resources: 13 | return whoisxml_results 14 | 15 | randomkey = await singlekeyloader(configs, "whoisxmlapi") 16 | 17 | if randomkey is None: 18 | return whoisxml_results 19 | url = f"https://subdomains.whoisxmlapi.com/api/v1?apiKey={randomkey}&domainName={domain}" 20 | headers = {'User-Agent': UserAgents()} 21 | response: httpx.Response = await session.request("GET", url, headers=headers, timeout=args.timeout) 22 | if response.status_code != 200: 23 | if args.show_key_info: 24 | logger(f"WhoisXML API blocking our request, {username} please check your API usage for this key: {randomkey}", "warn", args.no_color) 25 | return whoisxml_results 26 | data = response.json() 27 | subdomains = [record["domain"] for record in data.get("result", {}).get("records", []) if "domain" in record] 28 | whoisxml_results.extend(subdomains) 29 | except httpx.TimeoutException as e: 30 | if args.show_timeout_info: 31 | logger(f"Timeout reached for WhoisXML API, due to: {e}", "warn", args.no_color) 32 | except Exception as e: 33 | if args.verbose: 34 | logger(f"Exception error occurred in WhoisXML API module due to: {e}, {type(e)}", "warn", args.no_color) 35 | finally: 36 | if args.verbose: 37 | logger(f"Total Subdomains found by WhoisXML API: {len(whoisxml_results)}", "info", args.no_color) 38 | return whoisxml_results -------------------------------------------------------------------------------- /subdominator/modules/subscraper/zoomeyeapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/subscraper/zoomeyeapi/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/subscraper/zoomeyeapi/zoomeyeapi.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from subdominator.modules.logger.logger import logger 3 | from subdominator.modules.utils.utils import UserAgents, dualkeyloader 4 | zoomeyes = [] 5 | 6 | async def zoomeyeapi(domain: str, session: httpx.AsyncClient, configs: str, username: str, args): 7 | try: 8 | if args.include_resources and "zoomeye" not in args.include_resources and not args.all: 9 | return zoomeyes 10 | 11 | if args.exclude_resources and "zoomeye" in args.exclude_resources: 12 | return zoomeyes 13 | 14 | host,randomkey = await dualkeyloader(configs, "zoomeyeapi") 15 | if randomkey is None: 16 | return zoomeyes 17 | 18 | base_url = f"https://{host}/domain/search" 19 | page = 1 20 | 21 | while True: 22 | url = f"{base_url}?q={domain}&type=1&s=1000&page={page}" 23 | headers = { 24 | "API-KEY": randomkey, 25 | "User-Agent": UserAgents(), 26 | "Accept": "application/json", 27 | "Content-Type": "application/json", 28 | } 29 | response: httpx.Response = await session.request("GET", url, headers=headers, timeout=args.timeout) 30 | if response.status_code != 200: 31 | if args.show_key_info: 32 | logger(f"Zoomeye API blocking our request, {username} please check your API usage for this key: {randomkey}", "warn", args.no_color) 33 | return zoomeyes 34 | 35 | data = response.json() 36 | if "list" not in data: 37 | return zoomeyes 38 | subdomains = [item["name"] for item in data["list"] if "name" in item] 39 | zoomeyes.extend(subdomains) 40 | page += 1 41 | except httpx.TimeoutException as e: 42 | if args.show_timeout_info: 43 | logger(f"Timeout reached for Zoomeye API, due to: {e}", "warn", args.no_color) 44 | except Exception as e: 45 | if args.verbose: 46 | logger(f"Exception error occurred in Zoomeye API module due to: {e}, {type(e)}", "warn", args.no_color) 47 | finally: 48 | if args.verbose: 49 | logger(f"Total Subdomains found by Zoomeye API: {len(zoomeyes)}", "info", args.no_color) 50 | return zoomeyes -------------------------------------------------------------------------------- /subdominator/modules/update/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/update/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/update/update.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import subprocess 3 | import os 4 | from rich.console import Console 5 | from rich.markdown import Markdown 6 | from subdominator.modules.logger.logger import logger 7 | from subdominator.modules.utils.utils import Exit 8 | import importlib.metadata as data 9 | 10 | console = Console() 11 | 12 | def getverify(pkg): 13 | version = data.version(pkg) 14 | return version 15 | 16 | def getzip(): 17 | try: 18 | url = "https://api.github.com/repos/RevoltSecurities/Subdominator/releases/latest" 19 | response = requests.get(url, timeout=10) 20 | if response.status_code == 200: 21 | return response.json()['zipball_url'] 22 | else: 23 | logger(f"Hey Update Failed for Subdominator, Please try to update the Subdominator manually", "warn") 24 | Exit(1) 25 | except Exception as e: 26 | pass 27 | 28 | def launch(url,config): 29 | try: 30 | response = requests.get(url, timeout=20, stream=True) 31 | filepath = f"{config}/Subdominator.zip" 32 | if response.status_code == 200: 33 | logger(f"Downloading Subdominator latest version with PIPX", "info") 34 | with open(f"{filepath}", "wb") as streamw: 35 | for data in response.iter_content(): 36 | if data: 37 | streamw.write(data) 38 | try: 39 | subprocess.run(["pipx", "install", f"{filepath}", "--force"], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 40 | except Exception as e: 41 | logger(f"Update Failed for Subdominator, Please try to update the Subdominator manually", "warn") 42 | os.remove(filepath) 43 | Exit(1) 44 | finally: 45 | os.remove(filepath) 46 | else: 47 | logger(f"Update Failed for Subdominator, Please try to update the Subdominator manually", "info") 48 | Exit(1) 49 | except Exception as e: 50 | pass 51 | 52 | def updatelog(): 53 | try: 54 | url = f"https://api.github.com/repos/RevoltSecurities/Subdominator/releases/latest" 55 | response = requests.get(url, timeout=20) 56 | if response.status_code == 200: 57 | loader = response.json()["body"] 58 | console.print(Markdown(loader)) 59 | else: 60 | logger(f"Hey unable to fetch update logs so please visit here --> https://github.com/RevoltSecurities/Subdominator", "info") 61 | Exit(1) 62 | except Exception as e: 63 | pass -------------------------------------------------------------------------------- /subdominator/modules/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/utils/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/utils/utils.py: -------------------------------------------------------------------------------- 1 | import re 2 | import tldextract 3 | import aiofiles 4 | import yaml 5 | import random 6 | from typing import Optional,Tuple 7 | from fake_useragent import UserAgent 8 | import os 9 | from subdominator.modules.logger.logger import logger 10 | from colorama import Fore,Style 11 | import sys 12 | 13 | red = Fore.RED 14 | green = Fore.GREEN 15 | magenta = Fore.MAGENTA 16 | cyan = Fore.CYAN 17 | mixed = Fore.RED + Fore.BLUE 18 | blue = Fore.BLUE 19 | yellow = Fore.YELLOW 20 | white = Fore.WHITE 21 | reset = Style.RESET_ALL 22 | bold = Style.BRIGHT 23 | 24 | def Exit(code=0): 25 | sys.exit(code) 26 | 27 | async def compiler(domain): 28 | pattern = re.compile(r"(?i)[a-zA-Z0-9\*_.-]+\." + re.escape(domain)) 29 | return pattern 30 | 31 | async def extracts(response, domain): 32 | regex = await compiler(domain) 33 | matches = regex.findall(response) 34 | return set(matches) 35 | 36 | def check_subdomain(domain): 37 | parsed = tldextract.extract(domain) 38 | return parsed 39 | 40 | def UserAgents() -> str: 41 | return UserAgent().random 42 | 43 | async def singlekeyloader(filename: str, source: str) -> Optional[str]: 44 | try: 45 | async with aiofiles.open(filename, "r") as streamr: 46 | data = yaml.safe_load(await streamr.read()) 47 | if source not in data: 48 | return None 49 | randomkey = random.choice(data.get(source, [])) 50 | if len(randomkey) != 0: 51 | return randomkey 52 | else: 53 | return None 54 | except Exception as e: 55 | return None 56 | 57 | async def dualkeyloader(filename: str, source: str, splits=False) -> Optional[Tuple[str, str]]: 58 | try: 59 | async with aiofiles.open(filename, "r") as streamr: 60 | data = yaml.safe_load(await streamr.read()) 61 | if source not in data: 62 | return None,None 63 | randomkeys = random.choice(data.get(source, [])) 64 | if len(randomkeys) != 0 : 65 | if splits: 66 | key1, key2 = randomkeys.rsplit(":", 1) 67 | else: 68 | key1, key2 = randomkeys.split(":") 69 | return key1,key2 70 | else: 71 | return None,None 72 | except Exception as e: 73 | return None 74 | 75 | def filters(results): 76 | if not results: 77 | return [] 78 | filtered = [] 79 | for subdomains in results: 80 | if subdomains is None: 81 | subdomains = [] 82 | for subdomain in subdomains: 83 | if subdomain is not None: 84 | filtered.append(subdomain) 85 | return sorted(set(filtered)) 86 | 87 | 88 | async def reader(filename: str, args) -> list: 89 | try: 90 | async with aiofiles.open(filename, mode="r") as streamr: 91 | data = await streamr.read() 92 | return data.splitlines() 93 | except FileNotFoundError: 94 | logger(f"File '{filename}' not found. Please check if it exists.") 95 | Exit(1) 96 | except Exception as e: 97 | logger(f"Exception in file reader module due to: {e} ({type(e).__name__})", "warn", args.no_color) 98 | Exit(1) 99 | 100 | async def check_file_permission(filename: str, args): 101 | try: 102 | async with aiofiles.open(filename, mode="a"): 103 | pass 104 | return True 105 | except PermissionError: 106 | logger(f"Permission denied: User doesn't have write permission in '{filename}' file", "warn", args.no_color) 107 | return False 108 | except Exception as e: 109 | logger(f"Exception in permission check: {e} ({type(e).__name__})", "warn", args.no_color) 110 | return False 111 | 112 | async def check_directory_permission(directory: str, args): 113 | try: 114 | if not os.path.exists(directory): 115 | os.makedirs(directory, exist_ok=True) 116 | test_file = os.path.join(directory, ".permission_check") 117 | async with aiofiles.open(test_file, mode="w") as test: 118 | await test.write("test") 119 | os.remove(test_file) 120 | return True 121 | except PermissionError: 122 | logger(f"Permission denied: User doesn't have write permission in '{directory}' directory", "warn", args.no_color) 123 | return False 124 | except Exception as e: 125 | logger(f"Exception in directory permission check: {e} ({type(e).__name__})", "warn", args.no_color) 126 | return False 127 | 128 | def split_to_list(values: str, delimiter: str = ",") -> list: 129 | return [val.strip() for val in values.split(delimiter) if val.strip()] -------------------------------------------------------------------------------- /subdominator/modules/version/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevoltSecurities/Subdominator/6f09a12cc835f43aa1199103a39657d9bd6d0f71/subdominator/modules/version/__init__.py -------------------------------------------------------------------------------- /subdominator/modules/version/version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import requests 3 | 4 | def version(): 5 | 6 | url = f"https://api.github.com/repos/Revoltsecurities/Subdominator/releases/latest" 7 | 8 | try: 9 | response = requests.get(url, verify=True, timeout=10) 10 | if response.status_code == 200: 11 | data = response.json() 12 | latest = data.get('tag_name') 13 | return latest 14 | except KeyboardInterrupt as e: 15 | quit() 16 | except Exception as e: 17 | pass -------------------------------------------------------------------------------- /subdominator/subdominator.py: -------------------------------------------------------------------------------- 1 | from subdominator.modules.handler import main_handler 2 | 3 | def main(): 4 | main_handler() 5 | 6 | if __name__ == "__main__": 7 | main() --------------------------------------------------------------------------------