Created
May 4, 2026 10:07
-
-
Save swarupsro/1d4fb8c8448b10a187f393eb9c0392bf to your computer and use it in GitHub Desktop.
RevserseIP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| import requests | |
| import socket | |
| import curses | |
| import sys | |
| import math | |
| class Colors: | |
| GREEN = '\033[92m' | |
| CYAN = '\033[96m' | |
| YELLOW = '\033[93m' | |
| RED = '\033[91m' | |
| RESET = '\033[0m' | |
| BOLD = '\033[1m' | |
| SUSPICIOUS_KEYWORDS = [ | |
| "cpanel", "webmail", "mail", "ftp", "admin", "login", | |
| "test", "dev", "uat", "staging", "backup", "old", | |
| "portal", "vpn", "remote", "db", "database", "phpmyadmin", | |
| "wordpress", "wp-admin", "api" | |
| ] | |
| def get_ip(domain): | |
| try: | |
| return socket.gethostbyname(domain) | |
| except Exception as e: | |
| print(f"{Colors.RED}[!] Error resolving domain: {e}{Colors.RESET}") | |
| sys.exit(1) | |
| def reverse_ip_lookup(ip): | |
| url = f"https://api.hackertarget.com/reverseiplookup/?q={ip}" | |
| try: | |
| response = requests.get(url, timeout=15) | |
| return response.text | |
| except Exception as e: | |
| print(f"{Colors.RED}[!] API request failed: {e}{Colors.RESET}") | |
| sys.exit(1) | |
| def prepare_domains(data): | |
| if "error" in data.lower(): | |
| return [] | |
| return sorted(set([d.strip() for d in data.strip().splitlines() if d.strip()])) | |
| def is_suspicious(domain): | |
| domain_lower = domain.lower() | |
| return any(keyword in domain_lower for keyword in SUSPICIOUS_KEYWORDS) | |
| def get_domain_color(domain): | |
| d = domain.lower() | |
| if is_suspicious(d): | |
| return 4 # Red | |
| if d.endswith(".gov") or ".gov." in d: | |
| return 5 # Magenta | |
| if d.endswith(".edu") or ".edu." in d or d.endswith(".ac.bd"): | |
| return 6 # Blue | |
| if d.endswith(".com") or ".com." in d: | |
| return 2 # Green | |
| if d.endswith(".bd"): | |
| return 7 # White | |
| return 2 # Green | |
| def get_column_count(width): | |
| if width >= 150: | |
| return 3 | |
| elif width >= 90: | |
| return 2 | |
| return 1 | |
| def build_rows(domains, width): | |
| columns = get_column_count(width) | |
| total = len(domains) | |
| if total == 0: | |
| return [[None]] | |
| rows = math.ceil(total / columns) | |
| table = [] | |
| for row in range(rows): | |
| line_items = [] | |
| for col in range(columns): | |
| index = row + (col * rows) | |
| if index < total: | |
| line_items.append((index + 1, domains[index])) | |
| else: | |
| line_items.append(None) | |
| table.append(line_items) | |
| return table | |
| def draw_domain_item(stdscr, y, x, number, domain, col_width): | |
| number_text = f"{number:03}" | |
| arrow_text = " -> " | |
| domain_text = domain[:max(1, col_width - 8)] | |
| try: | |
| stdscr.addstr(y, x, number_text, curses.color_pair(3) | curses.A_BOLD) | |
| x += len(number_text) | |
| stdscr.addstr(y, x, arrow_text, curses.color_pair(1)) | |
| x += len(arrow_text) | |
| domain_color = get_domain_color(domain) | |
| attr = curses.color_pair(domain_color) | |
| if is_suspicious(domain): | |
| attr |= curses.A_BOLD | |
| stdscr.addstr(y, x, domain_text, attr) | |
| except curses.error: | |
| pass | |
| def curses_ui(stdscr, domain, ip, domains): | |
| curses.curs_set(0) | |
| stdscr.nodelay(False) | |
| stdscr.keypad(True) | |
| curses.start_color() | |
| curses.use_default_colors() | |
| curses.init_pair(1, curses.COLOR_CYAN, -1) # Arrow / info | |
| curses.init_pair(2, curses.COLOR_GREEN, -1) # .com / normal | |
| curses.init_pair(3, curses.COLOR_YELLOW, -1) # Number | |
| curses.init_pair(4, curses.COLOR_RED, -1) # Suspicious | |
| curses.init_pair(5, curses.COLOR_MAGENTA, -1) # .gov | |
| curses.init_pair(6, curses.COLOR_BLUE, -1) # .edu / .ac.bd | |
| curses.init_pair(7, curses.COLOR_WHITE, -1) # .bd | |
| curses.init_pair(8, curses.COLOR_BLACK, curses.COLOR_YELLOW) # Highlight label | |
| scroll_pos = 0 | |
| search_query = "" | |
| while True: | |
| stdscr.clear() | |
| height, width = stdscr.getmaxyx() | |
| filtered = [d for d in domains if search_query.lower() in d.lower()] | |
| rows = build_rows(filtered, width) | |
| columns = get_column_count(width) | |
| col_width = max(35, width // columns) | |
| header1 = "Reverse IP Lookup Tool" | |
| header2 = f"Domain: {domain} | IP: {ip} | Total: {len(domains)} | Showing: {len(filtered)} | Columns: {columns}" | |
| header3 = f"Filter: {search_query if search_query else 'None'}" | |
| help_line = "Keys: Up/Down Scroll | / Search | Backspace Clear | q Quit" | |
| legend = "Legend: RED=Suspicious | MAGENTA=.gov | BLUE=.edu/.ac.bd | GREEN=.com/normal | WHITE=.bd" | |
| stdscr.addstr(0, 0, header1[:width-1], curses.color_pair(2) | curses.A_BOLD) | |
| stdscr.addstr(1, 0, header2[:width-1], curses.color_pair(3)) | |
| stdscr.addstr(2, 0, header3[:width-1], curses.color_pair(1)) | |
| stdscr.addstr(3, 0, help_line[:width-1], curses.color_pair(4)) | |
| stdscr.addstr(4, 0, legend[:width-1], curses.color_pair(1)) | |
| available_height = height - 6 | |
| max_scroll = max(0, len(rows) - available_height) | |
| if scroll_pos > max_scroll: | |
| scroll_pos = max_scroll | |
| visible_rows = rows[scroll_pos:scroll_pos + available_height] | |
| for row_index, row_items in enumerate(visible_rows): | |
| y = row_index + 6 | |
| if row_items == [None]: | |
| stdscr.addstr(y, 0, "No domains found.", curses.color_pair(4)) | |
| continue | |
| for col_index, item in enumerate(row_items): | |
| if item is None: | |
| continue | |
| number, domain_name = item | |
| x = col_index * col_width | |
| draw_domain_item(stdscr, y, x, number, domain_name, col_width) | |
| stdscr.refresh() | |
| key = stdscr.getch() | |
| if key == ord("q"): | |
| break | |
| elif key == curses.KEY_DOWN: | |
| scroll_pos = min(scroll_pos + 1, max_scroll) | |
| elif key == curses.KEY_UP: | |
| scroll_pos = max(scroll_pos - 1, 0) | |
| elif key == ord("/"): | |
| curses.echo() | |
| curses.curs_set(1) | |
| stdscr.addstr(5, 0, "Search: ".ljust(width-1), curses.color_pair(3)) | |
| stdscr.refresh() | |
| search_query = stdscr.getstr(5, 8, 80).decode(errors="ignore").strip() | |
| scroll_pos = 0 | |
| curses.noecho() | |
| curses.curs_set(0) | |
| elif key in [curses.KEY_BACKSPACE, 127, 8]: | |
| search_query = "" | |
| scroll_pos = 0 | |
| def main(): | |
| print(f"{Colors.BOLD}{Colors.GREEN}Reverse IP Lookup Tool{Colors.RESET}") | |
| domain = input(f"{Colors.YELLOW}Enter Domain: {Colors.RESET}").strip() | |
| if not domain: | |
| print(f"{Colors.RED}[!] Domain cannot be empty{Colors.RESET}") | |
| sys.exit(1) | |
| ip = get_ip(domain) | |
| print(f"{Colors.CYAN}Resolved IP:{Colors.RESET} {ip}") | |
| print(f"{Colors.YELLOW}Fetching reverse IP results...{Colors.RESET}") | |
| data = reverse_ip_lookup(ip) | |
| domains = prepare_domains(data) | |
| if not domains: | |
| print(f"{Colors.RED}[!] No valid domains found or API error.{Colors.RESET}") | |
| print(data) | |
| sys.exit(1) | |
| curses.wrapper(curses_ui, domain, ip, domains) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment