Autarch/modules/yandex_osint.py

327 lines
11 KiB
Python
Raw Permalink Normal View History

"""
AUTARCH Yandex OSINT Module
Gather information about Yandex users from their login, email, or public links
"""
import json
import os
import sys
import webbrowser
from pathlib import Path
# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))
from core.banner import Colors
# Module metadata
NAME = "Yandex OSINT"
DESCRIPTION = "Gather intel from Yandex user accounts"
AUTHOR = "darkHal Security Group"
VERSION = "1.0"
CATEGORY = "osint"
# Try to import requests
try:
import requests
except ImportError:
requests = None
class YandexParser:
"""Parser for Yandex user information."""
def __init__(self):
self.session = None
self.timeout = 10
self.user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) 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 lookup_by_login(self, login: str) -> dict:
"""Lookup Yandex user by login/email.
Args:
login: Yandex login or email.
Returns:
Dict with user information.
"""
# Strip domain from email
login = login.split('@')[0].strip()
if not login:
return {'error': 'Invalid login'}
result = {
'login': login,
'email': f"{login}@yandex.ru",
'display_name': None,
'public_id': None,
'avatar_url': None,
'profiles': {},
}
print(f"{Colors.CYAN}[*] Looking up Yandex user: {login}{Colors.RESET}")
# Query Yandex Collections API
try:
url = f"https://yandex.ru/collections/api/users/{login}/"
response = self.session.get(url, timeout=self.timeout)
if response.status_code == 200:
data = response.json()
if data.get('title') == "404 Not Found":
result['error'] = 'User not found'
return result
result['display_name'] = data.get('display_name')
result['public_id'] = data.get('public_id')
avatar_id = data.get('default_avatar_id')
if avatar_id:
result['avatar_url'] = f"https://avatars.mds.yandex.net/get-yapic/{avatar_id}/islands-300"
# Build profile URLs
pub_id = result['public_id']
if pub_id:
result['profiles'] = {
'reviews': f"https://reviews.yandex.ru/user/{pub_id}",
'market': f"https://market.yandex.ru/user/{pub_id}/reviews",
'dzen': f"https://zen.yandex.ru/user/{pub_id}",
'qa': f"https://yandex.ru/q/profile/{pub_id}/",
}
result['profiles']['music'] = f"https://music.yandex.ru/users/{login}/tracks"
result['profiles']['disk'] = f"https://disk.yandex.ru/client/disk"
print(f"{Colors.GREEN}[+] User found!{Colors.RESET}")
elif response.status_code == 404:
result['error'] = 'User not found'
else:
result['error'] = f'API error: {response.status_code}'
except requests.exceptions.RequestException as e:
result['error'] = f'Network error: {str(e)}'
except json.JSONDecodeError:
result['error'] = 'Invalid API response'
except Exception as e:
result['error'] = f'Error: {str(e)}'
return result
def lookup_by_disk_link(self, url: str) -> dict:
"""Extract user info from Yandex.Disk public link.
Args:
url: Public Yandex.Disk link.
Returns:
Dict with user information.
"""
print(f"{Colors.CYAN}[*] Extracting user from Yandex.Disk link...{Colors.RESET}")
try:
response = self.session.get(url, timeout=self.timeout)
if response.status_code != 200:
return {'error': 'Failed to fetch disk link'}
# Extract displayName from page
try:
login = response.text.split('displayName":"')[1].split('"')[0]
except (IndexError, AttributeError):
return {'error': 'Could not extract user from link'}
if not login:
return {'error': 'No user found in link'}
print(f"{Colors.GREEN}[+] Extracted login: {login}{Colors.RESET}")
return self.lookup_by_login(login)
except Exception as e:
return {'error': f'Error: {str(e)}'}
def lookup_by_public_id(self, public_id: str) -> dict:
"""Lookup user by Yandex public ID.
Args:
public_id: 26-character Yandex user identifier.
Returns:
Dict with user information.
"""
if len(public_id) != 26:
return {'error': 'Invalid public ID (must be 26 characters)'}
result = {
'public_id': public_id,
'profiles': {
'reviews': f"https://reviews.yandex.ru/user/{public_id}",
'market': f"https://market.yandex.ru/user/{public_id}/reviews",
'dzen': f"https://zen.yandex.ru/user/{public_id}",
'qa': f"https://yandex.ru/q/profile/{public_id}/",
}
}
print(f"{Colors.CYAN}[*] Looking up public ID: {public_id}{Colors.RESET}")
# Try to get more info from collections API
try:
url = f"https://yandex.ru/collections/api/users/{public_id}/"
response = self.session.get(url, timeout=self.timeout)
if response.status_code == 200:
data = response.json()
if data.get('title') != "404 Not Found":
result['display_name'] = data.get('display_name')
avatar_id = data.get('default_avatar_id')
if avatar_id:
result['avatar_url'] = f"https://avatars.mds.yandex.net/get-yapic/{avatar_id}/islands-300"
except Exception:
pass
print(f"{Colors.GREEN}[+] Profile URLs generated!{Colors.RESET}")
return result
def display_result(result: dict, open_browser: bool = False):
"""Display lookup result nicely.
Args:
result: Lookup result dict.
open_browser: Whether to open URLs in browser.
"""
if 'error' in result:
print(f"{Colors.RED}[X] {result['error']}{Colors.RESET}")
return
print(f"\n{Colors.CYAN}{'=' * 55}{Colors.RESET}")
print(f"{Colors.GREEN}{Colors.BOLD} YANDEX USER PROFILE{Colors.RESET}")
print(f"{Colors.CYAN}{'=' * 55}{Colors.RESET}")
if result.get('display_name'):
print(f" {Colors.GREEN}Name:{Colors.RESET} {result['display_name']}")
if result.get('login'):
print(f" {Colors.GREEN}Login:{Colors.RESET} {result['login']}")
if result.get('email'):
print(f" {Colors.GREEN}Email:{Colors.RESET} {result['email']}")
if result.get('public_id'):
print(f" {Colors.GREEN}Public ID:{Colors.RESET} {result['public_id']}")
if result.get('avatar_url'):
print(f"\n {Colors.GREEN}Avatar:{Colors.RESET}")
print(f" {Colors.DIM}{result['avatar_url']}{Colors.RESET}")
profiles = result.get('profiles', {})
if profiles:
print(f"\n {Colors.GREEN}Yandex Services:{Colors.RESET}")
for name, url in profiles.items():
print(f" {Colors.CYAN}{name.title()}:{Colors.RESET} {url}")
if open_browser:
try:
webbrowser.open(url)
except Exception:
pass
print()
def display_menu():
"""Display the Yandex OSINT module menu."""
print(f"""
{Colors.CYAN} Yandex OSINT{Colors.RESET}
{Colors.DIM} Gather intelligence from Yandex user accounts{Colors.RESET}
{Colors.DIM}{'' * 55}{Colors.RESET}
{Colors.GREEN}[1]{Colors.RESET} Lookup by Login/Email
{Colors.GREEN}[2]{Colors.RESET} Lookup by Yandex.Disk Public Link
{Colors.GREEN}[3]{Colors.RESET} Lookup by Public ID (26-char hash)
{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
parser = YandexParser()
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 Yandex login or email:{Colors.RESET}")
print(f"{Colors.DIM}Example: username or username@yandex.ru{Colors.RESET}")
login = input(f"\n{Colors.GREEN}Login: {Colors.RESET}").strip()
if not login:
continue
result = parser.lookup_by_login(login)
open_links = input(f"\n{Colors.YELLOW}Open profile links in browser? (y/n): {Colors.RESET}").strip().lower()
display_result(result, open_browser=(open_links == 'y'))
input(f"{Colors.DIM}Press Enter to continue...{Colors.RESET}")
elif choice == '2':
print(f"\n{Colors.CYAN}Enter Yandex.Disk public link:{Colors.RESET}")
print(f"{Colors.DIM}Example: https://yadi.sk/d/xxxxx{Colors.RESET}")
url = input(f"\n{Colors.GREEN}URL: {Colors.RESET}").strip()
if not url:
continue
result = parser.lookup_by_disk_link(url)
open_links = input(f"\n{Colors.YELLOW}Open profile links in browser? (y/n): {Colors.RESET}").strip().lower()
display_result(result, open_browser=(open_links == 'y'))
input(f"{Colors.DIM}Press Enter to continue...{Colors.RESET}")
elif choice == '3':
print(f"\n{Colors.CYAN}Enter Yandex public ID (26 characters):{Colors.RESET}")
print(f"{Colors.DIM}Example: tr6r2c8ea4tvdt3xmpy5atuwg0{Colors.RESET}")
pub_id = input(f"\n{Colors.GREEN}Public ID: {Colors.RESET}").strip()
if not pub_id:
continue
result = parser.lookup_by_public_id(pub_id)
open_links = input(f"\n{Colors.YELLOW}Open profile links in browser? (y/n): {Colors.RESET}").strip().lower()
display_result(result, open_browser=(open_links == 'y'))
input(f"{Colors.DIM}Press Enter to continue...{Colors.RESET}")
else:
print(f"{Colors.RED}[!] Invalid option{Colors.RESET}")
if __name__ == "__main__":
run()