├── .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 |
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 |   [](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 | Subdomain |
169 |
170 | {subdomain_rows}
171 |
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("?li>", "", 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()
--------------------------------------------------------------------------------