Не удалось прогрузить медиа
Эксклюзивно для форума LIMB.cc
Решить проблему получения крупных диапазонов доменных имен можно разными способами.
Спарсить поисковую систему - первое, о чем можно подумать. Ураганы капч, блокировок IP и аккаунтов. В идеале все должно работать через графический интерфейс браузера, типа селениума, и все равно будет капчить, и все равно нужен рабочий пул хороших прокси, и все равно это чудо будет ломаться а-ля старый дешевый автомобиль. Впрочем, это способ.
Раздачи списков доменных имен - еще один чудесный способ. В них отсутствие игрушечного Т3 тира порой компенсируется аномально огромным количеством рпанелей. Впрочем, каждому свое.
Каталоги сайтов a1webdirectory, blogarama, sitepromotiondirectory, curlie - легко спарсить. Например, парсер под viesearch:
Спойлер: code
C-like:
package main
import (
"bufio"
"fmt"
"io"
"net/http"
"net/url"
"os"
"regexp"
"strings"
"time"
)
func main() {
const delay = 10 * time.Second
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if len(via) > 0 {
fmt.Printf("🔁 Редирект с %s на %s\n", via[len(via)-1].URL, req.URL)
}
return nil
},
}
file, err := os.Open("links.txt")
if err != nil {
fmt.Printf("❌ Ошибка при открытии links.txt: %v\n", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
baseURL := strings.TrimSpace(scanner.Text())
if baseURL == "" {
continue
}
parsed, err := url.Parse(baseURL)
if err != nil {
fmt.Printf("⚠️ Невалидный URL %s: %v\n", baseURL, err)
continue
}
segments := strings.Split(strings.Trim(parsed.Path, "/"), "/")
category := segments[len(segments)-1]
if category == "" && len(segments) > 1 {
category = segments[len(segments)-2]
}
filename := category + ".txt"
output, err := os.Create(filename)
if err != nil {
fmt.Printf("❌ Не удалось создать файл %s: %v\n", filename, err)
continue
}
defer output.Close()
fmt.Printf("📂 Обработка категории: %s\n", category)
page := 1
for {
pageURL := fmt.Sprintf("%s?p=%d", baseURL, page)
fmt.Printf("🌐 Загружаем: %s\n", pageURL)
req, err := http.NewRequest("GET", pageURL, nil)
if err != nil {
fmt.Printf("⚠️ Ошибка создания запроса: %v\n", err)
break
}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("⚠️ Ошибка при выполнении запроса: %v\n", err)
break
}
bodyBytes, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
fmt.Printf("⚠️ Ошибка чтения тела: %v\n", err)
break
}
body := string(bodyBytes)
if strings.Contains(body, "&s=n") {
fmt.Println("🚧 Достигнут конец категории (найдено '&s=n')")
break
}
re := regexp.MustCompile(`<div\s+class="vie_flex_url">.*?<a[^>]+href="(https?://[^"]+)"`)
matches := re.FindAllStringSubmatch(body, -1)
if len(matches) == 0 {
fmt.Println("⚠️ Нет <div class=\"vie_flex_url\"> на странице.")
}
for _, m := range matches {
link := m[1]
fmt.Fprintf(output, "%s\n", link)
fmt.Printf("✅ Найдена ссылка: %s\n", link)
}
page++
fmt.Printf("⏳ Пауза %v...\n", delay)
time.Sleep(delay)
}
fmt.Printf("✅ Завершено: %s\n\n", filename)
}
if err := scanner.Err(); err != nil {
fmt.Printf("❌ Ошибка при чтении links.txt: %v\n", err)
}
}
Минус в том, что это всегда ограниченное статическое количество доменов, обычно до нескольких сотен тысяч. Оставлю одну мысль, ее суть в рекурсивном парсинге сайтов, где будут ссылки на другие сайты, например, википедии.
DNS-протокол отвечает за преобразование доменных имен в IP-адреса, представляет собой специальную программу, запущенную на специальных серверах. И даже не одну, а целый кластер. Он как бы знает домены своей зоны, но отдать ее не может. Операция называется AXFR, и на нее, насколько я понимаю, существует некий запрет.
А можно ли получить домены через сертификаты?
Все началось с сайта crt.sh, который используют для получения поддоменов. По сути, он берет из своей таблицы все сертификаты, которые были выписаны домену,
и предоставляет их тому, кто запрашивает. Но можно ли составить запрос так, чтобы он вернул все сертификаты, выданные центром сертификации, например lets encrypt?
Мы видим эти данные в логах. Колдуя над запросом, сайт падает - неудивительно, ответ должен быть огромным.
У crt есть открытая база данных: psql -h crt.sh -p 5432 -U guest certwatch. И да, у них есть эти таблицы, из которых можно вытащить логи - победа? Permission denied :(
Но откуда у crt вообще эти данные, что происходит?
Пытаясь разобраться, оказывается, что работаем мы с логами Certificate Transparency, которые хранят записи обо всех выданных сертификатах. ИИ говорит, что любой может посмотреть в публичных логах - какие сертификаты и для каких доменов были выданы. Это вызывает сомнения. В конце концов, куда гуглить?
RFC 6962 описывает Certificate Transparency. Из него действительно следует, что центры сертификации обязаны логировать выпущенный сертификат хотя бы в один CT-лог.
А у сервера, который хранит эти логи, должны быть ручки (которые в том числе и описывает RFC), которые публично должны отдавать эти логи.
Пока я все это гуглил, наткнулся на сайт certstream.calidog.io - типа стриминг-монитор CT-логов. GitHub - https://github.com/calidog
Проект - https://github.com/CaliDog/certstream-python
Шесть строчек кода:
Python:
import certstream
def print_callback(message, context):
if message['message_type'] == "certificate_update":
domains = message['data']['leaf_cert']['all_domains']
print("Новые домены:", domains)
certstream.listen_for_events(print_callback, url='wss://certstream.calidog.io/')
Десятки тысяч доменов в минуту.
Не удалось прогрузить медиа
Все, что тут происходит, - лишь подключение по сокету к стриму сервера-ретранслятора. Но как он работает под капотом?
Суть примерно такая: отсюда получаем список журналов серверов - https://www.gstatic.com/ct/log_list/v3/log_list.json
Не удалось прогрузить медиа
Берем, к примеру, https://ct.googleapis.com/logs/us1/argon2025h2 информативное API :)
Превращаем в https://ct.googleapis.com/logs/us1/argon2025h2/ct/v1/get-entries?start=0&end=100
Не удалось прогрузить медиа
Декодируем base64, вытаскиваем x509-сертификат - примерно так.
Спойлер: code
Code:
import requests
import base64
from cryptography import x509
from cryptography.hazmat.backends import default_backend
def parse_extra_data(extra_data: bytes):
certs = []
i = 0
while i < len(extra_data):
if i + 3 > len(extra_data):
break
cert_len = int.from_bytes(extra_data[i:i+3], "big")
i += 3
cert_bytes = extra_data[i:i+cert_len]
i += cert_len
certs.append(cert_bytes)
return certs
url = "https://ct.googleapis.com/logs/us1/argon2025h2/ct/v1/get-entries?start=1000&end=10000"
resp = requests.get(url).json()
for entry in resp["entries"]:
extra_data = base64.b64decode(entry["extra_data"])
for cert_bytes in parse_extra_data(extra_data):
try:
cert = x509.load_der_x509_certificate(cert_bytes, default_backend())
domains = []
try:
ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName)
domains = ext.value.get_values_for_type(x509.DNSName)
except Exception:
pass
print("CN:", cert.subject.rfc4514_string(), "Domains:", domains)
except Exception:
continue
Откуда взялась ручка? ИИ. Но изначально, конечно, она из RFC.
Очень простой способ, и в то же время это не то, что даст Google по запросу получение всех доменов. Обнаружилось методом тыканья :)
👍21
❤️4
🔥3
🤔2
LIMB