Autarch Will Control The Internet
This commit is contained in:
443
modules/geoip.py
Normal file
443
modules/geoip.py
Normal file
@@ -0,0 +1,443 @@
|
||||
"""
|
||||
AUTARCH GEO IP/Domain Lookup Module
|
||||
Get geolocation info for IPs, domains, and URLs
|
||||
Based on Snoop Project's GEO_IP/domain plugin
|
||||
"""
|
||||
|
||||
import ipaddress
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from core.banner import Colors
|
||||
|
||||
# Module metadata
|
||||
NAME = "GEO IP Lookup"
|
||||
DESCRIPTION = "Get geolocation for IPs, domains, and URLs"
|
||||
AUTHOR = "darkHal Security Group"
|
||||
VERSION = "1.0"
|
||||
CATEGORY = "osint"
|
||||
|
||||
# Try to import requests
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
requests = None
|
||||
|
||||
|
||||
class GeoIPLookup:
|
||||
"""GEO IP/Domain lookup utility."""
|
||||
|
||||
def __init__(self):
|
||||
self.session = None
|
||||
self.timeout = 10
|
||||
self.user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36"
|
||||
self._init_session()
|
||||
|
||||
def _init_session(self):
|
||||
"""Initialize requests session."""
|
||||
if requests is None:
|
||||
return
|
||||
|
||||
self.session = requests.Session()
|
||||
adapter = requests.adapters.HTTPAdapter(max_retries=2)
|
||||
self.session.mount('https://', adapter)
|
||||
self.session.mount('http://', adapter)
|
||||
self.session.headers.update({'User-Agent': self.user_agent})
|
||||
|
||||
def _resolve_domain(self, target: str, timeout: int = 4) -> dict:
|
||||
"""Resolve domain to IP addresses.
|
||||
|
||||
Args:
|
||||
target: Domain name or IP address.
|
||||
timeout: Socket timeout in seconds.
|
||||
|
||||
Returns:
|
||||
Dict with resolved IPs and domain info.
|
||||
"""
|
||||
result = {
|
||||
'domain': None,
|
||||
'ipv4': None,
|
||||
'ipv6': None,
|
||||
}
|
||||
|
||||
def get_fqdn():
|
||||
try:
|
||||
result['domain'] = socket.getfqdn(target)
|
||||
except Exception:
|
||||
result['domain'] = target
|
||||
|
||||
def get_ips():
|
||||
try:
|
||||
addr_info = socket.getaddrinfo(target, 443)
|
||||
for info in addr_info:
|
||||
ip = info[4][0]
|
||||
try:
|
||||
if ipaddress.IPv4Address(ip):
|
||||
result['ipv4'] = ip
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
if ipaddress.IPv6Address(ip):
|
||||
result['ipv6'] = ip
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Run in threads with timeout
|
||||
t1 = threading.Thread(target=get_fqdn)
|
||||
t2 = threading.Thread(target=get_ips)
|
||||
t1.start()
|
||||
t2.start()
|
||||
t1.join(timeout)
|
||||
t2.join(timeout)
|
||||
|
||||
return result
|
||||
|
||||
def _parse_target(self, target: str) -> str:
|
||||
"""Parse and clean target input.
|
||||
|
||||
Args:
|
||||
target: User input (IP, domain, or URL).
|
||||
|
||||
Returns:
|
||||
Cleaned target string.
|
||||
"""
|
||||
target = target.strip()
|
||||
|
||||
# Check if it's a URL
|
||||
if '://' in target:
|
||||
parsed = urlparse(target)
|
||||
if parsed.hostname:
|
||||
target = parsed.hostname.replace('www.', '')
|
||||
elif '/' in target:
|
||||
target = target.split('/')[0]
|
||||
|
||||
return target
|
||||
|
||||
def _is_ip(self, target: str) -> bool:
|
||||
"""Check if target is an IP address."""
|
||||
try:
|
||||
ipaddress.ip_address(target)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def lookup(self, target: str) -> dict:
|
||||
"""Perform GEO IP lookup.
|
||||
|
||||
Args:
|
||||
target: IP address, domain, or URL.
|
||||
|
||||
Returns:
|
||||
Dict with geolocation information.
|
||||
"""
|
||||
if self.session is None:
|
||||
return {'error': 'requests library not available'}
|
||||
|
||||
target = self._parse_target(target)
|
||||
|
||||
# Validate input
|
||||
if not target or len(target) < 4:
|
||||
return {'error': 'Invalid target'}
|
||||
|
||||
if '..' in target:
|
||||
return {'error': 'Invalid target format'}
|
||||
|
||||
result = {
|
||||
'target': target,
|
||||
'country_code': None,
|
||||
'country': None,
|
||||
'region': None,
|
||||
'city': None,
|
||||
'latitude': None,
|
||||
'longitude': None,
|
||||
'isp': None,
|
||||
'org': None,
|
||||
'ipv4': None,
|
||||
'ipv6': None,
|
||||
'domain': None,
|
||||
'map_osm': None,
|
||||
'map_google': None,
|
||||
}
|
||||
|
||||
# Resolve domain/IP
|
||||
print(f"{Colors.CYAN}[*] Resolving target...{Colors.RESET}")
|
||||
resolved = self._resolve_domain(target)
|
||||
result['domain'] = resolved.get('domain')
|
||||
result['ipv4'] = resolved.get('ipv4')
|
||||
result['ipv6'] = resolved.get('ipv6')
|
||||
|
||||
# If target is IP, use it directly
|
||||
if self._is_ip(target):
|
||||
try:
|
||||
if ipaddress.IPv4Address(target):
|
||||
result['ipv4'] = target
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
if ipaddress.IPv6Address(target):
|
||||
result['ipv6'] = target
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Determine IP to lookup
|
||||
lookup_ip = result['ipv4'] or target
|
||||
|
||||
# Try ipwho.is first
|
||||
print(f"{Colors.CYAN}[*] Querying geolocation APIs...{Colors.RESET}")
|
||||
geo_data = self._query_ipwhois(lookup_ip)
|
||||
|
||||
if not geo_data or geo_data.get('success') is False:
|
||||
# Fallback to ipinfo.io
|
||||
geo_data = self._query_ipinfo(lookup_ip)
|
||||
|
||||
if geo_data:
|
||||
result['country_code'] = geo_data.get('country_code') or geo_data.get('country')
|
||||
result['country'] = geo_data.get('country_name') or geo_data.get('country')
|
||||
result['region'] = geo_data.get('region')
|
||||
result['city'] = geo_data.get('city')
|
||||
result['latitude'] = geo_data.get('latitude') or geo_data.get('lat')
|
||||
result['longitude'] = geo_data.get('longitude') or geo_data.get('lon')
|
||||
result['isp'] = geo_data.get('isp') or geo_data.get('org')
|
||||
result['org'] = geo_data.get('org')
|
||||
|
||||
if not result['ipv4']:
|
||||
result['ipv4'] = geo_data.get('ip')
|
||||
|
||||
# Generate map links
|
||||
if result['latitude'] and result['longitude']:
|
||||
lat, lon = result['latitude'], result['longitude']
|
||||
result['map_osm'] = f"https://www.openstreetmap.org/#map=13/{lat}/{lon}"
|
||||
result['map_google'] = f"https://www.google.com/maps/@{lat},{lon},12z"
|
||||
|
||||
return result
|
||||
|
||||
def _query_ipwhois(self, ip: str) -> dict:
|
||||
"""Query ipwho.is API.
|
||||
|
||||
Args:
|
||||
ip: IP address to lookup.
|
||||
|
||||
Returns:
|
||||
Dict with GEO data or None.
|
||||
"""
|
||||
try:
|
||||
url = f"https://ipwho.is/{ip}" if ip else "https://ipwho.is/"
|
||||
response = self.session.get(url, timeout=self.timeout)
|
||||
data = response.json()
|
||||
|
||||
if data.get('success') is False:
|
||||
return None
|
||||
|
||||
return {
|
||||
'ip': data.get('ip'),
|
||||
'country_code': data.get('country_code'),
|
||||
'country_name': data.get('country'),
|
||||
'region': data.get('region'),
|
||||
'city': data.get('city'),
|
||||
'latitude': data.get('latitude'),
|
||||
'longitude': data.get('longitude'),
|
||||
'isp': data.get('connection', {}).get('isp'),
|
||||
'org': data.get('connection', {}).get('org'),
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"{Colors.DIM} ipwho.is error: {e}{Colors.RESET}")
|
||||
return None
|
||||
|
||||
def _query_ipinfo(self, ip: str) -> dict:
|
||||
"""Query ipinfo.io API.
|
||||
|
||||
Args:
|
||||
ip: IP address to lookup.
|
||||
|
||||
Returns:
|
||||
Dict with GEO data or None.
|
||||
"""
|
||||
try:
|
||||
url = f"https://ipinfo.io/{ip}/json" if ip else "https://ipinfo.io/json"
|
||||
response = self.session.get(url, timeout=self.timeout)
|
||||
data = response.json()
|
||||
|
||||
loc = data.get('loc', ',').split(',')
|
||||
lat = float(loc[0]) if len(loc) > 0 and loc[0] else None
|
||||
lon = float(loc[1]) if len(loc) > 1 and loc[1] else None
|
||||
|
||||
return {
|
||||
'ip': data.get('ip'),
|
||||
'country_code': data.get('country'),
|
||||
'country_name': data.get('country'),
|
||||
'region': data.get('region'),
|
||||
'city': data.get('city'),
|
||||
'latitude': lat,
|
||||
'longitude': lon,
|
||||
'isp': data.get('org'),
|
||||
'org': data.get('org'),
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"{Colors.DIM} ipinfo.io error: {e}{Colors.RESET}")
|
||||
return None
|
||||
|
||||
def lookup_self(self) -> dict:
|
||||
"""Lookup your own public IP.
|
||||
|
||||
Returns:
|
||||
Dict with geolocation information.
|
||||
"""
|
||||
print(f"{Colors.CYAN}[*] Looking up your public IP...{Colors.RESET}")
|
||||
return self.lookup('')
|
||||
|
||||
def bulk_lookup(self, targets: list) -> list:
|
||||
"""Perform bulk GEO lookups.
|
||||
|
||||
Args:
|
||||
targets: List of IPs/domains to lookup.
|
||||
|
||||
Returns:
|
||||
List of result dicts.
|
||||
"""
|
||||
results = []
|
||||
for i, target in enumerate(targets):
|
||||
print(f"\n{Colors.CYAN}[{i+1}/{len(targets)}] Looking up: {target}{Colors.RESET}")
|
||||
result = self.lookup(target)
|
||||
results.append(result)
|
||||
time.sleep(0.5) # Rate limiting
|
||||
return results
|
||||
|
||||
|
||||
def display_result(result: dict):
|
||||
"""Display lookup result nicely."""
|
||||
if 'error' in result:
|
||||
print(f"{Colors.RED}[X] Error: {result['error']}{Colors.RESET}")
|
||||
return
|
||||
|
||||
print(f"\n{Colors.CYAN}{'=' * 50}{Colors.RESET}")
|
||||
print(f"{Colors.GREEN}{Colors.BOLD}Target:{Colors.RESET} {result['target']}")
|
||||
print(f"{Colors.CYAN}{'=' * 50}{Colors.RESET}")
|
||||
|
||||
if result['ipv4']:
|
||||
print(f" {Colors.GREEN}IPv4:{Colors.RESET} {result['ipv4']}")
|
||||
if result['ipv6']:
|
||||
print(f" {Colors.GREEN}IPv6:{Colors.RESET} {result['ipv6']}")
|
||||
if result['domain'] and result['domain'] != result['target']:
|
||||
print(f" {Colors.GREEN}Domain:{Colors.RESET} {result['domain']}")
|
||||
|
||||
print()
|
||||
|
||||
if result['country_code']:
|
||||
country_str = f"{result['country_code']}"
|
||||
if result['country'] and result['country'] != result['country_code']:
|
||||
country_str += f" ({result['country']})"
|
||||
print(f" {Colors.GREEN}Country:{Colors.RESET} {country_str}")
|
||||
|
||||
if result['region']:
|
||||
print(f" {Colors.GREEN}Region:{Colors.RESET} {result['region']}")
|
||||
if result['city']:
|
||||
print(f" {Colors.GREEN}City:{Colors.RESET} {result['city']}")
|
||||
if result['isp']:
|
||||
print(f" {Colors.GREEN}ISP:{Colors.RESET} {result['isp']}")
|
||||
|
||||
if result['latitude'] and result['longitude']:
|
||||
print(f"\n {Colors.GREEN}Coordinates:{Colors.RESET} {result['latitude']}, {result['longitude']}")
|
||||
|
||||
if result['map_osm']:
|
||||
print(f"\n {Colors.DIM}OpenStreetMap: {result['map_osm']}{Colors.RESET}")
|
||||
if result['map_google']:
|
||||
print(f" {Colors.DIM}Google Maps: {result['map_google']}{Colors.RESET}")
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def display_menu():
|
||||
"""Display the GEO IP module menu."""
|
||||
print(f"""
|
||||
{Colors.CYAN} GEO IP/Domain Lookup{Colors.RESET}
|
||||
{Colors.DIM} Get geolocation for IPs, domains, and URLs{Colors.RESET}
|
||||
{Colors.DIM}{'─' * 50}{Colors.RESET}
|
||||
|
||||
{Colors.GREEN}[1]{Colors.RESET} Lookup IP/Domain/URL
|
||||
{Colors.GREEN}[2]{Colors.RESET} Lookup My IP
|
||||
{Colors.GREEN}[3]{Colors.RESET} Bulk Lookup from File
|
||||
|
||||
{Colors.RED}[0]{Colors.RESET} Back to OSINT Menu
|
||||
""")
|
||||
|
||||
|
||||
def run():
|
||||
"""Main entry point for the module."""
|
||||
if requests is None:
|
||||
print(f"{Colors.RED}[X] This module requires 'requests' library{Colors.RESET}")
|
||||
print(f"{Colors.DIM} Install with: pip install requests{Colors.RESET}")
|
||||
input(f"\n{Colors.DIM}Press Enter to continue...{Colors.RESET}")
|
||||
return
|
||||
|
||||
lookup = GeoIPLookup()
|
||||
|
||||
while True:
|
||||
display_menu()
|
||||
choice = input(f"{Colors.GREEN}Select option: {Colors.RESET}").strip()
|
||||
|
||||
if choice == '0':
|
||||
break
|
||||
|
||||
elif choice == '1':
|
||||
print(f"\n{Colors.CYAN}Enter IP, domain, or URL:{Colors.RESET}")
|
||||
print(f"{Colors.DIM}Examples: 8.8.8.8, google.com, https://example.com/path{Colors.RESET}")
|
||||
target = input(f"\n{Colors.GREEN}Target: {Colors.RESET}").strip()
|
||||
|
||||
if not target:
|
||||
continue
|
||||
|
||||
result = lookup.lookup(target)
|
||||
display_result(result)
|
||||
input(f"{Colors.DIM}Press Enter to continue...{Colors.RESET}")
|
||||
|
||||
elif choice == '2':
|
||||
result = lookup.lookup_self()
|
||||
display_result(result)
|
||||
input(f"{Colors.DIM}Press Enter to continue...{Colors.RESET}")
|
||||
|
||||
elif choice == '3':
|
||||
print(f"\n{Colors.CYAN}Enter path to file with targets (one per line):{Colors.RESET}")
|
||||
filepath = input(f"\n{Colors.GREEN}File path: {Colors.RESET}").strip()
|
||||
|
||||
if not filepath or not os.path.exists(filepath):
|
||||
print(f"{Colors.RED}[X] File not found{Colors.RESET}")
|
||||
continue
|
||||
|
||||
try:
|
||||
with open(filepath, 'r') as f:
|
||||
targets = [line.strip() for line in f if line.strip()]
|
||||
|
||||
if not targets:
|
||||
print(f"{Colors.RED}[X] No targets found in file{Colors.RESET}")
|
||||
continue
|
||||
|
||||
print(f"{Colors.GREEN}[+] Found {len(targets)} targets{Colors.RESET}")
|
||||
confirm = input(f"\n{Colors.YELLOW}Proceed with lookup? (y/n): {Colors.RESET}").strip().lower()
|
||||
|
||||
if confirm == 'y':
|
||||
results = lookup.bulk_lookup(targets)
|
||||
for result in results:
|
||||
display_result(result)
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Colors.RED}[X] Error reading file: {e}{Colors.RESET}")
|
||||
|
||||
input(f"\n{Colors.DIM}Press Enter to continue...{Colors.RESET}")
|
||||
|
||||
else:
|
||||
print(f"{Colors.RED}[!] Invalid option{Colors.RESET}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
Reference in New Issue
Block a user