Автор: acc2ss
источник: runion.su
Приветствую всех читателей. Давненько я не писал статьи, и вот все же решился
Сегодня хотелось бы написать о такой теме как осинт сайтов.
Перед началом основной части нужно понять, а зачем это вообще нужно? Что можно узнать о сайте? Что можно сделать с этой информацией?
Я бы сказал что осинт - самый первый этап в обработке таргета(после его поиска конечно), поэтому ему нужно уделять не меньше внимания, чем любым другим этапам. Во время осинта мы можем найти множество полезной для нас в будущем информации: начиная от оригинального ip сервера, заканчиваяпсихозом от акамаи админками где логин и пароль 123.
Сам осинт проводится с помощью различных сервисов, в ручную вы вряд ли сможете собрать много информации.
Сегодня мы будем собирать такую информацию как:
1. оригинальный ip сайта
2. поддомены
3. технологии
4. порты
5. потенциально уязвимые страницы, параметры, формы
6. технические страницы
7. изучение js кода и т.п
8. WAF и возможность его обойти
Немного поясню за поддомены и js код, т.к многие новички могут не понимать зачем это нужно. С поддоменами все просто - с их помощью иногда можно найти оригинальный ip сервера если это не удалось с основного домена. Но зачем изучать js код? В нем тоже может быть полезная информация, например если кодердолбаеб забыл убрать оригинальный ip сервера из запросов к бэкэнду. На практике вряд ли такое попадется, но проверять все же стоит.
Таргет на сегодняшнюю статью будет - https://e-shop.robotis.co.jp/
Начнем с попыток поиска оригинального ip. Это можно сделать через сервисы: Fofa, Censys, Shodan. Как видим таргет состоит из поддомена и основного домена, если попробовать убрать поддомен, то нас будет редиректить обратно, так что для начала я хочу проверить трагет с поддоменом. вставляем во все сервисы e-shop.robotis.co.jp и смотрим результат.
censys сразу выдал ip, и если его проверить, то нас перекидывает на сайт. Хоть он и будет отличаться от того что с поддоменом, но мы можем увидеть что ip принадлежит тому сайту что мы ищем. P.S советую искать сразу на censys, он чаще всех остальных показывает ip.
Интересный факт(для тех кто не знал) - fofa иногда может показывать связанные с доменом поддомены:
Достаточно удобная фишка, в censys я такого не видел. Если сравнивать одинаковые запросы в этих 2-х сервисах, то fofa покажет больше информации:
Было слишком просто, поэтому сделаем вид что ip мы сразу не нашли
Поиск поддоменов можно проводить как через сервисы, так и самому, скриптами. Из сервисов я могу порекомендовать securitytrails.com. Вводим домен и видим все поддомены:
Если оригинальный ip не был найден сразу, то можно проверить поддомены, иногда через них тоже можно найти. Так и сделаем. Пробуем вставить поддомены и видим такой результат:
Никакие поддомены не показали оригинального ip. Тут как повезет.
Поиск технологий можно проводить через wappalyzer, это расширение для браузера. При переходе на любую страницу мы сразу можем увидеть используемые сайтом технологии.
Здесь для нас может быть полезным знать что используется php, клауд и jquery.
Поиск портов можно проводить через сервисы, либо через nmap. Тут на ваше усмотрение, но я буду использовать свой сервис. Вводим либо оригинальный ip(если уже нашли), либо домен сайта. На нашем таргете открыты 3 порта:
Первые 2 не особо интересные, а 444 дает информацию что на сайте используется snpp.
Потенциально уязвимые страницы, параметры и формы можно искать в ручную. У меня есть скрипт для поиска форм и ссылок, я буду использовать его. Если вам не лень, то можете поискать скрипты на гитхабе
Спойлер: код
Python: Скопировать в буфер обмена
Пример запуска: py main.py https://121.78.116.92/ --depth 2 --threads 10 --delay 0.5 --output results.txt
(--help для помощи)
После поиска всех страниц и форм можно приступать к "Фильтрации" полученных данных. Нас интересуют ссылки где есть формы и ссылки с параметрами. Сидим, смотрим, ищем и выписываем то что нужно.
У меня получился такой небольшой список:
Форма 1 и есть на всех страницах, поэтому здесь ее нет.
Для поиска технических страниц так же будем использовать скрипт. Если вам не лень, то можете поискать другие.
Спойлер: код
Python: Скопировать в буфер обмена
Это тот же скрипт что выше, но немного переделанный. Пример запуска: py main.py https://121.78.116.92/ --teh --threads 10 --output output.txt --tech-file tech.txt --delay 0.1
Поиск занимает долгое время, поэтому для примера я вставил страницы которые есть на сайте.
wordlist'ы вы можете собрать с гитхаба, их там горы.
И так, к этому моменту мы собрали уже достаточно много информации для дальнейшей работы:
еще 1 пункт - изучения js кода. Это достаточно муторный процесс, и делать его вы вряд ли будете. Рассказывать и показывать я тоже не буду. Я не мазохист просто смотрите код на предмет интересных строчек.
Возможно вы найдете отправку данных из js в бэкэнд. Что то типо такого:
JavaScript: Скопировать в буфер обмена
Определение WAF. наверно самый простой этап. Его можно узнать через wappalyzer, chek-host, либо скриптами - например Whatwaf. Попробуем запихнуть наш таргет в скрипт и посмотрим что он даст.
К сожалению инструмент мне ничего не показал, поэтому смотрим wappalyzer и видим клауд. Вообщем все просто. Так же сюда можно добавить попытки обхода WAF в случае если не нашли ориг ip. Ищем параметры или формы, пробуем вставить пэйлоад и если видим 403, то пробуем использовать тамперы. Это уже не совсем тема осинта.
Еще одним вариантом поиска поддоменов и ip является - burp. Если поиск поддоменов понятен сразу всем, то с ip у некоторых могут появится вопросы.
Поиск поддоменов через burp:
Открываем сайт, заходим в Proxy -> http history и отправляем таргет в интрудер. ставим §§ перед доменом и меняем хост на основной домен.
В пэйлоадах вписываем поддомены и запускаем атаку. По окончанию сортируем по status code и получаем рабочие поддомены:
Для поиска ip через бюрп вам придется полазить по сайту, отправлять данные через формы и тд и тп. После таких лазаний заходим в http hisory и смотрим на колонку ip. Если вам повезет, то разрабы могли где то оставить ориг ip, и вы его увидите.
И так, основные этапы я вроде рассказал. Посмотрим еще раз что мы нашли:
Информации достаточно много. Ip можно использовать для обхода WAF, поддомены для поиска других язв либо ip, технологии для понимания из чего состоит сайт, порты для знания открытых служб, формы и параметры для поиска язв, тех страницы для поиск других уязвимостей.
То что я показал в статье не является полным "мануалом" для осинта, т.к все используют разные инструменты, которые могут показывать менее/более полную информацию.
Если у вас есть какие либо вопросы касающиеся пентестинга - смело пишите в теме/лс форума/tox Отвечу всем по мере возможности.
Так же пока есть такая возможность, оставлю ссылку на свой сервис - https://domain-scanner.dosx.su/ все пока в ранней бетке и работает не всегда, но улучшения потихоньку идут. Все абсолютно бесплатно. Из функционала:
Поддержать разработку сайта и автора:
USDT trc(20) TNjxY6W1buZWP47WFi8BMXKhKaUr4U5hTi
BTC bc1qnhu6nfawzx9crfr3vvr65zrjdjrz3et7qajd4e
источник: runion.su
Приветствую всех читателей. Давненько я не писал статьи, и вот все же решился
Сегодня хотелось бы написать о такой теме как осинт сайтов.
Перед началом основной части нужно понять, а зачем это вообще нужно? Что можно узнать о сайте? Что можно сделать с этой информацией?
Я бы сказал что осинт - самый первый этап в обработке таргета(после его поиска конечно), поэтому ему нужно уделять не меньше внимания, чем любым другим этапам. Во время осинта мы можем найти множество полезной для нас в будущем информации: начиная от оригинального ip сервера, заканчивая
Сам осинт проводится с помощью различных сервисов, в ручную вы вряд ли сможете собрать много информации.
Сегодня мы будем собирать такую информацию как:
1. оригинальный ip сайта
2. поддомены
3. технологии
4. порты
5. потенциально уязвимые страницы, параметры, формы
6. технические страницы
7. изучение js кода и т.п
8. WAF и возможность его обойти
Немного поясню за поддомены и js код, т.к многие новички могут не понимать зачем это нужно. С поддоменами все просто - с их помощью иногда можно найти оригинальный ip сервера если это не удалось с основного домена. Но зачем изучать js код? В нем тоже может быть полезная информация, например если кодер
Таргет на сегодняшнюю статью будет - https://e-shop.robotis.co.jp/
Начнем с попыток поиска оригинального ip. Это можно сделать через сервисы: Fofa, Censys, Shodan. Как видим таргет состоит из поддомена и основного домена, если попробовать убрать поддомен, то нас будет редиректить обратно, так что для начала я хочу проверить трагет с поддоменом. вставляем во все сервисы e-shop.robotis.co.jp и смотрим результат.
censys сразу выдал ip, и если его проверить, то нас перекидывает на сайт. Хоть он и будет отличаться от того что с поддоменом, но мы можем увидеть что ip принадлежит тому сайту что мы ищем. P.S советую искать сразу на censys, он чаще всех остальных показывает ip.
Интересный факт(для тех кто не знал) - fofa иногда может показывать связанные с доменом поддомены:
Достаточно удобная фишка, в censys я такого не видел. Если сравнивать одинаковые запросы в этих 2-х сервисах, то fofa покажет больше информации:
Было слишком просто, поэтому сделаем вид что ip мы сразу не нашли
Поиск поддоменов можно проводить как через сервисы, так и самому, скриптами. Из сервисов я могу порекомендовать securitytrails.com. Вводим домен и видим все поддомены:
Если оригинальный ip не был найден сразу, то можно проверить поддомены, иногда через них тоже можно найти. Так и сделаем. Пробуем вставить поддомены и видим такой результат:
Никакие поддомены не показали оригинального ip. Тут как повезет.
Поиск технологий можно проводить через wappalyzer, это расширение для браузера. При переходе на любую страницу мы сразу можем увидеть используемые сайтом технологии.
Здесь для нас может быть полезным знать что используется php, клауд и jquery.
Поиск портов можно проводить через сервисы, либо через nmap. Тут на ваше усмотрение, но я буду использовать свой сервис. Вводим либо оригинальный ip(если уже нашли), либо домен сайта. На нашем таргете открыты 3 порта:
Первые 2 не особо интересные, а 444 дает информацию что на сайте используется snpp.
Потенциально уязвимые страницы, параметры и формы можно искать в ручную. У меня есть скрипт для поиска форм и ссылок, я буду использовать его. Если вам не лень, то можете поискать скрипты на гитхабе
Спойлер: код
Python: Скопировать в буфер обмена
Code:
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
import argparse
import time
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def get_all_forms(url):
logging.info(f"Fetching forms from: {url}")
try:
response = requests.get(url, verify=False)
except requests.exceptions.SSLError as e:
logging.error(f"SSL error for {url}: {e}")
return []
except Exception as e:
logging.error(f"Error fetching {url}: {e}")
return []
soup = BeautifulSoup(response.text, "html.parser")
return [(form.get('action'), form) for form in soup.find_all("form")]
def get_all_links(url, base_domain):
logging.info(f"Fetching links from: {url}")
try:
response = requests.get(url, verify=False)
except requests.exceptions.SSLError as e:
logging.error(f"SSL error for {url}: {e}")
return set()
except Exception as e:
logging.error(f"Error fetching {url}: {e}")
return set()
soup = BeautifulSoup(response.text, "html.parser")
links = set()
for link in soup.find_all("a"):
href = link.get("href")
if href and not href.startswith("javascript:void(0)"):
full_url = urljoin(url, href)
parsed_link = urlparse(full_url)
if parsed_link.netloc == "" or parsed_link.netloc == base_domain:
links.add(full_url)
return links
def crawl_page(url, depth, max_depth, delay, base_domain):
if depth > max_depth:
return [], []
time.sleep(delay)
forms = get_all_forms(url)
links = get_all_links(url, base_domain)
return forms, links
def crawl_site(start_url, max_depth=2, max_workers=10, delay=1):
visited = set()
to_visit = [(start_url, 0)]
forms_data = []
links_data = []
base_domain = urlparse(start_url).netloc
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(crawl_page, url, depth, max_depth, delay, base_domain): (url, depth) for url, depth in to_visit}
while futures:
for future in as_completed(futures):
url, depth = futures[future]
try:
forms, links = future.result()
if forms:
forms_data.append((url, forms))
if links:
links_data.append((url, links))
visited.add(url)
for link in links:
if link not in visited and (link, depth + 1) not in futures:
futures[executor.submit(crawl_page, link, depth + 1, max_depth, delay, base_domain)] = (link, depth + 1)
except Exception as e:
logging.error(f"Error visiting {url}: {e}")
del futures[future]
return forms_data, links_data
def save_to_file(forms_data, links_data, filename="output.txt"):
with open(filename, "w", encoding='utf-8') as f:
f.write("Forms:\n")
for url, forms in forms_data:
f.write(f"URL: {url}\n")
for action, form in forms:
f.write(f"Form action: {action}\n")
f.write("\n")
f.write("\nLinks:\n")
for url, links in links_data:
f.write(f"URL: {url}\n")
for link in links:
f.write(f"{link}\n")
f.write("\n")
def main():
parser = argparse.ArgumentParser(description="Web Crawler")
parser.add_argument("url", help="URL for crawler")
parser.add_argument("--depth", type=int, default=2, help="Maximum depth to crawl")
parser.add_argument("--threads", type=int, default=10, help="Number of threads to use")
parser.add_argument("--output", type=str, default="output.txt", help="Output file")
parser.add_argument("--delay", type=float, default=1, help="Delay between requests in seconds")
args = parser.parse_args()
parsed_url = urlparse(args.url)
if parsed_url.scheme == "https":
try:
response = requests.get(args.url, verify=False)
response.raise_for_status()
except requests.exceptions.SSLError:
args.url = parsed_url._replace(scheme="http").geturl()
forms_data, links_data = crawl_site(args.url, max_depth=args.depth, max_workers=args.threads, delay=args.delay)
save_to_file(forms_data, links_data, filename=args.output)
for url, forms in forms_data:
logging.info(f"URL: {url} - Found {len(forms)} forms")
for action, form in forms:
logging.info(f"Form action: {action}")
for url, links in links_data:
logging.info(f"URL: {url} - Found {len(links)} links")
for link in links:
logging.info(link)
if __name__ == "__main__":
main()
(--help для помощи)
После поиска всех страниц и форм можно приступать к "Фильтрации" полученных данных. Нас интересуют ссылки где есть формы и ссылки с параметрами. Сидим, смотрим, ищем и выписываем то что нужно.
У меня получился такой небольшой список:
Форма 1 и есть на всех страницах, поэтому здесь ее нет.
Для поиска технических страниц так же будем использовать скрипт. Если вам не лень, то можете поискать другие.
Спойлер: код
Python: Скопировать в буфер обмена
Code:
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
import argparse
import time
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def load_technical_pages(filename):
try:
with open(filename, 'r', encoding='utf-8') as f:
return [line.strip() for line in f if line.strip()]
except Exception as e:
logging.error(f"Ошибка при чтении файла {filename}: {e}")
return []
def get_all_forms(url):
logging.info(f"Fetching forms from: {url}")
try:
response = requests.get(url, verify=False)
response.raise_for_status()
except requests.exceptions.SSLError as e:
logging.error(f"SSL error for {url}: {e}")
return []
except Exception as e:
logging.error(f"Error fetching {url}: {e}")
return []
soup = BeautifulSoup(response.text, "html.parser")
return [(form.get('action'), form) for form in soup.find_all("form")]
def get_all_links(url, base_domain):
logging.info(f"Fetching links from: {url}")
try:
response = requests.get(url, verify=False)
response.raise_for_status()
except requests.exceptions.SSLError as e:
logging.error(f"SSL error for {url}: {e}")
return set()
except Exception as e:
logging.error(f"Error fetching {url}: {e}")
return set()
soup = BeautifulSoup(response.text, "html.parser")
links = set()
for link in soup.find_all("a"):
href = link.get("href")
if href and not href.startswith("javascript:void(0)"):
full_url = urljoin(url, href)
parsed_link = urlparse(full_url)
if parsed_link.netloc == "" or parsed_link.netloc == base_domain:
links.add(full_url)
return links
def check_technical_page(url):
logging.info(f"Checking technical page: {url}")
try:
response = requests.get(url, verify=False)
if response.status_code == 200:
logging.info(f"Technical page found: {url}")
return url
except Exception as e:
logging.error(f"Error checking {url}: {e}")
return None
def check_technical_pages(base_url, technical_pages, visited):
found_pages = []
with ThreadPoolExecutor() as executor:
futures = {executor.submit(check_technical_page, urljoin(base_url, page)): page for page in technical_pages if urljoin(base_url, page) not in visited}
for future in as_completed(futures):
page = futures[future]
result = future.result()
if result:
found_pages.append(result)
visited.add(result)
return found_pages
def crawl_page(url, depth, max_depth, delay, base_domain, search_type, technical_pages, visited):
if search_type == "technical":
time.sleep(delay)
return [], check_technical_pages(url, technical_pages, visited)
if depth > max_depth:
return [], []
time.sleep(delay)
forms = get_all_forms(url)
links = get_all_links(url, base_domain)
return forms, links
def crawl_site(start_url, search_type, technical_pages, max_depth=2, max_workers=10, delay=1):
visited = set()
to_visit = [(start_url, 0)]
forms_data = []
links_data = set()
base_domain = urlparse(start_url).netloc
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(crawl_page, url, depth, max_depth, delay, base_domain, search_type, technical_pages, visited): (url, depth) for url, depth in to_visit}
while futures:
for future in as_completed(futures):
url, depth = futures[future]
try:
forms, links = future.result()
if forms:
forms_data.append((url, forms))
if links:
links_data.update(links)
visited.add(url)
for link in links:
if link not in visited and (link, depth + 1) not in futures:
futures[executor.submit(crawl_page, link, depth + 1, max_depth, delay, base_domain, search_type, technical_pages, visited)] = (link, depth + 1)
except Exception as e:
logging.error(f"Error visiting {url}: {e}")
del futures[future]
return forms_data, links_data
def save_to_file(forms_data, links_data, found_technical_pages, filename="output.txt"):
with open(filename, "w", encoding='utf-8') as f:
f.write("Forms:\n")
for url, forms in forms_data:
f.write(f"URL: {url}\n")
for action, form in forms:
f.write(f"Form action: {action}\n")
f.write("\n")
f.write("\nLinks:\n")
for link in links_data:
f.write(f"{link}\n")
f.write("\n")
f.write("\nFound Technical Pages:\n")
for page in found_technical_pages:
f.write(f"{page}\n")
f.write("\n")
def main():
parser = argparse.ArgumentParser(description="Web Crawler")
parser.add_argument("url", help="URL for crawler")
parser.add_argument("--depth", type=int, default=2, help="Maximum depth to crawl")
parser.add_argument("--threads", type=int, default=10, help="Number of threads")
parser.add_argument("--output", type=str, default="output.txt", help="Output file")
parser.add_argument("--delay", type=float, default=1, help="Delay between requests in seconds")
parser.add_argument("--teh", action='store_true', help="Search technical pages")
parser.add_argument("--fl", action='store_true', help="Search forms and links")
parser.add_argument("--tech-file", type=str, required=True, help="File technical pages")
args = parser.parse_args()
if not (args.teh or args.fl):
logging.error("You must specify either --teh or --fl.")
return
technical_pages = load_technical_pages(args.tech_file)
parsed_url = urlparse(args.url)
if parsed_url.scheme == "https":
try:
response = requests.get(args.url, verify=False)
response.raise_for_status()
except requests.exceptions.SSLError:
args.url = parsed_url._replace(scheme="http").geturl()
search_type = "technical" if args.teh else "forms_links"
forms_data, links_data = crawl_site(args.url, search_type, technical_pages, max_depth=args.depth if search_type == "forms_links" else None, max_workers=args.threads, delay=args.delay)
save_to_file(forms_data, links_data, links_data, filename=args.output)
for url, forms in forms_data:
logging.info(f"URL: {url} - Found {len(forms)} forms")
for action, form in forms:
logging.info(f"Form action: {action}")
for link in links_data:
logging.info(f"Found link: {link}")
if __name__ == "__main__":
main()
Поиск занимает долгое время, поэтому для примера я вставил страницы которые есть на сайте.
wordlist'ы вы можете собрать с гитхаба, их там горы.
И так, к этому моменту мы собрали уже достаточно много информации для дальнейшей работы:
еще 1 пункт - изучения js кода. Это достаточно муторный процесс, и делать его вы вряд ли будете. Рассказывать и показывать я тоже не буду. Я не мазохист просто смотрите код на предмет интересных строчек.
Возможно вы найдете отправку данных из js в бэкэнд. Что то типо такого:
JavaScript: Скопировать в буфер обмена
Code:
async function sendData() {
const data = { key: "value" };
try {
const response = await fetch('http://1.1.1.1:5000/popa', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
const result = await response.json();
console.log(result);
} catch (error) {
console.error('Ошибка:', error);
}
}
sendData();
Определение WAF. наверно самый простой этап. Его можно узнать через wappalyzer, chek-host, либо скриптами - например Whatwaf. Попробуем запихнуть наш таргет в скрипт и посмотрим что он даст.
К сожалению инструмент мне ничего не показал, поэтому смотрим wappalyzer и видим клауд. Вообщем все просто. Так же сюда можно добавить попытки обхода WAF в случае если не нашли ориг ip. Ищем параметры или формы, пробуем вставить пэйлоад и если видим 403, то пробуем использовать тамперы. Это уже не совсем тема осинта.
Еще одним вариантом поиска поддоменов и ip является - burp. Если поиск поддоменов понятен сразу всем, то с ip у некоторых могут появится вопросы.
Поиск поддоменов через burp:
Открываем сайт, заходим в Proxy -> http history и отправляем таргет в интрудер. ставим §§ перед доменом и меняем хост на основной домен.
В пэйлоадах вписываем поддомены и запускаем атаку. По окончанию сортируем по status code и получаем рабочие поддомены:
Для поиска ip через бюрп вам придется полазить по сайту, отправлять данные через формы и тд и тп. После таких лазаний заходим в http hisory и смотрим на колонку ip. Если вам повезет, то разрабы могли где то оставить ориг ip, и вы его увидите.
И так, основные этапы я вроде рассказал. Посмотрим еще раз что мы нашли:
Информации достаточно много. Ip можно использовать для обхода WAF, поддомены для поиска других язв либо ip, технологии для понимания из чего состоит сайт, порты для знания открытых служб, формы и параметры для поиска язв, тех страницы для поиск других уязвимостей.
То что я показал в статье не является полным "мануалом" для осинта, т.к все используют разные инструменты, которые могут показывать менее/более полную информацию.
Если у вас есть какие либо вопросы касающиеся пентестинга - смело пишите в теме/лс форума/tox Отвечу всем по мере возможности.
Так же пока есть такая возможность, оставлю ссылку на свой сервис - https://domain-scanner.dosx.su/ все пока в ранней бетке и работает не всегда, но улучшения потихоньку идут. Все абсолютно бесплатно. Из функционала:
Поддержать разработку сайта и автора:
USDT trc(20) TNjxY6W1buZWP47WFi8BMXKhKaUr4U5hTi
BTC bc1qnhu6nfawzx9crfr3vvr65zrjdjrz3et7qajd4e