├── Dockerfile ├── LICENSE.txt ├── README.md ├── cli └── chiasmodon_cli.py ├── pychiasmodon.py ├── requirements.txt └── setup.py /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-alpine 2 | 3 | COPY . . 4 | RUN python setup.py install 5 | 6 | ENTRYPOINT [ "python3","/cli/chiasmodon_cli.py" ] -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Chiasmodon 3 | 4 | [![asciicast](https://asciinema.org/a/QrEtBLFMQrjU1sjRjcgTdo41m.svg)](https://asciinema.org/a/QrEtBLFMQrjU1sjRjcgTdo41m) 5 |

6 | 7 |

8 | Chiasmodon is an OSINT tool that allows users to gather information from various sources and conduct targeted searches based on domains, Google Play applications, email addresses, IP addresses, organizations, URLs, and more. It provides comprehensive scanning capabilities, customizable output formats, and additional options for enhanced data analysis and customization. 9 | 10 | 11 | ## ✨Features 12 | 13 | - [x] **🌐Domain**: Conduct targeted searches by specifying a domain name to gather relevant information related to the domain. 14 | - [x] **🎮Google Play Application**: Search for information related to a specific application on the Google Play Store by providing the application ID. 15 | - [x] **✉️Email, 👤Username, 🔒Password**: Conduct searches based on email, username, or password to identify potential security risks or compromised credentials. 16 | - [x] **🔍 IP Address**: Perform searches using an IP address to gather information such as geolocation, associated domain names, and historical data. 17 | - [x] **🌍 CIDR**: Search for information related to a specified CIDR (Classless Inter-Domain Routing) block, including IP range details and associated networks. 18 | - [x] **🔢 ASN**: Retrieve information about an Autonomous System Number (ASN), including its owner, associated IP ranges, and network details. 19 | - [x] **🔌 Port**: Search for information about a specific port number, including its common usage, associated services, and potential vulnerabilities. 20 | - [x] **🌐 ISP**: Conduct searches based on an Internet Service Provider (ISP) name to gather information about the ISP, its services, and associated IP ranges. 21 | - [x] **🏢 Organization (ORG)**: Search for information related to a specific organization or company, including its contact details, associated domains, and network infrastructure. 22 | - [x] **🔗 URL Path**: Perform searches based on a specific URL path to gather information about the path, its content, and potential security risks. 23 | - [x] **📞 Phone**: Conduct searches using a phone number to gather information such as the associated owner, location, and any available public records. 24 | - [x] **🔍Scan**: Perform a comprehensive scan on a given company domain name in one click, including finding 25 | - Related companies. 26 | - App applications. 27 | - Ips (`Port, Org, Isp, Asn`). 28 | - Subdomains. 29 | - Client credentials (`Email, Username, Password`). 30 | - Employee credentials (`Email, Username, Password`) 31 | - URLs (`Domain/IP, Port, Endpoint`) 32 | 33 | - [X] **🌍Country**: Sort and filter search results by country to gain insights into the geographic distribution of the identified information. 34 | - [x] **📋Output Customization**: Choose the desired output format (text, JSON, or CSV) and specify the filename to save the search results. 35 | - [x] **⚙️Additional Options**: The tool offers various additional options, such as viewing different result types (credentials, URLs, subdomains, emails, passwords, usernames, or applications), setting API tokens, specifying timeouts, limiting results, and more. 36 | 37 | ## 🚀Comming soon 38 | 39 | - **🏢Company Name**: We understand the importance of comprehensive company research. In our upcoming release, you'll be able to search by company name and access a wide range of documents associated with that company. This feature will provide you with a convenient and efficient way to gather crucial information, such as legal documents, financial reports, and other relevant records. 40 | 41 | - **👤Face (Photo)**: Visual data is a powerful tool, and we are excited to introduce our advanced facial recognition feature. With "Search by Face (Photo)," you can upload an image containing a face and leverage cutting-edge technology to identify and match individuals across various data sources. This will allow you to gather valuable information, such as social media profiles, online presence, and potential connections, all through the power of facial recognition. 42 | 43 | ## Why Chiasmodon name ? 44 | Chiasmodon niger is a species of deep sea fish in the family Chiasmodontidae. It is known for its ability to **swallow fish larger than itself**. and so do we. 😉 45 | ![Chiasmodon background](https://journal.voca.network/wp-content/uploads/2017/10/DTR083_1200.png) 46 | 47 | ## 🔑 Subscription 48 | Join us today and unlock the potential of our cutting-edge OSINT tool. Contact https://t.me/Chiasmod0n on Telegram to subscribe and start harnessing the power of Chiasmodon for your domain investigations. 49 | 50 | ## ⬇️Install 51 | ```bash 52 | $ pip install chiasmodon 53 | ``` 54 | Only for linux 👇 55 | ```bash 56 | $ activate-global-python-argcomplete 57 | ``` 58 | ## 💻Usage 59 | Chiasmodon provides a flexible and user-friendly command-line interface and python library. Here are some examples to demonstrate its usage: 60 | 61 | 62 | ``` 63 | usage: chiasmodon_cli.py [-h] 64 | [-m {cred.username,cred.password,cred.email,cred.phone,cred.email.domain,cred.country,domain,domain.all,ip,ip.asn,ip.isp,ip.org,ip.port,ip.country,app.id,app.name,app.domain,url.path,url.port}] 65 | [-vt {full,cred,url,email,phone,password,username,app,domain,ip,related,subdomain}] [-s] [-sr SCAN_RELATED] 66 | [-ss SCAN_SUBDOMAINS] [-sa SCAN_APPS] [-si SCAN_IPS] [-sc SCAN_CLIENTS] [-se SCAN_EMPLOYEES] [-o OUTPUT] 67 | [-ot {text,json,csv}] [-t TIMEOUT] [-l LIMIT] [-nc] [-lv] [-lm] [--init INIT] [-v] 68 | query 69 | 70 | Chiasmodon CLI 71 | 72 | positional arguments: 73 | query query argument 74 | 75 | options: 76 | -h, --help show this help message and exit 77 | -m {cred.username,cred.password,cred.email,cred.phone,cred.email.domain,cred.country,domain,domain.all,ip,ip.asn,ip.isp,ip.org,ip.port,ip.country,app.id,app.name,app.domain,url.path,url.port}, --method {cred.username,cred.password,cred.email,cred.phone,cred.email.domain,cred.country,domain,domain.all,ip,ip.asn,ip.isp,ip.org,ip.port,ip.country,app.id,app.name,app.domain,url.path,url.port} 78 | method to search by it,default is "domain". 79 | -vt {full,cred,url,email,phone,password,username,app,domain,ip,related,subdomain}, --view-type {full,cred,url,email,phone,password,username,app,domain,ip,related,subdomain} 80 | type view the result default is "full". 81 | -s, --scan scan the company domain (Related company, Clients, Employees, Company ASNs, Company Apps). 82 | -sr SCAN_RELATED, --scan-related SCAN_RELATED 83 | Run related scan, default is yes, Ex: -sr no 84 | -ss SCAN_SUBDOMAINS, --scan-subdomains SCAN_SUBDOMAINS 85 | Run subdomains scan, default is yes, Ex: -ss no 86 | -sa SCAN_APPS, --scan-apps SCAN_APPS 87 | Run App scan, default is yes, Ex: -sa no 88 | -si SCAN_IPS, --scan-ips SCAN_IPS 89 | Run IPs scan, default is yes, Ex: -si no 90 | -sc SCAN_CLIENTS, --scan-clients SCAN_CLIENTS 91 | Run clients scan, default is yes, Ex: -sc no 92 | -se SCAN_EMPLOYEES, --scan-employees SCAN_EMPLOYEES 93 | Run employees scan, default is yes, Ex: -se no 94 | -o OUTPUT, --output OUTPUT 95 | filename to save the result 96 | -ot {text,json,csv}, --output-type {text,json,csv} 97 | output format default is "text". 98 | -t TIMEOUT, --timeout TIMEOUT 99 | request timeout default is 360 sec. 100 | -l LIMIT, --limit LIMIT 101 | limit results default is 10000. 102 | -nc, --no-color show result without color. 103 | -lv, --list-view-type 104 | list view type. 105 | -lm, --list-methods list methods. 106 | --init INIT set the api token. 107 | -v, --version version. 108 | ``` 109 | 110 | Examples: 111 | ``` 112 | # Scan company by domain 113 | chiasmodon_cli.py example.com --scan 114 | 115 | # Search for target domain, you will see the result for only this "example.com" 116 | chiasmodon_cli.py example.com 117 | 118 | # Search in target and target subdomains 119 | chiasmodon_cli.py example.com --method domain.all 120 | 121 | # Search for target subdomains 122 | chiasmodon_cli.py example.com --view-type subdomain 123 | 124 | # Search for all creds in United States 125 | chiasmodon_cli.py US --method cred.country 126 | 127 | # Search for related companies by domain 128 | chiasmodon_cli.py example.com --view-type related 129 | 130 | # search for target app id 131 | chiasmodon_cli.py com.discord --method app.id 132 | 133 | # search for target app domain 134 | chiasmodon_cli.py discord.com --method app.domain 135 | 136 | # search for target app name 137 | chiasmodon_cli.py Discord --method app.name 138 | 139 | # Search for ip asn 140 | chiasmodon_cli.py AS123 --method ip.asn 141 | 142 | # Search for cred username 143 | chiasmodon_cli.py someone --method cred.username 144 | 145 | # Search for cred password 146 | chiasmodon_cli.py example@123 --method cred.password 147 | 148 | # Search for url endpoint 149 | chiasmodon_cli.py /wp-login.php --method url.path 150 | 151 | # Search for ip 152 | chiasmodon_cli.py 1.1.1.1 --method ip 153 | 154 | # Search for cidr 155 | chiasmodon_cli.py xx.xx.xx.0/24 --method ip 156 | 157 | # Search for target creds by domain emsils 158 | chiasmodon_cli.py example.com --method cred.email.domain 159 | 160 | # Search for target email 161 | chiasmodon_cli.py someone@example.com --method cred.email 162 | 163 | # search for multiple targets: 164 | chiasmodon_cli.py targets.txt --method domain --output example-creds.txt 165 | ``` 166 | 167 | Please note that these examples represent only a fraction of the available options and use cases. Refer to the documentation for more detailed instructions and explore the full range of features provided by Chiasmodon. 168 | 169 | 170 | ## 💬 Contributions and Feedback 171 | 172 | Contributions and feedback are welcome! If you encounter any issues or have suggestions for improvements, please submit them to the Chiasmodon GitHub repository. Your input will help us enhance the tool and make it more effective for the OSINT community. 173 | 174 | ## 📜License 175 | 176 | Chiasmodon is released under the [MIT License](https://opensource.org/licenses/MIT). See the [LICENSE](https://github.com/chiasmodon/LICENSE.txt) file for more details. 177 | 178 | ## ⚠️Disclaimer 179 | 180 | Chiasmodon is intended for legal and authorized use only. Users are responsible for ensuring compliance with applicable laws and regulations when using the tool. The developers of Chiasmodon disclaim any responsibility for the misuse or illegal use of the tool. 181 | 182 | ## 📢Acknowledgments 183 | 184 | Chiasmodon is the result of collaborative efforts from a dedicated team of contributors who believe in the power of OSINT. We would like to express our gratitude to the open-source community for their valuable contributions and support. 185 | 186 | ## 🔗Chiasmodon Links 187 | 188 | - [🐍 Python Library](https://pypi.org/project/chiasmodon) 189 | - [📱 Mobile (APK)](https://github.com/chiasmod0n/chiasmodon-mobile) 190 | - [🌐 Website](http://chiasmodon.online) 191 | - [💬 Telegram](https://t.me/chiasmod0n) 192 | - [🐦 X/Twitter](https://x.com/chiasmod0n) 193 | 194 | 195 | ## ⭐️Star History 196 | 197 | 198 | 199 | 200 | 201 | Star History Chart 202 | 203 | 204 | -------------------------------------------------------------------------------- /cli/chiasmodon_cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # PYTHON_ARGCOMPLETE_OK 3 | 4 | import os 5 | import sys 6 | import argcomplete 7 | import json 8 | import argparse 9 | import tldextract 10 | from yaspin import yaspin 11 | from pathlib import Path 12 | from pychiasmodon import Chiasmodon,Result,VERSION,VIEW_TYPE_LIST,T,_METHODS 13 | 14 | ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) 15 | 16 | class ULIT: 17 | @staticmethod 18 | def rFile(file:Path) -> str: 19 | if not file.is_file(): 20 | print(f'{T.RED}Not found {file} file.{T.RESET}') 21 | return 22 | 23 | with open(file, 'r') as f: 24 | return f.read() 25 | 26 | @staticmethod 27 | def wFile(file:Path, data:str) -> str: 28 | with open(file, 'w') as f: 29 | f.write(data) 30 | 31 | @staticmethod 32 | def rFileToJson(file:Path) -> dict: 33 | return json.loads(ULIT.rFile(file=file)) 34 | 35 | @staticmethod 36 | def wJsonToFile(file:Path, data:dict) -> dict: 37 | ULIT.wFile( 38 | file=file, 39 | data=json.dumps(data) 40 | ) 41 | 42 | @staticmethod 43 | def get_root_domain(d:str) -> str: 44 | domain = d.split()[0] 45 | x = tldextract.extract(domain) 46 | if not x.suffix: 47 | return None 48 | 49 | return '{}.{}'.format(x.domain, x.suffix) 50 | 51 | class Scan(Chiasmodon): 52 | def __init__(self, options:argparse.Namespace) -> None: 53 | self.options :argparse.Namespace=options 54 | self.result :list = [] 55 | conf_file :Path = Path(ROOT_DIR, 'conf.json') 56 | token:str = '' 57 | 58 | if not conf_file.is_file():ULIT.wJsonToFile(conf_file, {}) 59 | 60 | if self.options.init: 61 | token = self.options.init 62 | ULIT.wJsonToFile(conf_file, {'token':self.options.init}) 63 | 64 | elif not conf_file.is_file(): 65 | ULIT.wJsonToFile(conf_file, {}) 66 | token = '' 67 | 68 | else: 69 | token = ULIT.rFileToJson(conf_file).get('token') or '' 70 | 71 | super().__init__( 72 | token=token, 73 | conf_file=conf_file, 74 | color=True if not self.options.no_color else False, 75 | debug=True, 76 | check_token=self.options.init, 77 | ) 78 | 79 | if self.options.init: 80 | sys.exit() 81 | 82 | self.scan_mode = True 83 | 84 | def scan_callback(self,beta,ys): 85 | self.print(beta.print(), ys) 86 | 87 | def proc(self): 88 | if not self.options.query: 89 | self.print(f"{T.RED}You can't run scan without company domain\nPlease use (-d or --domain) to scan the domain{T.RESET}") 90 | sys.exit(0) 91 | 92 | domain = ULIT.get_root_domain(self.options.query) 93 | if not domain: 94 | self.print(f"{T.RED}Wrong domain{T.RESET}",) 95 | sys.exit(0) 96 | 97 | self.output_folder = Path(domain) 98 | self.output_folder.mkdir(exist_ok=True, parents=True) 99 | 100 | self.__scan( 101 | domain=domain, 102 | ) 103 | 104 | 105 | def __scan(self, domain): 106 | status = False 107 | 108 | print_output = f'{T.MAGENTA}>{T.RESET}{T.YELLOW} Saved output{T.RESET}: \n' 109 | output = { 110 | 'related':[], 111 | 'apps':[], 112 | 'client-creds':[], 113 | 'client-usernames':[], 114 | 'client-passwords':[], 115 | 'client-emails':[], 116 | 'employe-creds':[], 117 | 'employe-usernames':[], 118 | 'employe-passwords':[], 119 | 'employe-emails':[], 120 | 'subdomains':[], 121 | 'urls':[], 122 | 'endpoints':[], 123 | 'ports':[], 124 | 125 | } 126 | 127 | if self.options.scan_related.lower() == 'yes': 128 | related = self.search( 129 | method='domain', 130 | query=domain, 131 | view_type='related', 132 | sort=True , 133 | timeout=self.options.timeout, 134 | limit=1000000, 135 | callback_view_result=self.scan_callback, 136 | yaspin=yaspin, 137 | search_text=f'Find {T.GREEN+domain+T.RESET} related companies...', 138 | err_text=f'Not found related !' 139 | ) 140 | 141 | 142 | if related: 143 | status = True 144 | output['related'] = [i.save_format() for i in related] 145 | ULIT.wFile((self.output_folder / 'related.txt'), '\n'.join(output['related'])) 146 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'related.txt')}{T.RESET}\n" 147 | else: 148 | self.print(f'{T.RED}💥 Not found related !{T.RESET}') 149 | self.print(f'{T.MAGENTA}{"-"*30}{T.RESET}') 150 | if self.options.scan_subdomains.lower() == 'yes': 151 | subdomains = self.search( 152 | method='domain', 153 | query=domain, 154 | view_type='subdomain', 155 | sort=True , 156 | timeout=self.options.timeout, 157 | limit=1000000, 158 | callback_view_result=self.scan_callback, 159 | yaspin=yaspin, 160 | search_text=f'Find {T.GREEN+domain+T.RESET} subdomains...', 161 | err_text=f'Not found subdomains !' 162 | ) 163 | 164 | 165 | if subdomains: 166 | status = True 167 | output['subdomains'] = [i.save_format() for i in subdomains] 168 | ULIT.wFile((self.output_folder / 'subdomains.txt'), '\n'.join(output['subdomains'])) 169 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'subdomains.txt')}{T.RESET}\n" 170 | self.print(f'{T.MAGENTA}{"-"*30}{T.RESET}') 171 | 172 | if self.options.scan_apps.lower() == 'yes': 173 | apps = self.search( 174 | method='app.domain', 175 | query=domain, 176 | view_type='app', 177 | sort=True , 178 | timeout=self.options.timeout, 179 | limit=1000000, 180 | callback_view_result=self.scan_callback, 181 | yaspin=yaspin, 182 | search_text=f'Find {T.GREEN+domain+T.RESET} Apps...', 183 | err_text=f'Not found apps !' 184 | ) 185 | 186 | 187 | if apps: 188 | status = True 189 | output['apps'] = [i.save_format() for i in apps] 190 | ULIT.wFile((self.output_folder / 'apps.txt'), '\n'.join(output['apps'])) 191 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'apps.txt')}{T.RESET}\n" 192 | self.print(f'{T.MAGENTA}{"-"*30}{T.RESET}') 193 | if self.options.scan_ips.lower() == 'yes': 194 | ips = self.search( 195 | method='domain.all', 196 | query=domain, 197 | view_type='ip', 198 | sort=True , 199 | timeout=self.options.timeout, 200 | limit=1000000, 201 | callback_view_result=self.scan_callback, 202 | yaspin=yaspin, 203 | search_text=f'Find {T.GREEN+domain+T.RESET} IPs...', 204 | err_text=f'Not found ips !' 205 | ) 206 | 207 | 208 | if ips: 209 | status = True 210 | output['ips'] = [i.save_format() for i in ips] 211 | ULIT.wFile((self.output_folder / 'ips.txt'), '\n'.join(output['ips'])) 212 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'ips.txt')}{T.RESET}\n" 213 | 214 | self.print(f'{T.MAGENTA}{"-"*30}{T.RESET}') 215 | if self.options.scan_clients.lower() == 'yes': 216 | client_creds:list[Result] = self.search( 217 | query=domain, 218 | method='domain.all', 219 | view_type='full', 220 | sort=True , 221 | timeout=self.options.timeout, 222 | limit=1000000, 223 | callback_view_result=self.scan_callback, 224 | yaspin=yaspin, 225 | search_text=f'Find {T.GREEN+domain+T.RESET} client creds...', 226 | err_text=f'Not found clients !' 227 | 228 | ) 229 | 230 | if client_creds: 231 | status = True 232 | output['client-creds'] =[i.save_format() for i in client_creds] 233 | ULIT.wFile((self.output_folder / 'client-creds.txt'), '\n'.join([':'.join(i) for i in output['client-creds']])) 234 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'client-creds.txt')}{T.RESET}\n" 235 | 236 | for i in client_creds: 237 | output['client-usernames'].append(i.credUsername) if i.credUsername and not i.credEmail and '/' not in i.credUsername and i.credUsername not in output['client-usernames'] else None 238 | output['client-passwords'].append(i.credPassword) if i.credPassword and i.credPassword not in output['client-passwords'] else None 239 | output['client-emails'].append(i.credEmail) if i.credEmail and i.credEmail not in output['client-emails'] else None 240 | output['subdomains'].append(i.domain) if i.domain and i.domain not in output['subdomains'] and i.domain != domain else None 241 | output['urls'].append(i.url) if i.url and i.url not in output['urls'] else None 242 | output['endpoints'].append(i.urlPath) if i.urlPath and i.urlPath not in output['endpoints'] else None 243 | output['ports'].append(i.urlPort) if i.urlPort and i.urlPort not in output['ports'] else None 244 | 245 | ULIT.wFile((self.output_folder / 'client-usernames.txt'), '\n'.join(output['client-usernames'])) 246 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'client-usernames.txt')}{T.RESET}\n" 247 | ULIT.wFile((self.output_folder / 'client-emails.txt'), '\n'.join(output['client-emails'])) 248 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'client-emails.txt')}{T.RESET}\n" 249 | ULIT.wFile((self.output_folder / 'client-passwords.txt'), '\n'.join(output['client-passwords'])) 250 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'client-passwords.txt')}{T.RESET}\n" 251 | ULIT.wFile((self.output_folder / 'endpoints.txt'), '\n'.join(output['endpoints'])) 252 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'endpoints.txt')}{T.RESET}\n" 253 | ULIT.wFile((self.output_folder / 'ports.txt'), '\n'.join([f"{i}" for i in output['ports']])) 254 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'ports.txt')}{T.RESET}\n" 255 | ULIT.wFile((self.output_folder / 'subdomains.txt'), '\n'.join(output['subdomains'])) 256 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'subdomains.txt')}{T.RESET}\n" 257 | ULIT.wFile((self.output_folder / 'urls.txt'), '\n'.join([f"{i}" for i in output['urls']])) 258 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'urls.txt')}{T.RESET}\n" 259 | 260 | 261 | self.print(f'{T.MAGENTA}{"-"*30}{T.RESET}') 262 | if self.options.scan_employees.lower() == 'yes': 263 | employe_creds = self.search( 264 | query=domain, 265 | method='cred.email.domain', 266 | view_type='full', 267 | sort=True, 268 | timeout=self.options.timeout, 269 | callback_view_result=self.scan_callback, 270 | limit=1000000, 271 | yaspin=yaspin, 272 | search_text=f'Find {T.GREEN+domain+T.RESET} employees creds...', 273 | err_text=f'Not found Employees!' 274 | ) 275 | 276 | if employe_creds: 277 | status = True 278 | output['employe-creds'] =[i.save_format() for i in employe_creds] 279 | ULIT.wFile((self.output_folder / 'employe-creds.txt'), '\n'.join([':'.join(i) for i in output['employe-creds']])) 280 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'employe-creds.txt')}{T.RESET}\n" 281 | for i in employe_creds: 282 | output['employe-usernames'].append(i.credUsername) if i.credUsername and not i.credEmail and '/' not in i.credUsername and i.credUsername not in output['employe-usernames'] else None 283 | output['employe-passwords'].append(i.credPassword) if i.credPassword and i.credPassword not in output['employe-passwords'] else None 284 | output['employe-emails'].append(i.credEmail) if i.credEmail and i.credEmail not in output['employe-emails'] else None 285 | 286 | ULIT.wFile((self.output_folder / 'employe-usernames.txt'), '\n'.join(output['employe-usernames'])) 287 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'employe-usernames.txt')}{T.RESET}\n" 288 | ULIT.wFile((self.output_folder / 'employe-emails.txt'), '\n'.join(output['employe-emails'])) 289 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'employe-emails.txt')}{T.RESET}\n" 290 | ULIT.wFile((self.output_folder / 'employe-passwords.txt'), '\n'.join(output['employe-passwords'])) 291 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'employe-passwords.txt')}{T.RESET}\n" 292 | 293 | 294 | if status: 295 | ULIT.wJsonToFile((self.output_folder / 'scan.json'), output) 296 | print_output += f"\t{T.MAGENTA}-{T.RESET} {T.BLUE}{(self.output_folder / 'scan.json')}{T.RESET}" 297 | self.print(print_output) 298 | 299 | class CLI(Chiasmodon): 300 | def __init__(self, options:argparse.Namespace) -> None: 301 | 302 | self.options :argparse.Namespace=options 303 | self.result :list = [] 304 | 305 | conf_file :Path = Path(ROOT_DIR, 'conf.json') 306 | token:str = '' 307 | 308 | if not conf_file.is_file():ULIT.wJsonToFile(conf_file, {}) 309 | 310 | if self.options.init: 311 | token = self.options.init 312 | ULIT.wJsonToFile(conf_file, {'token':self.options.init}) 313 | 314 | elif not conf_file.is_file(): 315 | ULIT.wJsonToFile(conf_file, {}) 316 | token = '' 317 | 318 | else: 319 | token = ULIT.rFileToJson(conf_file).get('token') or '' 320 | 321 | super().__init__( 322 | token=token, 323 | conf_file=conf_file, 324 | color=True if not self.options.no_color else False, 325 | #debug=self.options.debug, 326 | debug=True, 327 | check_token=self.options.init, 328 | ) 329 | 330 | if self.options.init: 331 | sys.exit() 332 | 333 | def review_results(self, 334 | beta:Result, 335 | ys=True, 336 | ) -> None: 337 | 338 | if beta.save_format() not in self.result: 339 | self.print(beta.print(), ys=ys) 340 | self.result.append(beta.save_format()) 341 | 342 | def save_result(self, view_type) -> None: 343 | 344 | if self.options.output: 345 | 346 | if self.options.output_type == "text": 347 | if self.result and view_type != 'cred': 348 | self.result.remove(None) if None in self.result else None 349 | 350 | ULIT.wFile( 351 | self.options.output, 352 | '\n'.join([':'.join(i) if type(i) == list else i for i in self.result]) 353 | ) 354 | 355 | if self.options.output_type == "csv": 356 | if self.result and view_type != 'cred': 357 | self.result.remove(None) if None in self.result else None 358 | 359 | ULIT.wFile( 360 | self.options.output, 361 | '\n'.join([','.join(['url/app_id','user/email', 'password', 'country', 'date'])]+[','.join(i) if type(i) == list else i for i in self.result]) if view_type == 'cred' else '\n'.join([view_type]+[','.join(i) if type(i) == list else i for i in self.result]) 362 | ) 363 | 364 | if self.options.output_type == "json": 365 | ULIT.wJsonToFile( 366 | self.options.output, 367 | self.result 368 | ) 369 | 370 | 371 | def proc(self): 372 | 373 | query = ULIT.rFile(f).splitlines() if (f:=Path(self.options.query)).is_file() else [self.options.query.strip()] 374 | 375 | 376 | for i in query: 377 | self.search( 378 | query=i, 379 | method=self.options.method, 380 | view_type=self.options.view_type, 381 | limit=self.options.limit, 382 | timeout=self.options.timeout, 383 | sort=True, 384 | yaspin=yaspin, 385 | callback_view_result=self.review_results, 386 | ) 387 | 388 | if self.options.output and self.result: 389 | self.save_result(self.options.view_type) 390 | self.print(f'{T.MAGENTA}>{T.RESET}{T.YELLOW} Saved output to {T.RESET}: {T.GREEN}{self.options.output}{T.RESET}') 391 | 392 | 393 | if __name__ == "__main__": 394 | 395 | if len(sys.argv) == 1 or '--help' in sys.argv or '-h' in sys.argv: 396 | print(f""" 397 | 🙂 🙂 398 | /|\\ /|\\ 399 | /\\ /\\ 400 | \\___/ \\___/ 🔑 401 | {T.BLUE}~^~^~^~^~^~^~^~^~^~^~^~^~{T.BLUE}~^~^~^~{T.GREEN} {T.RESET}/|\\{T.GREEN} 402 | |\\ {T.YELLOW}\\\\\\\\{T.GREEN}__ {T.MAGENTA}Chiasmodon{T.RESET} {T.RED}{VERSION}{T.GREEN} {T.RESET}/\\{T.GREEN} 403 | | \\_/ {T.RED}o{T.GREEN} \\ {T.CYAN}o{T.GREEN} {T.RESET}\\___/{T.GREEN} 404 | > _ {T.YELLOW}(({T.GREEN} <_ {T.CYAN}oo{T.GREEN} 405 | | / \\__+___/ 406 | |/ |/ 407 | 408 | {T.MAGENTA}>{T.RESET} {T.YELLOW}Admin{T.RESET}: {T.GREEN}https://t.me/Chiasmod0n 409 | {T.RESET}""") 410 | parser = argparse.ArgumentParser(description='Chiasmodon CLI', formatter_class=argparse.RawTextHelpFormatter,) 411 | 412 | if '-lm' not in sys.argv and '--list-methods' not in sys.argv and '-lv' not in sys.argv and '--list-view-type' not in sys.argv and '-v' not in sys.argv and '--version' not in sys.argv and '--init' not in sys.argv: 413 | parser.add_argument('query', type=str, help='query argument') 414 | 415 | parser.add_argument('-m','--method', help='method to search by it,default is "domain".', choices=_METHODS, type=str, default='domain') 416 | parser.add_argument('-vt','--view-type', help='type view the result default is "full".', choices=VIEW_TYPE_LIST, type=str, default='full') 417 | parser.add_argument('-s','--scan', help='scan the company domain (Related company, Clients, Employees, Company ASNs, Company Apps).',action='store_true') 418 | parser.add_argument('-sr','--scan-related', help='Run related scan, default is yes, Ex: -sr no',type=str, default='yes') 419 | parser.add_argument('-ss','--scan-subdomains', help='Run subdomains scan, default is yes, Ex: -ss no',type=str, default='yes') 420 | parser.add_argument('-sa','--scan-apps', help='Run App scan, default is yes, Ex: -sa no',type=str, default='yes') 421 | parser.add_argument('-si','--scan-ips', help='Run IPs scan, default is yes, Ex: -si no',type=str, default='yes') 422 | parser.add_argument('-sc','--scan-clients', help='Run clients scan, default is yes, Ex: -sc no',type=str, default='yes') 423 | parser.add_argument('-se','--scan-employees',help='Run employees scan, default is yes, Ex: -se no',type=str, default='yes') 424 | parser.add_argument('-o','--output', help='filename to save the result', type=str,) 425 | parser.add_argument('-ot','--output-type', help='output format default is "text".', choices=['text', 'json', 'csv'], type=str, default='text') 426 | parser.add_argument('-t','--timeout', help='request timeout default is 360 sec.',type=int, default=360) 427 | parser.add_argument('-l','--limit', help='limit results default is 10000.',type=int, default=10000) 428 | parser.add_argument('-nc','--no-color', help='show result without color.',action='store_true') 429 | parser.add_argument('-lv','--list-view-type',help='list view type.',action='store_true') 430 | parser.add_argument('-lm','--list-methods', help='list methods.', action='store_true') 431 | parser.add_argument('--init', help='set the api token.',type=str) 432 | 433 | parser.add_argument('-v','--version', help='version.',action='store_true') 434 | 435 | parser.epilog = f''' 436 | Examples: 437 | 438 | # Scan company by domain 439 | {Path(sys.argv[0]).name} example.com --scan 440 | 441 | # Search for target domain, you will see the result for only this "example.com" 442 | {Path(sys.argv[0]).name} example.com 443 | 444 | # Search in target and target subdomains 445 | {Path(sys.argv[0]).name} example.com --method domain.all 446 | 447 | # Search for target subdomains 448 | {Path(sys.argv[0]).name} example.com --view-type subdomain 449 | 450 | # Search for all creds in United States 451 | {Path(sys.argv[0]).name} US --method cred.country 452 | 453 | # Search for related companies by domain 454 | {Path(sys.argv[0]).name} example.com --view-type related 455 | 456 | # search for target app id 457 | {Path(sys.argv[0]).name} com.discord --method app.id 458 | 459 | # search for target app domain 460 | {Path(sys.argv[0]).name} discord.com --method app.domain 461 | 462 | # search for target app name 463 | {Path(sys.argv[0]).name} Discord --method app.name 464 | 465 | # Search for ip asn 466 | {Path(sys.argv[0]).name} AS123 --method ip.asn 467 | 468 | # Search for cred username 469 | {Path(sys.argv[0]).name} someone --method cred.username 470 | 471 | # Search for cred password 472 | {Path(sys.argv[0]).name} example@123 --method cred.password 473 | 474 | # Search for url endpoint 475 | {Path(sys.argv[0]).name} /wp-login.php --method url.path 476 | 477 | # Search for ip 478 | {Path(sys.argv[0]).name} 1.1.1.1 --method ip 479 | 480 | # Search for cidr 481 | {Path(sys.argv[0]).name} xx.xx.xx.0/24 --method ip 482 | 483 | # Search for target creds by domain emsils 484 | {Path(sys.argv[0]).name} example.com --method cred.email.domain 485 | 486 | # Search for target email 487 | {Path(sys.argv[0]).name} someone@example.com --method cred.email 488 | 489 | # search for multiple targets: 490 | {Path(sys.argv[0]).name} targets.txt --method domain --output example-creds.txt 491 | ''' 492 | 493 | argcomplete.autocomplete(parser) 494 | args = parser.parse_args() 495 | if args.list_view_type: 496 | for i in VIEW_TYPE_LIST: 497 | print(i) 498 | sys.exit(0) 499 | 500 | if args.list_methods: 501 | for i in _METHODS: 502 | print(i) 503 | sys.exit(0) 504 | 505 | if args.version: 506 | print(VERSION) 507 | sys.exit(0) 508 | 509 | if args.scan: 510 | root=Scan(options=args) 511 | root.proc() 512 | 513 | else: 514 | root=CLI(options=args) 515 | root.proc() 516 | -------------------------------------------------------------------------------- /pychiasmodon.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | import sys 4 | import time 5 | import requests 6 | from yaspin import Spinner 7 | 8 | VERSION = "3.0.2" 9 | _API_URL = 'http://chiasmodon.online/v2/api/beta' 10 | _API_HEADERS = {'user-agent':'cli/python'} 11 | _VIEW_TYPE = { 12 | 'full':[ 13 | 'cred.username', 14 | 'cred.phone', 15 | 'cred.password', 16 | 'cred.email', 17 | 'cred.email.domain', 18 | 'cred.country', 19 | 'domain', 20 | 'domain.all', 21 | 22 | 'ip', 23 | 'ip.asn', 24 | 'ip.isp', 25 | 'ip.org', 26 | 'ip.port', 27 | 'ip.country', 28 | 'app.id', 29 | 'app.name', 30 | 'app.domain', 31 | 'url.path', 32 | 'url.port', 33 | ], 34 | 'cred':[ 35 | 'cred.phone', 36 | 'cred.username', 37 | 'cred.password', 38 | 'cred.email', 39 | 'cred.email.domain', 40 | 'cred.country', 41 | 'domain', 42 | 'domain.all', 43 | 44 | 'ip', 45 | 'ip.asn', 46 | 'ip.isp', 47 | 'ip.org', 48 | 'ip.port', 49 | 'ip.country', 50 | 'app.id', 51 | 'app.name', 52 | 'app.domain', 53 | 'url.path', 54 | 'url.port', 55 | ], 56 | 'url':[ 57 | 'cred.username', 58 | 'cred.password', 59 | 'cred.phone', 60 | 'cred.email', 61 | 'cred.email.domain', 62 | 'cred.country', 63 | 'domain', 64 | 'domain.all', 65 | 'ip', 66 | 'ip.asn', 67 | 'ip.isp', 68 | 'ip.org', 69 | 'ip.port', 70 | 'ip.country', 71 | 'url.path', 72 | 'url.port', 73 | ], 74 | 'email':[ 75 | 'cred.username', 76 | 'cred.phone', 77 | 'cred.password', 78 | 'cred.country', 79 | 'cred.email.domain', 80 | 'domain', 81 | 'domain.all', 82 | 'ip', 83 | 'ip.asn', 84 | 'ip.isp', 85 | 'ip.org', 86 | 'ip.port', 87 | 'ip.country', 88 | 'app.id', 89 | 'app.name', 90 | 'app.domain', 91 | 'url.path', 92 | 'url.port', 93 | ], 94 | 'phone':[ 95 | 'cred.username', 96 | 'cred.email', 97 | 'cred.email.domain', 98 | 'domain', 99 | 'domain.all', 100 | 'ip', 101 | 'ip.asn', 102 | 'ip.isp', 103 | 'ip.org', 104 | 'ip.port', 105 | 'ip.country', 106 | 'app.id', 107 | 'app.name', 108 | 'app.domain', 109 | 'url.path', 110 | 'url.port', 111 | 'cred.country', 112 | ], 113 | 'password':[ 114 | 'cred.username', 115 | 'cred.phone', 116 | 117 | 'cred.email', 118 | 'cred.email.domain', 119 | 'domain', 120 | 'domain.all', 121 | 'ip', 122 | 'ip.asn', 123 | 'ip.isp', 124 | 'ip.org', 125 | 'ip.port', 126 | 'ip.country', 127 | 'app.id', 128 | 'app.name', 129 | 'app.domain', 130 | 'url.path', 131 | 'url.port', 132 | 'cred.country', 133 | ], 134 | 'username': [ 135 | 'cred.phone', 136 | 'cred.password', 137 | 'domain', 138 | 'domain.all', 139 | 'ip', 140 | 'ip.asn', 141 | 'ip.isp', 142 | 'ip.org', 143 | 'ip.port', 144 | 'ip.country', 145 | 'app.id', 146 | 'app.name', 147 | 'app.domain', 148 | 'url.path', 149 | 'url.port', 150 | 'cred.country', 151 | ], 152 | 'app':[ 153 | 'cred.phone', 154 | 'cred.username', 155 | 'cred.password', 156 | 'cred.email', 157 | 'cred.email.domain', 158 | 'cred.country', 159 | 'app.domain' 160 | ], 161 | 'domain':[ 162 | 'cred.username', 163 | 'cred.phone', 164 | 'cred.password', 165 | 'cred.email', 166 | 'cred.email.domain', 167 | 'cred.country', 168 | 'domain', 169 | 'domain.all', 170 | 171 | 'ip', 172 | 'ip.asn', 173 | 'ip.isp', 174 | 'ip.org', 175 | 'ip.port', 176 | 'ip.country', 177 | 'app.id', 178 | 'app.name', 179 | 'app.domain', 180 | 'url.path', 181 | 'url.port', 182 | ], 183 | 'ip':[ 184 | 'cred.username', 185 | 'cred.phone', 186 | 'cred.password', 187 | 'cred.email', 188 | 'cred.email.domain', 189 | 'domain', 190 | 'domain.all', 191 | 'ip.asn', 192 | 'ip.isp', 193 | 'ip.org', 194 | 'ip.port', 195 | 'ip.country', 196 | 'app.id', 197 | 'app.name', 198 | 'app.domain', 199 | 'url.path', 200 | 'url.port', 201 | 'cred.country', 202 | ], 203 | 'related':[ 204 | 'domain', 205 | ], 206 | 'subdomain':[ 207 | 'domain' 208 | ] 209 | } 210 | 211 | _METHODS = [ 212 | # cred 213 | 'cred.username', # Query like -> somone 214 | 'cred.password', # Query like -> lol@123 215 | 'cred.email', # Query like -> somone@example.com 216 | 'cred.phone', # Query line -> xxxxxxxx # without : + or - or space or ) or ( 217 | 'cred.email.domain', # Query like -> example.com 218 | 'cred.country', # Query like -> US 219 | 220 | # domain 221 | 'domain', # Query like -> example.com 222 | 'domain.all', # Query like -> example.com 223 | 224 | # ip 225 | 'ip', # Query like -> 1.1.1.1 226 | 'ip.asn', # Query like -> as123 227 | 'ip.isp', # Query like -> "isp company" 228 | 'ip.org', # Query like -> "org name" 229 | 'ip.port', # Query like -> 22 230 | 'ip.country', # Query like -> US 231 | 232 | # app 233 | 'app.id', # Query like -> com.example 234 | 'app.name', # Query like -> Example 235 | 'app.domain', # Query like -> example.com 236 | 237 | # url 238 | 'url.path', # Query like -> "isp company" 239 | 'url.port', # Query like -> 8080 240 | ] 241 | 242 | VIEW_TYPE_LIST = list(_VIEW_TYPE.keys()) 243 | 244 | class T: 245 | RED = '\033[91m' 246 | GREEN = '\033[92m' 247 | YELLOW = '\033[93m' 248 | BLUE = '\033[94m' 249 | MAGENTA = '\033[95m' 250 | CYAN = '\033[96m' 251 | RESET = '\033[0m' 252 | 253 | 254 | class Chiasmodon: 255 | def __init__(self, token=None, color=True, debug=True,conf_file=None,check_token=True) -> None: 256 | self.token = token 257 | self.conf_file = conf_file 258 | self.debug = debug 259 | self.err :bool = False 260 | self.msg :str = '' 261 | self.__result:list[Result] = [] 262 | self.scan_mode = False 263 | 264 | if not color: 265 | T.RED = '' 266 | T.GREEN = '' 267 | T.YELLOW = '' 268 | T.BLUE = '' 269 | T.MAGENTA = '' 270 | T.CYAN = '' 271 | T.RESET = '' 272 | 273 | if self.token and check_token: 274 | if self.__check_token(): 275 | self.print(f'{T.GREEN}Set token successfully{T.RESET}') 276 | 277 | else: 278 | try:os.remove(conf_file) 279 | except:pass 280 | 281 | self.print(f'{T.RED}{self.msg}{T.RESET}') 282 | return 283 | 284 | def proc_all_domains(self, 285 | query, 286 | view_type, 287 | sort, 288 | timeout, 289 | limit, 290 | callback_view_result, 291 | yaspin, 292 | search_text, 293 | err_text) -> list: 294 | 295 | 296 | domains :list = self.__proc_query( 297 | query=query, 298 | method='domain', 299 | view_type='subdomain', 300 | sort=sort, 301 | timeout=timeout, 302 | limit=limit, 303 | callback_view_result=None, 304 | yaspin=None, 305 | search_text=search_text, 306 | err_text=err_text, 307 | ) 308 | self.__result :list = [] 309 | result :list = [] 310 | 311 | domains = [i.domain for i in domains] 312 | if query not in domains:domains.append(query) 313 | 314 | for domain in domains: 315 | result.extend(self.__proc_query( 316 | query=domain, 317 | method='domain', 318 | view_type=view_type, 319 | sort=sort, 320 | timeout=timeout, 321 | limit=limit, 322 | callback_view_result=callback_view_result, 323 | yaspin=yaspin, 324 | search_text=search_text.replace(query, domain), 325 | err_text=err_text, 326 | )) 327 | self.__result :list = [] 328 | 329 | return result 330 | 331 | 332 | def filter(self,query:str,method:str): 333 | 334 | if 'domain' in method: 335 | if not re.match(r"^(?!.*\d+\.\d+\.\d+\.\d+$)[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$", query): 336 | self.print(f'{T.RED}Your format query is wrong!\nThis is not domain.{T.RESET}') 337 | return False 338 | 339 | elif method == 'ip': 340 | if not re.match(r"\b(?:\d{1,3}\.){3}\d{1,3}\b", query): 341 | self.print(f'{T.RED}Your format query is wrong!\nAccept only ipv4.{T.RESET}') 342 | return False 343 | 344 | elif method == 'ip.asn': 345 | if not query.lower().startswith('as'): 346 | self.print(f'{T.RED}Your format query is wrong!\nThe ASN starts with AS\nLike this: AS1234.{T.RESET}') 347 | return False 348 | 349 | elif method in ['ip.port', 'url.port']: 350 | if not re.match(r":(\d+)", query): 351 | self.print(f'{T.RED}Your format query is wrong!\nThis is not port.{T.RESET}') 352 | return False 353 | 354 | elif method == 'cred.email': 355 | if not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', query): 356 | self.print(f'{T.RED}Your format query is wrong!\nThis is not email.{T.RESET}') 357 | return False 358 | 359 | elif method == 'cred.country' or method == 'ip.country': 360 | if not len(query) == 2: 361 | self.print(f'{T.RED}Your format query is wrong!\nAccept only country code.{T.RESET}') 362 | return False 363 | 364 | elif method == 'url.path': 365 | if not query[0] == '/': 366 | self.print(f'{T.RED}Your format query is wrong!\nThe url path moset be like: /somthing{T.RESET}') 367 | return False 368 | 369 | return query 370 | 371 | def print(self,text, ys=None, ys_err=False) -> None: 372 | if text == None:return 373 | if self.debug: 374 | if ys: 375 | if not ys_err: 376 | ys.write(text) 377 | else: 378 | ys.text = text 379 | else: 380 | print(text) 381 | 382 | def __check_token(self): 383 | if self.__request({ 384 | 'token' : self.token, 385 | 'version' : VERSION, 386 | 'method' : 'token' 387 | }).get('is_active'): 388 | return True 389 | 390 | return False 391 | 392 | def __request(self, data:dict,timeout=60): 393 | 394 | try: 395 | resp = requests.post(_API_URL, data=data, headers=_API_HEADERS, timeout=timeout) 396 | resp.close() 397 | resp = resp.json() 398 | 399 | try: 400 | if resp.get('err'): 401 | self.err = True 402 | self.msg = resp['msg'] 403 | except:pass 404 | 405 | return resp 406 | 407 | except requests.exceptions.ReadTimeout: 408 | self.print(f"{T.RED}\nError: timeout !\nPlease try agine later.{T.RESET}") 409 | sys.exit() 410 | 411 | except requests.exceptions.InvalidJSONError: 412 | self.print(f"{T.RED}\nError: Server send wrong data.\nPlease try agine later.{T.RESET}") 413 | sys.exit() 414 | 415 | except Exception as e: 416 | self.print(f"{T.RED}\nRequest error: {e}\nPlease try agine later.{T.RESET}") 417 | sys.exit() 418 | 419 | 420 | def __proc_query(self, 421 | method:str, 422 | query:str, 423 | view_type:str, 424 | timeout:int, 425 | sort:bool, 426 | limit:int, 427 | yaspin:bool, 428 | callback_view_result, 429 | search_text='', 430 | err_text='' 431 | ) -> dict: 432 | Result.VIEW_TYPE = view_type 433 | 434 | result : list[Result] = [] 435 | 436 | data = { 437 | 438 | 'token' : self.token, 439 | 'type-view' : view_type, 440 | 'method' : method, 441 | 'version' : VERSION, 442 | 'query' : query, 443 | 'get-info' : 'yes' 444 | } 445 | 446 | if yaspin: 447 | with yaspin(Spinner(["🐟","🐠","🐡","🐬","🐋","🐳","🦈","🐙","🐚","🪼","🪸"], 200),text=f"Processing {query} ..." if not search_text else search_text) as sp: 448 | process_info = self.__request( 449 | data=data, 450 | timeout=timeout, 451 | ) 452 | 453 | if process_info and process_info.get('count') == 0: 454 | if not err_text: 455 | self.print(f"{T.RED}Not found result{T.RESET}", sp,ys_err=True) 456 | else: 457 | self.print(f"{T.RED}{err_text}{T.RESET}", sp,ys_err=True) 458 | 459 | 460 | sp.fail("💥 ") 461 | sp.stop() 462 | return result 463 | 464 | else: 465 | sp.ok("⚓ ") 466 | 467 | else: 468 | process_info = self.__request( 469 | data=data, 470 | timeout=timeout, 471 | ) 472 | if process_info and process_info.get('count') == 0: 473 | self.print(f"{T.RED}Not found result{T.RESET}") 474 | return result 475 | 476 | 477 | del data['get-info'] 478 | 479 | if self.err: 480 | self.err= False 481 | self.print(f'{T.RED}Error: {self.msg}{T.RESET}',ys_err=True) 482 | return 483 | 484 | if yaspin: 485 | self.print(f"{T.YELLOW}Pages count{T.YELLOW}: {T.GREEN}{process_info['pages'] if process_info['count'] != -1 else 'unknown'}{T.RESET}") 486 | 487 | data['sid'] = process_info['sid'] 488 | 489 | if yaspin: 490 | YS = yaspin(f'Get pages 0/{process_info["pages"]}').green.bold.shark #.on_black 491 | YS.start() 492 | 493 | else: 494 | YS = None 495 | 496 | for p in range(1, process_info['pages']+0x1): 497 | if yaspin:YS.text = f'Get pages {p}/{process_info["pages"]}' 498 | 499 | data['page'] = p 500 | 501 | beta_result = self.__request( 502 | data=data, 503 | timeout=timeout, 504 | ) 505 | 506 | if self.err: 507 | self.err=False 508 | if yaspin:self.print(f"{T.RED}{self.msg}{T.RESET}", YS, ys_err=True);YS.fail("💥 ");YS.stop() 509 | return result 510 | 511 | for r in beta_result['data']: 512 | 513 | column :Result = Result(**r) 514 | 515 | if sort and column in self.__result: 516 | continue 517 | 518 | if callback_view_result != None: 519 | callback_view_result(beta=column, ys=YS) 520 | 521 | result.append(column) 522 | self.__result.append(column) 523 | 524 | if len(result) == limit: 525 | if yaspin:YS.text='';YS.stop() 526 | return result 527 | 528 | if beta_result['done']: 529 | if yaspin:YS.text='';YS.stop() 530 | return result 531 | 532 | time.sleep(0x1) 533 | 534 | if not result: 535 | if yaspin:self.print(f"{T.RED}Not found result{T.RESET}", YS,ys_err=True);YS.fail("💥 ");YS.stop() 536 | else:self.print(f"{T.RED}Not found result{T.RESET}") 537 | else: 538 | if yaspin:YS.text='';YS.stop() 539 | return result 540 | 541 | def search(self, 542 | query, 543 | method='domain', 544 | view_type='full', 545 | limit=10000, 546 | timeout=60, 547 | sort=True, 548 | yaspin=False, 549 | search_text='', 550 | err_text='', 551 | callback_view_result=None) -> dict: 552 | 553 | 554 | if method not in _METHODS: 555 | raise Exception(f"{T.RED}not found this method: {method}.{T.RESET}") 556 | 557 | if method not in _VIEW_TYPE[view_type]: 558 | raise Exception(f"{T.RED}{view_type} doesn't support ({method}).{T.RESET}") 559 | 560 | 561 | self.err = False 562 | self.msg = '' 563 | result = None 564 | 565 | query = self.filter(query, method) 566 | if query == False: 567 | return 568 | 569 | if method == "domain.all": 570 | result = self.proc_all_domains( 571 | query=query, 572 | view_type=view_type, 573 | sort=sort, 574 | timeout=timeout, 575 | limit=limit, 576 | callback_view_result=callback_view_result, 577 | yaspin=yaspin, 578 | search_text=search_text, 579 | err_text=err_text, 580 | ) 581 | 582 | else: 583 | 584 | result = self.__proc_query( 585 | query=query, 586 | method=method, 587 | view_type=view_type, 588 | sort=sort, 589 | timeout=timeout, 590 | limit=limit, 591 | callback_view_result=callback_view_result, 592 | yaspin=yaspin, 593 | search_text=search_text, 594 | err_text=err_text, 595 | ) 596 | 597 | self.__result:list = [] 598 | 599 | return result 600 | 601 | class Result(dict): 602 | VIEW_TYPE = None 603 | HID_PASS = True if os.environ.get('HID_PASS') else False 604 | def __init__(self,type,**kwargs) -> None: 605 | 606 | self.kwargs = kwargs 607 | Type = type 608 | 609 | self.url = None 610 | self.urlPort = None 611 | self.urlPath = None 612 | self.credEmail = None 613 | self.credUsername = None 614 | self.credPassword = None 615 | self.credCountry = None 616 | self.credDate = None 617 | self.credPhone = None 618 | self.domain = None 619 | self.ip = None 620 | self.ipAsn = None 621 | self.ipIsp = None 622 | self.ipOrg = None 623 | self.ipPorts = None 624 | self.ipCountry = None 625 | self.appID = None 626 | self.appName = None 627 | self.appIcon = None 628 | self.appDomain = None 629 | 630 | if Type == "login": 631 | if kwargs.get('url'): 632 | self.urlPath = kwargs['url']['path'] 633 | self.urlPort = kwargs['url']['port'] 634 | self.url = self.__convert_url(kwargs['url']) 635 | 636 | if kwargs['url']['ip']: 637 | self.__convert_and_set_ip(kwargs['url']['ip']) 638 | 639 | elif kwargs['url']['domain']: 640 | self.domain = self.__convert_domain(kwargs['url']['domain']) 641 | 642 | 643 | if kwargs.get('app'): 644 | self.appID = kwargs['app']['id'] 645 | self.appName = kwargs['app']['name'] 646 | self.appIcon = kwargs['app']['icon'] 647 | if kwargs['app']['domain']: 648 | self.appDomain = self.__convert_domain(kwargs['app']['domain']) 649 | 650 | if kwargs.get('cred'): 651 | if kwargs['cred']['email']: 652 | self.credEmail = self.__convert_email(kwargs['cred']['email']) 653 | 654 | self.credUsername = kwargs['cred']['username'] 655 | self.credPassword = kwargs['cred']['password'] if not self.HID_PASS else '*'*len(kwargs['cred']['password']) 656 | if kwargs['cred']['phone']: 657 | self.credPhone = self.__convert_phone(kwargs['cred']['phone']) 658 | 659 | 660 | if kwargs.get('country'): 661 | self.credCountry = kwargs['country']['f'] 662 | 663 | self.credDate = kwargs['date'] 664 | 665 | elif Type == 'url': 666 | self.urlPath = kwargs['path'] 667 | self.urlPort = kwargs['port'] 668 | self.url = self.__convert_url(kwargs) 669 | 670 | if kwargs['ip']: 671 | self.url = self.__convert_url(kwargs) 672 | self.__convert_and_set_ip(kwargs['ip']) 673 | 674 | elif kwargs['domain']: 675 | self.domain = self.__convert_domain(kwargs['domain']) 676 | 677 | elif Type == "email": 678 | self.credEmail = self.__convert_email(kwargs) 679 | 680 | elif Type == "domain": 681 | self.domain = self.__convert_domain(kwargs) 682 | 683 | elif Type == 'app': 684 | self.appID = kwargs['id'] 685 | self.appName = kwargs['name'] 686 | self.appIcon = kwargs['icon'] 687 | 688 | if kwargs['domain']: 689 | self.domain = self.__convert_domain(kwargs['domain']) 690 | 691 | 692 | elif Type == 'ip': 693 | self.__convert_and_set_ip(kwargs) 694 | 695 | def __convert_phone(self,phone): 696 | return f"+{phone['country']['p']} {phone['number']}" 697 | 698 | def __convert_email(self,email): 699 | return f"{email['name']}@{self.__convert_domain(email['domain'])}" 700 | 701 | def __convert_and_set_ip(self,ip): 702 | self.ip = ip['ip'] 703 | self.ipAsn = ip['asn'] 704 | self.ipOrg = ip['org'] 705 | self.ipIsp = ip['isp'] 706 | self.ipPorts = ip['ports'] 707 | self.ipCountry = ip['country']['f'] if ip['country'] else None 708 | 709 | def __convert_url(self,url:dict): 710 | if url['domain']: 711 | return f"{url['proto']}://{self.__convert_domain(url['domain'])}:{url['port']}{url['path']}" 712 | elif url['ip']: 713 | return f"{url['proto']}://{url['ip']['ip']}:{url['port']}{url['path']}" 714 | 715 | 716 | return None 717 | 718 | def __convert_domain(self,domain:dict): 719 | return f"{(domain['sub']+'.') if domain['sub'] else ''}{domain['name']}{('.'+domain['suffix']) if domain['suffix'] else ''}" 720 | 721 | 722 | def __str__(self) -> str: 723 | return self.save_format() 724 | 725 | def __radd__(self, other): 726 | if isinstance(other, str): 727 | return other + self.save_format() 728 | else: 729 | return NotImplemented 730 | 731 | def __add__(self, other): 732 | if isinstance(other, str): 733 | return self.save_format() + other 734 | else: 735 | return NotImplemented 736 | 737 | def __getattr__(self, key): 738 | if key in self: 739 | return self[key] 740 | else: 741 | raise AttributeError(f"'Result' object has no attribute '{key}'") 742 | 743 | def __setattr__(self, key, value): 744 | self[key] = value 745 | 746 | 747 | def print(self,): 748 | c="" 749 | 750 | if self.VIEW_TYPE == "email" and self.credEmail: 751 | c+=f"{T.MAGENTA}[ {T.YELLOW}Email{T.MAGENTA} ]{T.MAGENTA}> {T.CYAN}{self.credEmail}{T.RESET}" 752 | 753 | if self.VIEW_TYPE == "password" and self.credPassword: 754 | c+=f"{T.MAGENTA}[ {T.YELLOW}Email{T.MAGENTA} ]{T.MAGENTA}> {T.CYAN}{self.credPassword}{T.RESET}" 755 | 756 | if self.VIEW_TYPE == "username" and self.credUsername: 757 | c+=f"{T.MAGENTA}[ {T.YELLOW}Email{T.MAGENTA} ]{T.MAGENTA}> {T.CYAN}{self.credUsername}{T.RESET}" 758 | 759 | if self.VIEW_TYPE == "app" and self.appID: 760 | if self.appID:c+=f"{T.MAGENTA}[ {T.YELLOW}APP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.CYAN}{self.appID}{T.RESET}\n" 761 | if self.appName:c+=f"{T.MAGENTA}[ {T.YELLOW}APP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.RED} Name{T.RESET}{' ':10}: {T.CYAN}{self.appName}{T.RESET}\n" 762 | if self.appIcon:c+=f"{T.MAGENTA}[ {T.YELLOW}APP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.RED} Icon{T.RESET}{' ':10}: {T.CYAN}{self.appIcon}{T.RESET}\n" 763 | if self.appDomain:c+=f"{T.MAGENTA}[ {T.YELLOW}APP{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Domain{T.RESET}{' ':8}: {T.CYAN}{self.appDomain}{T.RESET}\n" 764 | 765 | if self.VIEW_TYPE == "url" and self.url: 766 | if self.url:c+=f"{T.MAGENTA}[ {T.YELLOW}URL{T.MAGENTA} ]{T.MAGENTA}> {T.CYAN}{self.url}{T.RESET}\n" 767 | if self.urlPath:c+=f"{T.MAGENTA}[ {T.YELLOW}URL{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Path{T.RESET}{' ':10}: {T.CYAN}{self.urlPath}{T.RESET}\n" 768 | if self.urlPort:c+=f"{T.MAGENTA}[ {T.YELLOW}URL{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Port{T.RESET}{' ':10}: {T.CYAN}{self.urlPort}{T.RESET}\n" 769 | 770 | if self.VIEW_TYPE == "ip" and self.ip: 771 | if self.ip:c+=f"{T.MAGENTA}[ {T.YELLOW}IP{T.MAGENTA} ]{T.MAGENTA}> {T.BLUE}{self.ip}{T.RESET}\n" 772 | if self.ipPorts:c+=f"{T.MAGENTA}[ {T.YELLOW}IP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.RED} Ports{T.RESET}{' ':9}: {T.CYAN}{self.ipPorts}{T.RESET}\n" 773 | if self.ipAsn:c+=f"{T.MAGENTA}[ {T.YELLOW}IP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.RED} Asn{T.RESET}{' ':11}: {T.CYAN}{self.ipAsn}{T.RESET}\n" 774 | if self.ipIsp:c+=f"{T.MAGENTA}[ {T.YELLOW}IP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.RED} Isp{T.RESET}{' ':11}: {T.CYAN}{self.ipIsp}{T.RESET}\n" 775 | if self.ipOrg:c+=f"{T.MAGENTA}[ {T.YELLOW}IP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.RED} Org{T.RESET}{' ':11}: {T.CYAN}{self.ipOrg}{T.RESET}\n" 776 | if self.ipCountry:c+=f"{T.MAGENTA}[ {T.YELLOW}IP{T.MAGENTA} ]{T.MAGENTA}>{T.RED} Country{T.RESET}{' ':7}: {T.CYAN}{self.ipCountry}{T.RESET}\n" 777 | 778 | if self.VIEW_TYPE in ["domain", 'subdomain', 'related'] and self.domain: 779 | c+=f"{T.MAGENTA}[ {T.YELLOW}Domain{T.MAGENTA} ]{T.MAGENTA}> {T.CYAN}{self.domain}{T.RESET}" 780 | 781 | if self.VIEW_TYPE == "phone" and self.credPhone: 782 | return f"{T.MAGENTA}> {T.CYAN}{self.credPhone}{T.RESET}" 783 | 784 | if self.VIEW_TYPE == "cred": 785 | if self.url:c+=f"{T.MAGENTA}[ {T.YELLOW}URL{T.MAGENTA} ]{T.MAGENTA}> {T.BLUE}{self.url}{T.RESET}\n" 786 | if self.appID:c+=f"{T.MAGENTA}[ {T.YELLOW}APP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.CYAN}{self.appID}{T.RESET}\n" 787 | if self.credEmail:c+=f"{T.MAGENTA}[ {T.YELLOW}CRED{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Email{T.RESET}{' ':9}: {T.GREEN}{self.credEmail}{T.RESET}\n" 788 | if self.credUsername and not self.credEmail:c+=f"{T.MAGENTA}[ {T.YELLOW}CRED{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Username{T.RESET}{' ':6}: {T.GREEN}{self.credUsername}{T.RESET}\n" 789 | if self.credPassword:c+=f"{T.MAGENTA}[ {T.YELLOW}CRED{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Password{T.RESET}{' ':6}: {T.GREEN}{self.credPassword}{T.RESET}\n" 790 | if self.credPhone:c+=f"{T.MAGENTA}[ {T.YELLOW}CRED{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Phone{T.RESET}{' ':9}: {T.GREEN}{self.credPhone}{T.RESET}\n" 791 | if self.credCountry:c+=f"{T.MAGENTA}[ {T.YELLOW}CRED{T.MAGENTA} ]{T.MAGENTA}>{T.RED} Country{T.RESET}{' ':7}: {T.CYAN}{self.credCountry}{T.RESET}\n" 792 | 793 | if self.VIEW_TYPE == "full": 794 | if self.url:c+=f"{T.MAGENTA}[ {T.YELLOW}URL{T.MAGENTA} ]{T.MAGENTA}> {T.BLUE}{self.url}{T.RESET}\n" 795 | if self.urlPath and self.urlPath != '/':c+=f"{T.MAGENTA}[ {T.YELLOW}URL{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Path{T.RESET}{' ':10}: {T.CYAN}{self.urlPath}{T.RESET}\n" 796 | if self.urlPort and self.urlPort not in [80, 443]:c+=f"{T.MAGENTA}[ {T.YELLOW}URL{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Port{T.RESET}{' ':10}: {T.CYAN}{self.urlPort}{T.RESET}\n" 797 | if self.ip:c+=f"{T.MAGENTA}[ {T.YELLOW}IP{T.MAGENTA} ]{T.MAGENTA}> {T.BLUE}{self.ip}{T.RESET}\n" 798 | if self.ipPorts:c+=f"{T.MAGENTA}[ {T.YELLOW}IP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.RED} Ports{T.RESET}{' ':9}: {T.CYAN}{self.ipPorts}{T.RESET}\n" 799 | if self.ipAsn:c+=f"{T.MAGENTA}[ {T.YELLOW}IP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.RED} Asn{T.RESET}{' ':11}: {T.CYAN}{self.ipAsn}{T.RESET}\n" 800 | if self.ipIsp:c+=f"{T.MAGENTA}[ {T.YELLOW}IP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.RED} Isp{T.RESET}{' ':11}: {T.CYAN}{self.ipIsp}{T.RESET}\n" 801 | if self.ipOrg:c+=f"{T.MAGENTA}[ {T.YELLOW}IP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.RED} Org{T.RESET}{' ':11}: {T.CYAN}{self.ipOrg}{T.RESET}\n" 802 | if self.ipCountry:c+=f"{T.MAGENTA}[ {T.YELLOW}IP{T.MAGENTA} ]{T.MAGENTA}>{T.RED} Country{T.RESET}{' ':7}: {T.CYAN}{self.ipCountry}{T.RESET}\n" 803 | 804 | if self.appID:c+=f"{T.MAGENTA}[ {T.YELLOW}APP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.CYAN}{self.appID}{T.RESET}\n" 805 | if self.appName:c+=f"{T.MAGENTA}[ {T.YELLOW}APP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.RED} Name{T.RESET}{' ':10}: {T.CYAN}{self.appName}{T.RESET}\n" 806 | if self.appIcon:c+=f"{T.MAGENTA}[ {T.YELLOW}APP{T.MAGENTA} ]{T.RED}{T.MAGENTA}> {T.RED} Icon{T.RESET}{' ':10}: {T.CYAN}{self.appIcon}{T.RESET}\n" 807 | if self.appDomain:c+=f"{T.MAGENTA}[ {T.YELLOW}APP{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Domain{T.RESET}{' ':8}: {T.CYAN}{self.appDomain}{T.RESET}\n" 808 | 809 | if self.credEmail:c+=f"{T.MAGENTA}[ {T.YELLOW}CRED{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Email{T.RESET}{' ':9}: {T.GREEN}{self.credEmail}{T.RESET}\n" 810 | if self.credUsername and not self.credEmail:c+=f"{T.MAGENTA}[ {T.YELLOW}CRED{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Username{T.RESET}{' ':6}: {T.GREEN}{self.credUsername}{T.RESET}\n" 811 | if self.credPassword:c+=f"{T.MAGENTA}[ {T.YELLOW}CRED{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Password{T.RESET}{' ':6}: {T.GREEN}{self.credPassword}{T.RESET}\n" 812 | if self.credPhone:c+=f"{T.MAGENTA}[ {T.YELLOW}CRED{T.MAGENTA} ]{T.MAGENTA}> {T.RED} Phone{T.RESET}{' ':9}: {T.GREEN}{self.credPhone}{T.RESET}\n" 813 | if self.credCountry:c+=f"{T.MAGENTA}[ {T.YELLOW}CRED{T.MAGENTA} ]{T.MAGENTA}>{T.RED} Country{T.RESET}{' ':7}: {T.CYAN}{self.credCountry}{T.RESET}\n" 814 | 815 | return c 816 | 817 | def save_format(self): 818 | result = [] 819 | if self.VIEW_TYPE in ['cred', 'full']: 820 | # 1 821 | if self.url: 822 | result.append(self.url) 823 | elif self.appID: 824 | result.append(self.appID) 825 | else: 826 | result.append('') 827 | 828 | # 2 829 | if self.credUsername: 830 | result.append(self.credUsername) 831 | elif self.credEmail: 832 | result.append(self.credEmail) 833 | else: 834 | result.append('') 835 | 836 | # 3 837 | if self.credPassword: 838 | result.append(self.credPassword) 839 | else: 840 | result.append('') 841 | 842 | # 4 843 | if self.credCountry: 844 | result.append(self.credCountry) 845 | else: 846 | result.append('') 847 | 848 | # 5 849 | #if self.credDate: 850 | # result.append(self.credDate) 851 | #else: 852 | # result.append('') 853 | 854 | return result 855 | 856 | elif self.VIEW_TYPE in ['subdomain', 'related', 'domain']: 857 | return self.domain 858 | 859 | elif self.VIEW_TYPE == 'email': 860 | return self.credEmail 861 | 862 | elif self.VIEW_TYPE == 'phone': 863 | return self.credPhone 864 | 865 | elif self.VIEW_TYPE == 'username': 866 | return self.credUsername 867 | 868 | elif self.VIEW_TYPE == 'password': 869 | return self.credPassword 870 | 871 | 872 | elif self.VIEW_TYPE == 'ip': 873 | return self.ip 874 | 875 | elif self.VIEW_TYPE == 'app': 876 | return self.appID 877 | 878 | elif self.VIEW_TYPE == 'url': 879 | return self.url 880 | else: 881 | return 'null' 882 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tldextract==5.1.2 2 | yaspin==3.0.1 3 | argcomplete==3.3.0 4 | requests==2.31.0 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from distutils.core import setup 3 | from os import path 4 | here = path.abspath(path.dirname(__file__)) 5 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 6 | long_description = f.read() 7 | 8 | setup(name='chiasmodon', 9 | version='3.0.2', 10 | description='Chiasmodon is an OSINT tool that allows users to gather information from various sources and conduct targeted searches based on domains, Google Play applications, email addresses, IP addresses, organizations, URLs, and more. It provides comprehensive scanning capabilities, customizable output formats, and additional options for enhanced data analysis and customization.', 11 | long_description=long_description, 12 | long_description_content_type='text/markdown', 13 | author='chiasmod0n', 14 | keywords='intelligence osint credentials emails asn cidr bugbounty subdomains information-gathering intelligence-analysis reconnaissance attack-surface subdomain-enumeration reconnaissance-framework bugbounty-tool email-enumeration chiasmodon', 15 | url='https://github.com/chiasmod0n/chiasmodon', 16 | packages=['.'], 17 | scripts=['cli/chiasmodon_cli.py'], 18 | install_requires=['requests', 'yaspin', 'tldextract','argcomplete'] 19 | ) 20 | --------------------------------------------------------------------------------