Как найти сайты на одном хостинге

Бесплатный OnLine сервис позволяет быстро найти все сайты (домены) на одном хостинге с одним IP-адресом. Посмотреть подробную информацию об IP-адресе – местоположение IP-адреса с точностью до города, Whois-сервис, а так-же узнать включен ли IP-адрес в Единый реестр запрещённых сайтов.

Сервис поддерживает IDN-домены и для их проверки не требуется переводить имя домена в Punycode. Просто введите адрес вашего кириллического домена и проверьте все сайты на IP-адресе.

Инструменты для веб-мастеров и оптимизаторов

Профессиональные OnLine инструменты и сервисы для веб-мастеров и оптимизаторов. Все, что необходимо для успешного развития сайта.

Whois-сервис

Бесплатный OnLine сервис для проверки доменов позволяет проверить домен на занятость и узнать регистрационную информацию.

Проверка IP-адреса

Сервис позволяет определять положение российских и украинских IP-адресов с точностью до города.

Узнать IP-адрес сайта

IP-адрес сайта или сервера, сервис определяет местонахождение сайта а также информацию о провайдере.

Просмотр HTTP-заголовков

Следите за корректностью HTTP-заголовков. Одна из причин некорректной индексации сайта — неправильный ответ веб-сервера в ответ на запрос поискового робота.

Проверка Last-Modified

Правильно настроенный сервер должен возвращать код 304 Not Modified, если клиент запросил документ методом GET, использовал заголовок If-Modified-Since или If-None-Match и документ не изменился с указанного момента.

Редактор HTML кода

Бесплатный визуальный редактор HTML кода. Удобен для перевода документов MS Word в HTML-код, а также схожих задач или OnLine-редактирования.

Шифрование HTML кода

Шифрование HTML кода позволяет зашифровать любой HTML код включая JavaScript так, чтобы никто не смог прочитать исходный код страницы сайта.

Форматирование HTML кода

С помощью данного инструмента Вы можете легко сделать сжатый HTML код красивым и легко читаемым.

Валидатор HTML кода

HTML-валидатор W3C — бесплатный OnLine сервис для проверки синтаксиса HTML и XHTML-кода на наличие ошибок.

Генерация пароля

Генератор паролей — бесплатный OnLine инструмент для создания случайных паролей всего за пару кликов.

Шифрование MD5

Зашифруйте любой текст используя алгоритм шифрования MD5.

Онлайн сервисы на основе HackWare.ru


Составление диапазонов IP

  • Географические единицы и их IP адреса
  • Все IP населённого пункта (области, города)
  • Все IP страны
  • Все IP провайдера
  • Все IP континента
  • Все IP Автономной Системы (AS)
  • Диапазоны IP-адресов всех Интернет-провайдеров и организаций

Узнать информацию о себе

  • Узнать свой IP
  • Узнать свой User Agent
  • Проверка на раскрытие реального IP адреса за прокси
  • Трассировка до меня
  • Есть ли у меня IPv6

Сбор информации

  • Узнать информацию об IP
  • Узнать информацию об IPv6
  • whois об IP или о сайте
  • Определение системы управления сайтом (CMS)
  • Разнообразная информация о сайтах
  • Похожие доменные имена
  • Сайты на одном IP
  • Узнать IP сайта
  • История IP адресов сайта
  • Посмотреть все DNS записи сайта
  • Просмотр определённых DNS записей сайта
  • HTTP заголовки ответа
  • Определить куда ведёт ссылка
  • Проверка, где «засветился» адрес электронной почты
  • Поиск профилей по нику пользователя
  • Поиск профилей по полным именам и другой информации
  • Проверка зарегистрированных доменов в разных зонах
  • Преобразовать IP адрес в имя хоста
  • Получение номера Автономной Системы по IP адресу
  • Поиск пользователя в социальных сетях
  • OSINT инструмент сбора данных о сайтах
  • Просмотр .onion сайтов (скрытых сервисов) из Даркнета
  • Сканер NetBIOS, SMB и Samba
  • Проверка доступности сайтов через Tor

Извлечение информации из кэша и веб архивов

  • Показ страницы из кэша Гугл
  • Одновременный поиск по нескольким веб архивам

IP калькуляторы

  • Калькулятор IP подсетей
  • Калькулятор IPv6 подсетей
  • Конвертер IP адресов
  • Агрегатор IP адресов, масок, подсетей
  • Диапазоны сетей в CIDR
  • Разбивка подсети по маске
  • Разбивка подсети по количеству IP
  • Двоичный IP в стандартный вид
  • Преобразование в двоичный вид
  • Десятичный IP в нормальную форму
  • Конвертация IP в десятичную форму
  • Шестнадцатеричный IP в нормальную форму
  • Конвертация IP в шестнадцатеричное число
  • Преобразование восьмеричного числа в IP
  • Конвертация IP в восьмеричное число
  • Разжатие IPv6
  • Конвертация в CIDR и агрегация диапазонов IP
  • Конвертация CIDR нотации в диапазоны IP

Обход запрета показа исходного HTML кода, обход социальных блокировщиков

  • Сервис обхода блокировок просмотра исходного кода страницы
  • Обход социальных блокировщиков

Продвинутое использование поисковых систем

  • Продвинутый поиск в Гугл
  • Продвинутый поиск в Яндекс

Работа с кодировками

  • Определить кодировку и преобразовать в читаемый вид
  • Кодирование файлов и строк в Base64
  • Декодирование файлов и строк из Base64

Инструменты противодействия CloudFlare

  • Проверка сайт на CloudFlare
  • Раскрытие реального IP сайта за сетью CloudFlare, используя неправильно настроенные DNS и базу данных старых записей
  • Раскрытие реального IP сайта за Cloudflare, Incapsula, SUCURI и другими WAF

Изображения и метаданные

  • Узнать где сделана фотография
  • Метаданные файла
  • Очистка метаданных в фотографиях
  • Очистка метаданных в офисных и других файлах
  • Универсальный конвертер изображений
  • Просмотр уровня качества JPG (степень сжатия изображения)
  • Конвертация PDF в JPG
  • Конвертация JPG в PDF
  • Скриншот веб-сайта
  • Проверка веб камеры

Информация о номерах телефонов

  • Узнать оператора и город по номеру
  • Узнать все номера телефонов оператора и города
  • Международные телефонные коды

Сканеры уязвимостей веб-сайтов

  • Сканирование WordPress на уязвимости
  • Проверка сайта на sql инъекции
  • Проверка Drupal, SilverStripe и WordPress

Сканеры уязвимостей, открытых портов и запущенных служб веб-серверов

  • Сканирование сервера (Nikto)
  • Сканирование открытых портов и запущенных служб онлайн (Nmap)
  • Продвинутые сканирования Nmap онлайн
  • Сканирование портов на IPv6 адресах
  • Тестирование шифрования TLS/SSL
  • Сканер заголовков ответа HTTP на безопасность
  • Определение WAF (файервола веб приложений)

Субдомены и скрытые файлы

  • Поиск всех субдоменов сайта
  • Поиск поддоменов сайта (второй метод)
  • Мгновенный поиск субдоменов (поддоменов) любого сайта (третий метод)

Получение информации по MAC-адресам

  • Определение производителя устройства по MAC-адресу

Анализ работы веб-сервера

  • Анализ логов Apache
  • Пинг IP и IPv6 адресов
  • Трассировка IP, IPv6 и веб-сайтов

Работа с хешами

  • Определение типа хеша
  • Генератор NTLM хешей

Анализ электронных писем

  • Извлечение всей информации из e-mail письма

Анализ файлов

  • Определение типа файла без расширения
  • Извлечение всех строк из исполнимого файла
  • Показать информацию об исполнимом файле
  • Извлечение содержимого из .DS_Store

Конвертация

  • Конвертация географических координат из десятичного вида в градусы, минуты и секунды
  • Конвертация географических координат из градусов в десятичный вид

QR-коды

  • Генератор QR-кода для текста
  • Генератор QR-кода для координат
  • Генератор QR-кода с номером телефона
  • Генератор QR-кода с адресом сайта или файлом для загрузки
  • SMS-сообщение в QR коде
  • E-mail в QR коде
  • Генератор QR кода для подключения к Wi-Fi
  • QR код для добавления контакта в адресную книгу
  • QR код для добавления события в календарь

Служебные страницы

  • Использование ресурсов сервера
  • Поиск по базе IP без очистки
  • Донат

Лучший хостинг Рунета:

  • Заказать хостинг
  • Регистрация доменов
  • Тарифы на хостинг

Данный сервис позволяет вам бесплатно узнать все сайты на одном IP. Введите доменное имя сайта или IP. Информация получается из двух источников, поэтому выводится два списка. Информация поступает от третьих лиц, её полнота и достоверность не гарантируются.

Вас также может заинтересовать следующий материал:

  • Сбор информации о владельце сайта. Поиск сайтов одного лица

Примеры:

  • w-e-b.site
  • prostolinux.ru
  • 5.45.80.175

Нередко на одном веб-сервере бывает расположено несколько сайтов. Данный сервис поможет определить, какие именно сайты размещены вместе с интересующим доменом или по определенному IP адресу. Для этого в поле адреса просто введите имя домена либо его IP адрес, и нажмите кнопку “Выполнить”.

URL домена  

Капча: обновить текст

  (код, изображенный на картинке)

Ограничение: 5 раз в сутки для 1 IP-адреса.
Лимит проверок для Вашего IP на сегодня исчерпан. Зарегистрируйтесь в системе, чтобы снять ограничение…

Baltic Digital Days 2023

2022-09-12_18-07.png

Зачастую на одном веб-сервере, особенно, если это крупный хостинг провайдер, может быть расположено множество виртуальных хостов. А значит, на одном IP-адресе могут соседствовать большое количество сайтов. Причины, по которым необходимо получить информацию о сайтах, которые расположены на одном IP, могут быть разные. Иногда это просто любопытство. Ведь интересно же, куда можно попасть, если попробовать вбить не адрес сайта, а его IP. Иногда данная информация может быть полезна во время пентеста, когда производится первоначальный сбор информации. Идея тут в том, что разные веб-приложения могут использовать разное программное обеспечение. Таким образом, если получается выяснить, что на тестируемом IP расположено еще несколько сайтов, можно попробовать найти уязвимость на другом сайте, если не получается на целевом. То есть, найти «слабое звено», тем самым есть вероятность того, что будет скомпрометирован весь сервер. Или это может помочь в атаке на целевой ресурс. Давайте с помощью Python создадим скрипт, с помощью которого попытаемся определить домены на одном IP-адресе, если они, конечно же, там присутствуют. И домен расположен на виртуальном хостинге.

Дисклеймер: Все данные, предоставленные в данной статье, взяты из открытых источников, не призывают к действию и являются только лишь данными для ознакомления, и изучения механизмов используемых технологий.

Что потребуется?

Давайте начнем с библиотек, которые необходимо установить для работы скрипта. Нам понадобится установить requests bs4 lxml. С их помощью мы будем делать запросы к разнообразным сайтам, а также получать информацию из результатов парсинга страниц. Для их установки пишем в терминале:

pip install requests bs4 lxml

Так же понадобятся две библиотеки, с помощью которых можно получить информацию WHOIS о домене и ip адресе домена. Это библиотеки python-whois и ipwhois. Для их установки используйте следующую команду:

pip install python-whois PySocks ipwhois

Для того, чтобы проверять доступность домена воспользуемся библиотекой ping3. Устанавливаем в терминале:

pip install ping3

И еще нам понадобится библиотека selenium и надстройка над данной библиотекой — selenium_stealth. С их помощью мы будем парсить поисковую выдачу Bing, которая касается получения информации о доменах на одном ip-адресе. Пишем в терминале для установки:

pip install selenium selenium_stealth

Ключ API от Virustotal

Так же, для работы скрипта необходимо получить ключ доступа к API на сайте Virustotal. Ничего сложного в этом нет. Идем на

Ссылка скрыта от гостей

, авторизуемся или регистрируемся. У меня получилось авторизоваться с учетной записью Google. После этого заходим в личный кабинет, в раздел «API key».

01.png

Здесь, в разделе вы увидите поле, в котором за звездочками будет скрываться ваш API key. Копируем его.

02.png

После того, как ключ будет скопирован, можно создать файл .py, я назвал его api_total.py и поместить в него скопированный ключ:

api_total = 'ваш_ключ'

Его мы будем импортировать в скрипт для получения информации. Для чего нужно получать ключ Virustotal? Все достаточно просто. Для того, чтобы получить информацию по одному IP-адресу и располагающимся на нем доменам, нужно собрать достаточно большую статистику. Именно такая статистика есть у Virustotal, так как пользователи отправляют на проверку самые различные сайты, страницы и домены. Ну и Virustotal с помощью API делится данной статистикой с тем, кто ее запросит. При бесплатном использовании вам будет доступно 500 запросов в день и 15500 запросов в месяц.

После того, как библиотеки будут установлены и получен ключ, можно импортировать необходимые библиотеки в скрипт. Для скрипта я создал файл с именем domaintoip.py.

Python:

import json
import os
import socket
import threading
import time
from ipaddress import ip_address, ip_network
from platform import system

import requests
import whois
from bs4 import BeautifulSoup
from ipwhois import IPWhois
from ping3 import ping
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium_stealth import stealth

from api_total import api_total

Теперь пропишем опции для браузера Chrome с использованием веб-драйвера Selenium, а также определим заголовки для запросов, которые будут выполнятся с помощью requests.

Python:

options = Options()
options.add_argument("--headless")
options.add_argument("start-maximized")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)

headers = {
    'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 '
                  'Safari/537.36',
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,'
              'application/signed-exchange;v=b3;q=0.9 '
}

Двигаемся дальше. Определим глобальные переменные, в которые будут помещаться различные значения во время работы скрипта. DOMAIN — тип множество. Сюда будут помещаться все найденные домены. И для того, чтобы исключить повторы, мы и будем его использовать, так как в множестве не может храниться двух одинаковых значений. CHECK — тип множество. Сюда мы будем складывать тоже домены, но уже после того, как выполним их проверку на доступность и соответствие искомому IP-адресу. ADDR_INFO — словарь, в который мы будем помещать значения полученные во время работы скрипта. Такие как информация о хостинг-провайдере, информация WHOIS, ну и в конце, после всех проверок, сюда же поместим информацию о найденных доменах. LIST_ADDR — тип список. Сюда мы поместим диапазоны IP-адресов, которые принадлежат Cloudflare. Определим переменную executable_path, она нужна здесь для того, чтобы поместить ее в область видимости функций.

Определим версию операционной системы, а следовательно, в зависимости от этого присвоим переменной executable_path путь в драйверу selenium.

Python:

DOMAIN = set()
CHECK = set()
ADDR_INFO = dict()
LIST_ADDR = []

executable_path = None

if system() == "Windows":
    executable_path = os.path.join(os.getcwd(), 'chromedriver', 'chromedriver.exe')
elif system() == "Linux":
    executable_path = os.path.join(os.getcwd(), 'chromedriver', 'chromedriver')

Загрузка диапазона адресов Cloudflare

Создадим функцию, которая будет загружать диапазоны адресов Cloudflare со страницы и помещать данные диапазоны в список. Я назвал ее load_range(). Так как LIST_ADDR определен глобально, мы можем сразу же присвоит полученный результат после того, как разобьем на строки в эту переменную. А также добавим еще один диапазон, которого нет на странице.

Python:

def load_range():
    """
    Загрузка диапазонов адресов Cloudflare.
    Помещение их в глобальный список диапазонов.
    """
    global LIST_ADDR

    print('n[*] Получение диапазона адресов Cloudflare')
    LIST_ADDR = requests.get(url='https://www.cloudflare.com/ips-v4').text.splitlines()
    LIST_ADDR.append("104.16.0.0/12")

Получение IP-адреса по доменному имени

Создадим функцию get_ip(domain: str). На вход она получает доменное имя, для которого будет получать IP-адрес. Данная функция будет использоваться не только при определении первоначального адреса домена, но и в дальнейшем, с ее помощью мы будем определять IP-адреса найденных доменов, для того, чтобы сравнить их с искомым IP. Для чего это нужно? Дело в том, что информация которая получается с сайта Viurstotal могла устареть. А следовательно, домен может сменить IP-адрес. Может вовсе прекратить свое существование. Вот для этого и нужна проверка. Используем для этого функцию socket.gethostbyname, в которую передадим в качестве параметра доменное имя. Она преобразует доменное имя в формат IPv4 адреса и возвращает в виде строки. Если же получить (преобразовать) доменное имя не удалось, обработаем исключение и вернем False.

Python:

def get_ip(domain: str):
    """
    Выполняется получение ip-адреса по имени домена.
    Если адрес не найден, завершаем работу скрипта.
    Если найден, передаем его в функцию для проверки на
    принадлежность адреса к диапазону Cloudflare.
    :param domain: домен для получения ip-адреса по его имени.
    """
    try:
        ip = socket.gethostbyname(domain)
        return ip
    except socket.gaierror:
        return False

Определение принадлежности IP-адреса к CDN Cloudflare

Создадим функцию cloud_detect(ip: str, domain: str). На входе она принимает ip-адрес. Его мы будем проверять на принадлежность к CDN, а также переменную domain, в которой содержится доменное имя, по которому мы определяли ip-адрес. Оно нужно для передачи его в другую функцию, которая будет вызвана после окончания проверки.

Для чего нужно проверять ip-адрес? Дело в том, что с помощью данного скрипта мы пытаемся получить информацию о хостинг-провайдере домена. Соответственно, если адрес принадлежит Cloudflare, определять хостинг-провайдера не имеет смысла.

Создаем цикл, в котором пробегаемся по диапазонам адресов содержащихся в глобальной переменной LIST_ADDR. Проверяем с помощью функций ip_address и ip_network, входит ли адрес в какой-либо из диапазонов. Функция ip_address возвращает объект IPv4Address. То есть, мы проверяем уже не строку, а объект. Функция ip_network возвращает объект IPv4Network. С ее помощью создается сеть с диапазоном адресов, которые переданы в нее переменной в виде cidr.

Таким образом, если адрес принадлежит диапазону, добавляем значение в словарь, и запускаем функцию domain_on_ip. Если же адрес не принадлежит к диапазону адресов Cloudflare, запускаем функцию whois_domain(domain, ip), в которую передаем доменное имя и ip-адрес, для получения доступной информации.

Python:

def cloud_detect(ip: str, domain: str):
    """
    Проверка ip-адреса на принадлежность к CDN Cloudflare.
    Если адрес не принадлежит диапазону, передача имени домена
    в функцию whois_domain() для получения информации о хостинге.
    :param ip: адрес для проверки.
    :param domain: домен для передачи в функцию whois_domain().
    :return: выход из функции.
    """
    global LIST_ADDR
    print('n[*] Проверка Cloudflare')
    for addr in LIST_ADDR:
        if ip_address(ip) in ip_network(addr):
            print('[**] Cloudflare обнаружен')
            ADDR_INFO[ip] = {"Cloudflare": "True"}
            domain_on_ip(ip)
            return
    print('[-] Cloudflare не обнаружен')
    whois_domain(domain, ip)
    return

Получение информации о хостинг-провайдере

Создадим функцию hosting_provider(ip: str). На вход она принимает ip-адрес в виде строки. С помощью данной функции мы получим информацию о хостинг провайдере. Именно для этих целей мы устанавливали библиотеку ipwhois. Создадим объект класса IPWhois, в который передадим ip-адрес. Теперь, как написано у разработчика данной библиотеки, используем для поиска информации функцию lookup_rdap и определим метод поиска asn. По умолчанию, если не указан метод, используется поиск по всем типам ‘dns’, ‘whois’, ‘http’.

asn = IPWhois(ip).lookup_rdap(asn_methods=["whois"])

После того, как информация будет получена, добавляем ее в словарь ADDR_INFO. Однако, добавляем не всю информацию, а только те значения ключей, которые содержат информацию о хостинг-провайдере, и информацию о диапазоне адресов, ему принадлежащих. Также выводим данную информацию в терминал. И обрабатываем исключение в случае неудачи.

Python:

def hosting_provider(ip: str):
    """
    Получение информации о хостинг-провайдере, диапазоне адресов
    хостинг-провайдера по ip-адресу домена.
    :param ip: ip-адрес домена.
    :return: имя хостинг-провайдера, диапазон адресов хостинг-провайдера.
    """
    try:
        asn = IPWhois(ip).lookup_rdap(asn_methods=["whois"])
        ADDR_INFO[ip].update({'Хостинг-провайдер': asn['asn_description'],
                              'Диапазон адресов провайдера': asn['asn_cidr']})
        print(f'n[+] Хостинг-провайдер: {asn["asn_description"]}n[+] Диапазон адресов провайдера: {asn["asn_cidr"]}')
    except Exception:
        return

Получаем информацию о домене

Для того, чтобы получить информацию о домене создадим функцию whois_domain(domain: str, ip: str). На вход она принимает домен, по которому нужно получить информацию и ip-адрес, который нужен будет для передачи в следующую функцию.
Для начала вызовем функцию, с помощью которой получим информацию о хостинг-провайдере.

hosting_provider(ip)

Затем создадим словарь, в котором содержатся значения ключей для замены в ответе на запрос.

Python:

list_field = {"domain_name": "Доменное имя", "registrar": "Регистратор", "updated_date": "Дата обновления",
                  "creation_date": "Дата создания", "expiration_date": "Дата истечения срока регистрации",
                  "name_servers": "Серверы NS", "dnssec": "dnssec", "name": "Владелец", "org": "Организация",
                  "address": "Адрес", "city": "Город", "state": "Штат", "zipcode": "Индекс", "country": "Страна"}

Создадим словарь, в который будем помещать найденные значения. Так как они для каждого домена могут различаться. В том смысле, что некоторых значений ключей в ответе может не быть. И выполним запрос.

Python:

    whois_dict = dict()
    wh = whois.whois(domain, flags=0)

Создаем цикл, в котором будем итерироваться по созданному нами ранее словарю. Забирать из него ключи и проверять, не равно ли значение ключа в ответе None. Если нет, забираем даты, добавляем их в словарь, а также все найденные значения. Почему даты проверяются отдельно? Дело в том, что в полученных значениях может содержаться не просто дата, а их список. И при добавлении данных дат в том виде, в котором они находятся в ответе, даты получаются в не особо читаемом формате. Именно поэтому мы каждое значение, в котором содержится дата, проверяем отдельно.

Python:

    for field in list_field:
        if wh.get(field) is not None:
            try:
                if field == 'updated_date':
                    date = [str(dat) for dat in wh.get(field)]
                    if str(wh[field]) not in whois_dict:
                        whois_dict[list_field[field]] = date
                elif field == 'creation_date':
                    date = [str(dat) for dat in wh.get(field)]
                    if str(wh[field]) not in whois_dict:
                        whois_dict[list_field[field]] = date
                elif field == 'expiration_date':
                    date = [str(dat) for dat in wh.get(field)]
                    if str(wh[field]) not in whois_dict:
                        whois_dict[list_field[field]] = date
                else:
                    if str(wh[field]) not in whois_dict:
                        whois_dict[list_field[field]] = wh[field]
            except TypeError:
                if str(wh[field]) not in whois_dict:
                    whois_dict[list_field[field]] = str(wh[field])

После того, как мы переберем все значения в ответе, проверяем длину словаря, в который мы складывали то, что нашли. Если длина больше 0, будем считать, что информация получена и добавим значения в результирующий глобальный словарь. Выведем в терминал содержимое словаря и запустим функцию domain_on_ip, в которой будем получать информацию от Virustotal. Если же словарь пуст, добавляем в глобальный словарь ADDR_INFO информацию о том, что WHOIS не получен и также запускаем функцию domain_on_ip.

Python:

    if len(whois_dict) > 0:
        print(f'[**] Информация WHOIS получена:n{"-" * 31}')
        ADDR_INFO[ip].update(whois_dict)
        for item in whois_dict:
            print(f'  - {item}: {whois_dict[item]}')
        domain_on_ip(ip)
    else:
        ADDR_INFO[ip].update({'WHOIS': None})
        print('[-] Информация WHOIS не получена')
        domain_on_ip(ip)

Python:

def whois_domain(domain: str, ip: str):
    """
    Получаем информацию о домене. Добавляем полученную
    информацию в словарь. Если словарь не пуст, сохраняем
    информацию в текстовый файл.
    Вызываем функцию получения информации о доменах на ip-адресе.
    :param domain: домен для получения информации о домене.
    :param ip: адрес домена для передачи в функцию запроса доменов
    на ip-адресе.
    """
    hosting_provider(ip)
    print('n[*] Получение информации WHOIS')
    list_field = {"domain_name": "Доменное имя", "registrar": "Регистратор", "updated_date": "Дата обновления",
                  "creation_date": "Дата создания", "expiration_date": "Дата истечения срока регистрации",
                  "name_servers": "Серверы NS", "dnssec": "dnssec", "name": "Владелец", "org": "Организация",
                  "address": "Адрес", "city": "Город", "state": "Штат", "zipcode": "Индекс", "country": "Страна"}

    whois_dict = dict()
    wh = whois.whois(domain, flags=0)
    for field in list_field:
        if wh.get(field) is not None:
            try:
                if field == 'updated_date':
                    date = [str(dat) for dat in wh.get(field)]
                    if str(wh[field]) not in whois_dict:
                        whois_dict[list_field[field]] = date
                elif field == 'creation_date':
                    date = [str(dat) for dat in wh.get(field)]
                    if str(wh[field]) not in whois_dict:
                        whois_dict[list_field[field]] = date
                elif field == 'expiration_date':
                    date = [str(dat) for dat in wh.get(field)]
                    if str(wh[field]) not in whois_dict:
                        whois_dict[list_field[field]] = date
                else:
                    if str(wh[field]) not in whois_dict:
                        whois_dict[list_field[field]] = wh[field]
            except TypeError:
                if str(wh[field]) not in whois_dict:
                    whois_dict[list_field[field]] = str(wh[field])

    if len(whois_dict) > 0:
        print(f'[**] Информация WHOIS получена:n{"-" * 31}')
        ADDR_INFO[ip].update(whois_dict)
        for item in whois_dict:
            print(f'  - {item}: {whois_dict[item]}')
        domain_on_ip(ip)
    else:
        ADDR_INFO[ip].update({'WHOIS': None})
        print('[-] Информация WHOIS не получена')
        domain_on_ip(ip)

Получение информации о доменах на ip-адресе от Virustotal

Создадим функцию domain_on_ip(ip: str). На вход она получает ip-адрес для поиска. Определяем параметры API запроса, в которые помещаем ip-адрес и apikey. Затем выполняем запрос по специальному адресу, в котором передаем также параметры. В ответ мы получим json с информацией о доменах.

Python:

    param = {'ip': ip, 'apikey': api_total}
    resp = requests.get(url='https://www.virustotal.com/vtapi/v2/ip-address/report', params=param).json()

Проверяем ключ «response_code», который содержит значение 1 или 0. 1 — информация получена, 0 — не получена. Если значение равно 1, двигаемся дальше и в цикле пробегаемся по всем значениям ключа «resolutions», в котором забираем значение ключа «hostname». Именно в этом ключе содержится информация о домене. Добавляем все найденные домены в глобальное множество DOMAIN. Если же домены не найдены, сообщаем об этом и запускаем, в любом случае, функцию поиска информации об ip в поисковой системе Bing.

Python:

    if resp['response_code'] == 1:
        print('[**] Домены найдены')
        for dom in resp['resolutions']:
            DOMAIN.add(dom['hostname'])
    else:
        print('[-] Домены не найдены.')
    get_bing(ip)

Python:

def domain_on_ip(ip: str):
    """
    Выполняется запрос для получения информации о доменах,
    которые имеют один и тот же ip-адрес и находятся в базе
    проверок Virustotal. Для выполнения запроса нужно получить
    ключ API предварительно зарегистрировав аккаунт и скопировав
    его в личном кабинете.
    :param ip: адрес для запроса к API Virustotal.
    """
    print(f'n[*] Запрос доменов на IP (Virustotal): {ip}')
    param = {'ip': ip, 'apikey': api_total}
    resp = requests.get(url='https://www.virustotal.com/vtapi/v2/ip-address/report', params=param).json()

    if resp['response_code'] == 1:
        print('[**] Домены найдены')
        for dom in resp['resolutions']:
            DOMAIN.add(dom['hostname'])
    else:
        print('[-] Домены не найдены.')
    get_bing(ip)

Поиск информации об IP в поисковой системе Bing

Выбор данной поисковой системы не случаен. В ней поддерживается поисковый оператор ip, с помощью которого можно попробовать найти домены или сайты на одном ip-адресе. Оператор имеет следующие параметры: «ip:<ip-адрес>». Запрос просто выдернут из поисковой строки. В нем мы меняем ip, а также страницы с результатами поиска. На каждой странице выводится по 10 результатов. Итерируемся по 5 страницам, так как дальше просматривать страницы не имеет большого смысла. Очень много повторов, а зачастую, если страниц по запросу мало, выводится одна и та же информация с последней страницы.

Для того, чтобы Bing нас не заблокировал, а также, чтобы забрать содержимое страницы, которая является динамической, будем использовать веб-драйвер selenium с браузером Google Chrome. Конечно, хорошо было бы использовать Firefox, но, так как мы будем использовать надстройку selenium_stealth, то про выбор браузера можно забыть. Так как стелс работает только с Chrome.

Создаем объект браузера и передаем в него параметры стелс.

Python:

    browser = webdriver.Chrome(options=options, service=Service(log_path=os.devnull, executable_path=executable_path))

    stealth(driver=browser,
            user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                       'Chrome/83.0.4103.53 Safari/537.36',
            languages=["ru-RU", "ru"],
            vendor="Google Inc.",
            platform="Win32",
            webgl_vendor="Intel Inc.",
            renderer="Intel Iris OpenGL Engine",
            fix_hairline=True,
            run_on_insecure_origins=True,
            )

Теперь определим два счетчика. Один, count, мы будем использовать в информационных целях, для того, чтобы сообщать пользователю, какую страницу мы в данный момент парсим. И второй, num, с помощью которого мы будем осуществлять переходы по страницам запроса.

Python:

    print('n[*] Поиск информации о доменах в BING')
    num = 1
    count = 1

Создадим цикл while, выходом из которого будет служить превышение значения числа num. То есть, в данном случае, если num больше 51, что соответствует 5 странице, выходим из цикла. Ну, а далее все просто. Переходим на страницу в безголовом режиме, ждем секунду, чтобы прогрузилась информация, после чего, загружаем содержимое страницы в BeautifulSoup, ищем ссылки на сайты, добавляем найденное в глобальное множество DOMAIN. Ну, и соответственно, после того, как поиск выполнится, выходим из цикла и закрываем браузер.

Python:

    while num < 51:
        print(f'r  - Получаю информацию со страницы: {count}', end='')
        browser.get(f'https://www.bing.com/search?q=ip%3a+'
                    f'{ip}&qs=HS&sc=1-0&cvid=E3A8381DCD48463B9D4FBFFC24B7C9B3&sp=1&first={num}&FORM=PERE')
        time.sleep(1)

        soup = BeautifulSoup(browser.page_source, 'lxml')
        links = soup.find('ol', id='b_results').find_all('li', class_='b_algo')
        for lin in links:
            DOMAIN.add(lin.find('div', class_='b_title').find('h2').find('a')['href'].split("/")[2])
        num += 10
        count += 1
        time.sleep(2)
    browser.quit()

Python:

def get_bing(ip: str):
    """
    Запрос в поиске Bing с параметром ip.
    Этот параметр позволяет находить в поиске домены
    на данном адресе. Итерируются 10 страниц поиска.
    Найденный домены сохраняются в глобальное множество
    DOMAIN.
    :param ip: адрес для запроса в поиске Bing.
    """
    browser = webdriver.Chrome(options=options, service=Service(log_path=os.devnull, executable_path=executable_path))

    stealth(driver=browser,
            user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                       'Chrome/83.0.4103.53 Safari/537.36',
            languages=["ru-RU", "ru"],
            vendor="Google Inc.",
            platform="Win32",
            webgl_vendor="Intel Inc.",
            renderer="Intel Iris OpenGL Engine",
            fix_hairline=True,
            run_on_insecure_origins=True,
            )

    print('n[*] Поиск информации о доменах в BING')
    num = 1
    count = 1
    while num < 51:
        print(f'r  - Получаю информацию со страницы: {count}', end='')
        browser.get(f'https://www.bing.com/search?q=ip%3a+'
                    f'{ip}&qs=HS&sc=1-0&cvid=E3A8381DCD48463B9D4FBFFC24B7C9B3&sp=1&first={num}&FORM=PERE')
        time.sleep(1)

        soup = BeautifulSoup(browser.page_source, 'lxml')
        links = soup.find('ol', id='b_results').find_all('li', class_='b_algo')
        for lin in links:
            DOMAIN.add(lin.find('div', class_='b_title').find('h2').find('a')['href'].split("/")[2])
        num += 10
        count += 1
        time.sleep(2)
    browser.quit()

Проверка доменов на доступность и соответствие искомому IP

Создадим функцию check_domain(dom: str, ip: str). На вход она принимает доменное имя и ip-адрес изначально определенный для домена, по которому проводится поиск. Для чего нужна данная проверка? Дело в том, что информация, которую мы получаем от Virustotal содержит множество доменных имен. Но, так как со временем домены могут попросту закрыться или у них изменится ip-адрес, информация не будет соответствовать текущей действительности.

Для проверки доступности домена будем использовать функцию ping из библиотеки ping3. Данная функция с помощью протокола ICMP делает запрос на домен и обрабатывает полученный ответ. Если ответ получен, возвращается время ответа в секундах. Если нет, функция возвращает None. Пишем условие, если получен ответ, двигаемся дальше. Запускаем цикл по списку, в котором содержатся наименования протоколов. Так как нам необходимо будет проверить ответ от сайта на одном из них. Конечно же, большинство сайтов в настоящее время работают по протоколу https, но, встречаются сайты, и таких довольно большое количество, протокол которых все еще http. Поэтому проверяем на обоих протоколах.

Python:

    if ping(dom):
        print(f'r  - Проверка: {dom}', end='')
        for pref in ['https://', 'http://']:

Делаем запрос и если ответ получен и нас не выбросило в исключение, получаем ip-адрес домена. Сравниваем его с эталонным, то есть изначальным. Если они равны, добавляем в результирующее множество CHECK. Ну, а если сработало исключение, пробуем выполнить запрос по следующему протоколу.

Python:

            try:
                requests.head(f'{pref}{dom}', headers=headers, timeout=3)
                if get_ip(dom) == ip:
                    CHECK.add(dom)
            except (requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout):
                continue

Python:

def check_domain(dom: str, ip: str):
    """
    Проверка домена на существование. Если домен отвечает,
    выполняем запрос и пытаемся получить заголовки. Если заголовки
    получены, добавляем в множество CHECK.
    :param ip: ip-адрес для проверки текущего ip домена.
    :param dom: доменное имя для проверки на валидность.
    :return: выход из функции.
    """
    if ping(dom):
        print(f'r  - Проверка: {dom}', end='')
        for pref in ['https://', 'http://']:
            try:
                requests.head(f'{pref}{dom}', headers=headers, timeout=3)
                if get_ip(dom) == ip:
                    CHECK.add(dom)
            except (requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout):
                continue

Так как доменов может быть довольно большое количество, проверка их, одного за другим, может длится довольно длительное время. Поэтому, создадим функцию thread_check(ip), которая на входе получает ip-адрес искомого домена, для передачи его в функцию проверки доменов. В данной функции мы будем запускать потоки для проверки доменов.

Создадим список, в который поместим потоки. В цикле проитерируемся по глобальному множеству DOMAIN. Создадим поток, который определим как daemon. Это означает, что данный поток автоматически остановит выполнение после завершения задачи. То есть, будет являться неблокирующим. Запустим его и добавим в список. И так для каждого домена. Поставим небольшую паузу между созданием потоков. Ну, а далее будем перебирать список потоков, пока каждый из них не завершит свою работу.

Python:

def thread_check(ip):
    """
    Запуск потоков для выполнения запросов для проверки доменов
    на существование.
    """
    threads = []
    for dom in DOMAIN:
        t = threading.Thread(target=check_domain, kwargs={'dom': dom, 'ip': ip}, daemon=True)
        t.start()
        threads.append(t)
        time.sleep(0.02)
    for thread in threads:
        thread.join()

Сохранение полученных значений

После того, как выполнятся все основные задачи, необходимо сохранить полученные значения в файлы. Так как мы создавали глобальный словарь ADDR_INFO, который содержит всю полученную нами информацию, для начала сохраним ее в файл json. А затем пробежимся по ключам данного словаря и сохраним информацию уже в текстовый документ.

Создадим функцию save_domain(domain: str). На входе она получает начальное имя домена, по которому был произведен поиск. Оно здесь нужно для того, чтобы присвоить соответствующее имя файлам. Для начала сохраняем глобальный словарь в файл json.

Python:

    with open(os.path.join(os.getcwd(), f'result_{domain}.json'), 'w', encoding='utf-8') as inf:
        json.dump(ADDR_INFO, inf, indent=4, ensure_ascii=False)

Затем создаем цикл, в котором итерируемся по ключам глобального словаря ADDR_INFO. Открываем файла на запись, в режиме «a», то есть append, чтобы не перезаписывать информацию, а добавлять ее в конец файла. Записываем первую строку.

Python:

    for it in ADDR_INFO:
        with open(os.path.join(os.getcwd(), f'result_{domain}.txt'), 'a', encoding='utf-8') as file:
            file.write(f'[+] Информация о хостинг-провайдере:n{"-" * 36}n')

Создадим еще один цикл, в котором будем итерироваться по ключам, которые находятся на уровень ниже. Проверяем, не является ли имя ключа «domains», так как в нем содержится список доменов, по которому нужно проитерироваться для сохранения. В терминал все найденные домены выводить не будем, а выведем только информацию о их количестве. И запишем в текстовый файл.

Python:

            for itm in ADDR_INFO[it].keys():
                if itm == 'domains':
                    file.write(f'n[+] Найдена информация о доменах:n{"-" * 34}n')
                    print(f'nn[+] Найдена информация о {len(ADDR_INFO[it][itm])} доменах')
                    for dom in ADDR_INFO[it][itm]:
                        file.write(f'{dom}n')
                else:
                    file.write(f'{itm}: {ADDR_INFO[it][itm]}n')

Python:

def save_domain(domain: str):
    """
    Сохраняем результат поисков в json и текстовый документ.
    :param domain: имя домена для сохранения результата.
    """
    with open(os.path.join(os.getcwd(), f'result_{domain}.json'), 'w', encoding='utf-8') as inf:
        json.dump(ADDR_INFO, inf, indent=4, ensure_ascii=False)

    for it in ADDR_INFO:
        with open(os.path.join(os.getcwd(), f'result_{domain}.txt'), 'a', encoding='utf-8') as file:
            file.write(f'[+] Информация о хостинг-провайдере:n{"-" * 36}n')
            for itm in ADDR_INFO[it].keys():
                if itm == 'domains':
                    file.write(f'n[+] Найдена информация о доменах:n{"-" * 34}n')
                    print(f'nn[+] Найдена информация о {len(ADDR_INFO[it][itm])} доменах')
                    for dom in ADDR_INFO[it][itm]:
                        file.write(f'{dom}n')
                else:
                    file.write(f'{itm}: {ADDR_INFO[it][itm]}n')
    print(f'[+] Информация сохранена в файлы: result_{domain}.txt, result_{domain}.json')

Обработка полученного домена и запуск функций

Создадим функцию main(). Для начала запросим у пользователя доменное имя, информацию о котором нужно будет получить. Добавим его в глобальное множество. Запишем первую строку в текстовый файл с именем домена. Здесь же будем обрабатывать исключения. Так как при попытке записи в виде https: будет вызвана ошибка. А значит пользователь ввел домен с http или https.

Python:

    domain = input('Введите доменное имя: ')
    DOMAIN.add(domain)

    try:
        with open(os.path.join(os.getcwd(), f'result_{domain}.txt'), 'w', encoding='utf-8') as file:
            file.write(f'Информация о доменах на ip-адресе домена: {domain}nn')
    except (FileNotFoundError, OSError):
        print('Введите доменное имя')
        return

Запускаем функцию загрузки диапазона адресов Cloudflare.

load_range()

Получаем ip-адрес и создаем в словаре ADDR_INFO словарь с ключом равным ip.

Python:

    print('n[*] Получение IP-адреса')
    ip = get_ip(domain)
    if ip:
        print(f'[**] IP-адрес получен: {ip}')
        ADDR_INFO[ip] = dict()
    else:
        print('[-] Невозможно получить IP-адрес')
        exit(0)

Запускаем функцию проверки на принадлежность IP к диапазону Cloudflare.

cloud_detect(ip, domain)

Далее, по цепочке, будут запускаться все остальные функции. И когда обработка адреса завершится, выполним проверку длины глобального множества DOMAIN. Если она не равна 0, значит информация получена и доступна для обработки. Запускаем потоки для проверки доменных имен.

Python:

    if len(DOMAIN) > 0:
        print('nn[*] Проверка доменов:')
        thread_check(ip)
    else:
        print('[-] Информация о доменах не найдена')

После того, как отработают потоки, проверяем длину глобального списка CHECK. Так как мало ли, может быть при проверке будут отсеяны все домены. Если длина множества больше 0, добавляем информацию о найденных доменах в глобальный словарь ADDR_INFO. Тут множество нужно перевести в список, чтобы не было ошибок при сохранении json. И запускаем функцию сохранения данных.

Python:

    if len(CHECK) > 0:
        ADDR_INFO[ip].update({'domains': list(CHECK)})
        save_domain(domain)
    else:
        print('n[-] Информация найдена, но домены не отвечают на запросы')

Ну и собственно условие, в котором и будет запускаться весь скрипт.

Python:

if __name__ == "__main__":
    main()

Python:

def main():
    """
    Запрос имени домена для поиска информации.
    Запуск функции для загрузки диапазона адресов
    Cloudflare.
    Запуск функции для получения ip-адреса домена.
    Проверка не является ли множество пустым и запуск функции
    проверки доменов на существование.
    Запуск функции сохранения результатов.
    """
    domain = input('Введите доменное имя: ')
    DOMAIN.add(domain)

    try:
        with open(os.path.join(os.getcwd(), f'result_{domain}.txt'), 'w', encoding='utf-8') as file:
            file.write(f'Информация о доменах на ip-адресе домена: {domain}nn')
    except (FileNotFoundError, OSError):
        print('Введите доменное имя')
        return

    load_range()

    print('n[*] Получение IP-адреса')
    ip = get_ip(domain)
    if ip:
        print(f'[**] IP-адрес получен: {ip}')
        ADDR_INFO[ip] = dict()
    else:
        print('[-] Невозможно получить IP-адрес')
        exit(0)

    cloud_detect(ip, domain)
    if len(DOMAIN) > 0:
        print('nn[*] Проверка доменов:')
        thread_check(ip)
    else:
        print('[-] Информация о доменах не найдена')
    if len(CHECK) > 0:
        ADDR_INFO[ip].update({'domains': list(CHECK)})
        save_domain(domain)
    else:
        print('n[-] Информация найдена, но домены не отвечают на запросы')


if __name__ == "__main__":
    main()

Для примера, выполним проверку по домену proglib.io, так как этот домен является показательным примером, когда не используется виртуальный хостинг. Ну, или просто не находится доменов ))

03.png

Думаю, никакой конфиденциальной информации раскрыто не будет. Потому публикую данный скриншот. Здесь, как видно ниже, найдено всего два домена. А точнее домен и субдомен, которые хостятся на Selectel. Однако, Cloudflare здесь также используется, но не прячет реальный IP.

Python:

"""
Скрипт для поиска доменов на одном ip-адресе.
Для работы требует установки следующих модулей:
pip install python-whois PySocks requests bs4 lxml ping3 selenium selenium_stealth ipwhois

Для выполнения запросов к поисковому сервису Bing используется selenium_stealth,
который пытается скрыть факт использования автоматического выполнения запросов
от поисковой системы для того, чтобы можно было парсить результаты.
"""
import json
import os
import socket
import threading
import time
from ipaddress import ip_address, ip_network
from platform import system

import requests
import whois
from bs4 import BeautifulSoup
from ipwhois import IPWhois
from ping3 import ping
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium_stealth import stealth

from api_total import api_total

options = Options()
options.add_argument("--headless")
options.add_argument("start-maximized")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)

headers = {
    'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 '
                  'Safari/537.36',
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,'
              'application/signed-exchange;v=b3;q=0.9 '
}

DOMAIN = set()
CHECK = set()
ADDR_INFO = dict()
LIST_ADDR = []

executable_path = None

if system() == "Windows":
    executable_path = os.path.join(os.getcwd(), 'chromedriver', 'chromedriver.exe')
elif system() == "Linux":
    executable_path = os.path.join(os.getcwd(), 'chromedriver', 'chromedriver')


def load_range():
    """
    Загрузка диапазонов адресов Cloudflare.
    Помещение их в глобальный список диапазонов.
    """
    global LIST_ADDR

    print('n[*] Получение диапазона адресов Cloudflare')
    LIST_ADDR = requests.get(url='https://www.cloudflare.com/ips-v4').text.splitlines()
    LIST_ADDR.append("104.16.0.0/12")


def get_ip(domain: str):
    """
    Выполняется получение ip-адреса по имени домена.
    Если адрес не найден, завершаем работу скрипта.
    Если найден, передаем его в функцию для проверки на
    принадлежность адреса к диапазону Cloudflare.
    :param domain: домен для получения ip-адреса по его имени.
    """
    try:
        ip = socket.gethostbyname(domain)
        return ip
    except socket.gaierror:
        return False


def cloud_detect(ip: str, domain: str):
    """
    Проверка ip-адреса на принадлежность к CDN Cloudflare.
    Если адрес не принадлежит диапазону, передача имени домена
    в функцию whois_domain() для получения информации о хостинге.
    :param ip: адрес для проверки.
    :param domain: домен для передачи в функцию whois_domain().
    :return: выход из функции.
    """
    global LIST_ADDR
    print('n[*] Проверка Cloudflare')
    for addr in LIST_ADDR:
        if ip_address(ip) in ip_network(addr):
            print('[**] Cloudflare обнаружен')
            ADDR_INFO[ip] = {"Cloudflare": "True"}
            domain_on_ip(ip)
            return
    print('[-] Cloudflare не обнаружен')
    whois_domain(domain, ip)
    return


def hosting_provider(ip: str):
    """
    Получение информации о хостинг-провайдере, диапазоне адресов
    хостинг-провайдера по ip-адресу домена.
    :param ip: ip-адрес домена.
    :return: имя хостинг-провайдера, диапазон адресов хостинг-провайдера.
    """
    try:
        asn = IPWhois(ip).lookup_rdap(asn_methods=["whois"])
        ADDR_INFO[ip].update({'Хостинг-провайдер': asn['asn_description'],
                              'Диапазон адресов провайдера': asn['asn_cidr']})
        print(f'n[+] Хостинг-провайдер: {asn["asn_description"]}n[+] Диапазон адресов провайдера: {asn["asn_cidr"]}')
    except Exception:
        return


def whois_domain(domain: str, ip: str):
    """
    Получаем информацию о домене. Добавляем полученную
    информацию в словарь. Если словарь не пуст, сохраняем
    информацию в текстовый файл.
    Вызываем функцию получения информации о доменах на ip-адресе.
    :param domain: домен для получения информации о домене.
    :param ip: адрес домена для передачи в функцию запроса доменов
    на ip-адресе.
    """
    hosting_provider(ip)
    print('n[*] Получение информации WHOIS')
    list_field = {"domain_name": "Доменное имя", "registrar": "Регистратор", "updated_date": "Дата обновления",
                  "creation_date": "Дата создания", "expiration_date": "Дата истечения срока регистрации",
                  "name_servers": "Серверы NS", "dnssec": "dnssec", "name": "Владелец", "org": "Организация",
                  "address": "Адрес", "city": "Город", "state": "Штат", "zipcode": "Индекс", "country": "Страна"}

    whois_dict = dict()
    wh = whois.whois(domain, flags=0)
    for field in list_field:
        if wh.get(field) is not None:
            try:
                if field == 'updated_date':
                    date = [str(dat) for dat in wh.get(field)]
                    if str(wh[field]) not in whois_dict:
                        whois_dict[list_field[field]] = date
                elif field == 'creation_date':
                    date = [str(dat) for dat in wh.get(field)]
                    if str(wh[field]) not in whois_dict:
                        whois_dict[list_field[field]] = date
                elif field == 'expiration_date':
                    date = [str(dat) for dat in wh.get(field)]
                    if str(wh[field]) not in whois_dict:
                        whois_dict[list_field[field]] = date
                else:
                    if str(wh[field]) not in whois_dict:
                        whois_dict[list_field[field]] = wh[field]
            except TypeError:
                if str(wh[field]) not in whois_dict:
                    whois_dict[list_field[field]] = str(wh[field])

    if len(whois_dict) > 0:
        print(f'[**] Информация WHOIS получена:n{"-" * 31}')
        ADDR_INFO[ip].update(whois_dict)
        for item in whois_dict:
            print(f'  - {item}: {whois_dict[item]}')
        domain_on_ip(ip)
    else:
        ADDR_INFO[ip].update({'WHOIS': None})
        print('[-] Информация WHOIS не получена')
        domain_on_ip(ip)


def domain_on_ip(ip: str):
    """
    Выполняется запрос для получения информации о доменах,
    которые имеют один и тот же ip-адрес и находятся в базе
    проверок Virustotal. Для выполнения запроса нужно получить
    ключ API предварительно зарегистрировав аккаунт и скопировав
    его в личном кабинете.
    :param ip: адрес для запроса к API Virustotal.
    """
    print(f'n[*] Запрос доменов на IP (Virustotal): {ip}')
    param = {'ip': ip, 'apikey': api_total}
    resp = requests.get(url='https://www.virustotal.com/vtapi/v2/ip-address/report', params=param).json()

    if resp['response_code'] == 1:
        print('[**] Домены найдены')
        for dom in resp['resolutions']:
            DOMAIN.add(dom['hostname'])
    else:
        print('[-] Домены не найдены.')
    get_bing(ip)


def get_bing(ip: str):
    """
    Запрос в поиске Bing с параметром ip.
    Этот параметр позволяет находить в поиске домены
    на данном адресе. Итерируются 10 страниц поиска.
    Найденный домены сохраняются в глобальное множество
    DOMAIN.
    :param ip: адрес для запроса в поиске Bing.
    """
    browser = webdriver.Chrome(options=options, service=Service(log_path=os.devnull, executable_path=executable_path))

    stealth(driver=browser,
            user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                       'Chrome/83.0.4103.53 Safari/537.36',
            languages=["ru-RU", "ru"],
            vendor="Google Inc.",
            platform="Win32",
            webgl_vendor="Intel Inc.",
            renderer="Intel Iris OpenGL Engine",
            fix_hairline=True,
            run_on_insecure_origins=True,
            )

    print('n[*] Поиск информации о доменах в BING')
    num = 1
    count = 1
    while num < 51:
        print(f'r  - Получаю информацию со страницы: {count}', end='')
        browser.get(f'https://www.bing.com/search?q=ip%3a+'
                    f'{ip}&qs=HS&sc=1-0&cvid=E3A8381DCD48463B9D4FBFFC24B7C9B3&sp=1&first={num}&FORM=PERE')
        time.sleep(1)

        soup = BeautifulSoup(browser.page_source, 'lxml')
        links = soup.find('ol', id='b_results').find_all('li', class_='b_algo')
        for lin in links:
            DOMAIN.add(lin.find('div', class_='b_title').find('h2').find('a')['href'].split("/")[2])
        num += 10
        count += 1
        time.sleep(2)
    browser.quit()


def check_domain(dom: str, ip: str):
    """
    Проверка домена на существование. Если домен отвечает,
    выполняем запрос и пытаемся получить заголовки. Если заголовки
    получены, добавляем в множество CHECK.
    :param ip: ip-адрес для проверки текущего ip домена.
    :param dom: доменное имя для проверки на валидность.
    :return: выход из функции.
    """
    if ping(dom):
        print(f'r  - Проверка: {dom}', end='')
        for pref in ['https://', 'http://']:
            try:
                requests.head(f'{pref}{dom}', headers=headers, timeout=3)
                if get_ip(dom) == ip:
                    CHECK.add(dom)
            except (requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout):
                continue


def thread_check(ip):
    """
    Запуск потоков для выполнения запросов для проверки доменов
    на существование.
    """
    threads = []
    for dom in DOMAIN:
        t = threading.Thread(target=check_domain, kwargs={'dom': dom, 'ip': ip}, daemon=True)
        t.start()
        threads.append(t)
        time.sleep(0.02)
    for thread in threads:
        thread.join()


def save_domain(domain: str):
    """
    Сохраняем результат поисков в json и текстовый документ.
    :param domain: имя домена для сохранения результата.
    """
    with open(os.path.join(os.getcwd(), f'result_{domain}.json'), 'w', encoding='utf-8') as inf:
        json.dump(ADDR_INFO, inf, indent=4, ensure_ascii=False)

    for it in ADDR_INFO:
        with open(os.path.join(os.getcwd(), f'result_{domain}.txt'), 'a', encoding='utf-8') as file:
            file.write(f'[+] Информация о хостинг-провайдере:n{"-" * 36}n')
            for itm in ADDR_INFO[it].keys():
                if itm == 'domains':
                    file.write(f'n[+] Найдена информация о доменах:n{"-" * 34}n')
                    print(f'nn[+] Найдена информация о {len(ADDR_INFO[it][itm])} доменах')
                    for dom in ADDR_INFO[it][itm]:
                        file.write(f'{dom}n')
                else:
                    file.write(f'{itm}: {ADDR_INFO[it][itm]}n')
    print(f'[+] Информация сохранена в файлы: result_{domain}.txt, result_{domain}.json')


def main():
    """
    Запрос имени домена для поиска информации.
    Запуск функции для загрузки диапазона адресов
    Cloudflare.
    Запуск функции для получения ip-адреса домена.
    Проверка не является ли множество пустым и запуск функции
    проверки доменов на существование.
    Запуск функции сохранения результатов.
    """
    domain = input('Введите доменное имя: ')
    DOMAIN.add(domain)

    try:
        with open(os.path.join(os.getcwd(), f'result_{domain}.txt'), 'w', encoding='utf-8') as file:
            file.write(f'Информация о доменах на ip-адресе домена: {domain}nn')
    except (FileNotFoundError, OSError):
        print('Введите доменное имя')
        return

    load_range()

    print('n[*] Получение IP-адреса')
    ip = get_ip(domain)
    if ip:
        print(f'[**] IP-адрес получен: {ip}')
        ADDR_INFO[ip] = dict()
    else:
        print('[-] Невозможно получить IP-адрес')
        exit(0)

    cloud_detect(ip, domain)
    if len(DOMAIN) > 0:
        print('nn[*] Проверка доменов:')
        thread_check(ip)
    else:
        print('[-] Информация о доменах не найдена')
    if len(CHECK) > 0:
        ADDR_INFO[ip].update({'domains': list(CHECK)})
        save_domain(domain)
    else:
        print('n[-] Информация найдена, но домены не отвечают на запросы')


if __name__ == "__main__":
    main()

А на этом, пожалуй, все.

Спасибо за внимание. Надеюсь данная информация будет вам полезна

Добавить комментарий