<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Shadow</title><generator>teletype.in</generator><description><![CDATA[Shadow]]></description><image><url>https://img1.teletype.in/files/44/58/44587e5a-b44a-43bb-a0e1-c31c7ac0cf48.png</url><title>Shadow</title><link>https://teletype.in/@specialist_infosec</link></image><link>https://teletype.in/@specialist_infosec?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/specialist_infosec?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/specialist_infosec?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Tue, 28 Jul 2026 14:08:21 GMT</pubDate><lastBuildDate>Tue, 28 Jul 2026 14:08:21 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@specialist_infosec/Geolocation-methods-based-on-photos</guid><link>https://teletype.in/@specialist_infosec/Geolocation-methods-based-on-photos?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec</link><comments>https://teletype.in/@specialist_infosec/Geolocation-methods-based-on-photos?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec#comments</comments><dc:creator>specialist_infosec</dc:creator><title>Методы геолокации по фотографиям: ландшафт, архитектура, растительность, дорожные знаки</title><pubDate>Sun, 26 Jul 2026 17:09:06 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/7e/17/7e17bc42-1a04-47c7-9949-3685766252b1.png"></media:content><description><![CDATA[<img src="https://img4.teletype.in/files/bc/92/bc9267cd-9e12-44ff-902c-b1eebd6a5091.png"></img>Геолокация по фотографии — это искусство и наука определения места съемки на основе визуальных элементов, присутствующих на изображении. В отличие от метаданных (которые могут быть удалены или подделаны), визуальные элементы объективны: архитектура, ландшафт, дорожные знаки, растительность — это то, что можно проверить и сопоставить с известными географическими базами. Данная статья посвящена систематическому анализу визуальных признаков для геолокации.]]></description><content:encoded><![CDATA[
  <figure id="ycvY" class="m_column">
    <img src="https://img4.teletype.in/files/bc/92/bc9267cd-9e12-44ff-902c-b1eebd6a5091.png" width="2199" />
  </figure>
  <h3 id="mo9O">Методы геолокации по фотографиям: ландшафт, архитектура, растительность, дорожные знаки</h3>
  <p id="ZGPf">Геолокация по фотографии — это искусство и наука определения места съемки на основе визуальных элементов, присутствующих на изображении. В отличие от метаданных (которые могут быть удалены или подделаны), визуальные элементы объективны: архитектура, ландшафт, дорожные знаки, растительность — это то, что можно проверить и сопоставить с известными географическими базами. Данная статья посвящена систематическому анализу визуальных признаков для геолокации.</p>
  <p id="AkHp"><strong>Уровни геолокации по фотографии</strong></p>
  <figure id="Ny61" class="m_column">
    <img src="https://img3.teletype.in/files/67/55/6755f13a-15cc-4bbe-8d6e-bc4f366de76b.png" width="945" />
  </figure>
  <p id="YlYR"><strong>Пирамида геолокации</strong></p>
  <ol id="KujG">
    <li id="MOJ5"><strong>Язык и письменность</strong> - Вывески, надписи, реклама.</li>
    <li id="IUtx"><strong>Архитектура</strong> - Стиль зданий, форма крыш, строительные материалы.</li>
    <li id="6LnF"><strong>Транспорт</strong> - Типы автомобилей, номера, дорожная разметка.</li>
    <li id="ZXvH"><strong>Инфраструктура</strong> - Дорожные знаки, фонари, столбы, опоры ЛЭП.</li>
    <li id="Sr4m"><strong>Ландшафт и природа</strong> - Растительность, рельеф, водоемы.</li>
    <li id="JtvV"><strong>Погода и освещение</strong> - Солнце, тени, облака.</li>
    <li id="agu5"><strong>Специфические маркеры</strong> - Флаги, гербы, памятники, таблички.</li>
  </ol>
  <p id="mYon"><strong>Этап 1: Язык и письменность</strong></p>
  <p id="uR5t">Это самый быстрый и очевидный способ ограничить географию.</p>
  <p id="af1v"><strong>Что искать:</strong></p>
  <ul id="7ANP">
    <li id="VbUG"><strong>Текст на вывесках:</strong> Магазины, кафе, остановки, указатели.</li>
    <li id="b9rW"><strong>Язык:</strong> Русский, украинский, английский, китайский, арабский и т.д.</li>
    <li id="8yQH"><strong>Скрипт (алфавит):</strong> Кириллица, латиница, арабское письмо, иероглифы.</li>
    <li id="X8mx"><strong>Особенности орфографии:</strong> Буква «і» указывает на украинский, «ğ» - на турецкий.</li>
    <li id="Sfvp"><strong>Политика языков:</strong> Некоторые страны имеют два официальных языка - это сужает поиск.</li>
  </ul>
  <p id="4rOG"><strong>Примеры:</strong></p>
  <ul id="aHfU">
    <li id="JW4n">Надпись на украинском и кириллица - Украина.</li>
    <li id="hszr">Латиница с диакритикой (ě, š, č) - Чехия, Словакия, Польша.</li>
    <li id="7DXB">Арабский шрифт - Ближний Восток.</li>
    <li id="IWpy">Иероглифы (упрощенные) - Китай.</li>
    <li id="Mgem">Иероглифы (традиционные) - Тайвань, Гонконг.</li>
  </ul>
  <p id="aPs3"><strong>Инструменты:</strong></p>
  <ul id="bwEv">
    <li id="epyp">Google Translate (распознавание незнакомых языков по фото - Google Lens).</li>
    <li id="YbcX">База Unicode, информация о региональных языках.</li>
  </ul>
  <p id="7kaQ"><strong>Этап 2: Архитектура</strong></p>
  <p id="vvUn">Архитектурный стиль региона - один из самых надежных маркеров, так как он меняется медленно.</p>
  <p id="ru0F"><strong>Ключевые элементы архитектуры:</strong></p>
  <figure id="Vgjy" class="m_original">
    <img src="https://img4.teletype.in/files/f8/76/f876bb49-b5b1-4b6d-a769-4f85f81aa701.png" width="934" />
  </figure>
  <p id="Ji2w"><strong>Дополнительные признаки:</strong></p>
  <ul id="Jmxj">
    <li id="eaUx"><strong>Цвет фасадов</strong> (в Санкт-Петербурге - пастельные тона, в Скандинавии - яркие цвета, в Париже - бежевый камень).</li>
    <li id="jkyN"><strong>Высота зданий</strong> (в Европе редко бывают небоскребы, в Азии и США - часто).</li>
    <li id="J415"><strong>Элементы отделки</strong> (лепнина, мозаика, кафель, скульптуры).</li>
  </ul>
  <p id="Hv0k"><strong>Инструменты:</strong></p>
  <ul id="YICC">
    <li id="wzq0">Google Lens: найти похожую архитектуру в интернете.</li>
    <li id="SRhc">Базы архитектурных стилей (Wikipedia, архитектурные сайты).</li>
    <li id="r0JC">Google Images и Yandex.Images (поиск по фрагментам).</li>
  </ul>
  <p id="q53v"><strong>Этап 3: Транспорт и автомобили</strong></p>
  <p id="4FyM">Автомобили и другие транспортные средства - отличный маркер региона.</p>
  <p id="3tcc"><strong>Ключевые элементы транспорта:</strong></p>
  <figure id="Km7x" class="m_column">
    <img src="https://img3.teletype.in/files/21/88/2188b309-5fd3-4071-a5d8-127e0d4cdd50.png" width="947" />
  </figure>
  <p id="qgtK"><strong>Анализ номерных знаков:</strong></p>
  <ul id="6usx">
    <li id="xIXO"><strong>Код региона:</strong> В России и Казахстане региональный код часто включен в номер.</li>
    <li id="ZnfA"><strong>Страна:</strong> Цвет и формат номеров регулируются законодательством.</li>
    <li id="uNdH"><strong>Цвет фона:</strong></li>
    <ul id="Ni32">
      <li id="f9ZW">Белый - большинство стран.</li>
      <li id="wzrC">Желтый - Великобритания, Люксембург.</li>
      <li id="jo0b">Зеленый - некоторые коммерческие авто в странах ЕС.</li>
    </ul>
  </ul>
  <p id="SaVu"><strong>Инструменты:</strong></p>
  <ul id="8gbn">
    <li id="hdaU"><strong>PlateRecognizer</strong> (распознавание номеров по фото - платное).</li>
    <li id="uqHD"><strong>Таблицы автомобильных номеров</strong> (поисковые запросы по формату).</li>
  </ul>
  <p id="F3EG"><strong>Этап 4: Дорожные знаки и инфраструктура</strong></p>
  <p id="CQ0j">Дорожные знаки имеют национальные особенности, хотя и стандартизированы (Венская конвенция).</p>
  <p id="fd8L"><strong>Ключевые признаки дорожных знаков:</strong></p>
  <ul id="BcwB">
    <li id="JrrG"><strong>Форма:</strong> Круглые (запрещающие, предписывающие) vs треугольные (предупреждающие).</li>
    <li id="qsJt"><strong>Цвет:</strong> Красная кайма (запрет), синий фон (предписание, информация).</li>
    <li id="Zq99"><strong>Оформление:</strong> Шрифт, пиктограммы, наличие надписей на национальном языке.</li>
    <li id="KD6c"><strong>Столбы и фонари:</strong> Тип и форма фонарных столбов может указывать на страну.</li>
  </ul>
  <p id="th4a"><strong>Примеры:</strong></p>
  <ul id="yCzo">
    <li id="QlVd">В России знак «Пешеходный переход» - синий квадрат с белым треугольником. В Европе - знак с пешеходом на зебре, часто желтый фон.</li>
    <li id="rQky">В странах ЕС границы стран часто обозначаются синим щитом с желтыми звездами.</li>
  </ul>
  <p id="VX3J"><strong>Этап 5: Ландшафт и растительность</strong></p>
  <p id="AQcz">Природные особенности - одни из самых сложных, но и самых достоверных маркеров.</p>
  <figure id="AI1r" class="m_column">
    <img src="https://img1.teletype.in/files/c6/8b/c68b1682-0594-4c09-ab3e-b4d2c4c31fac.png" width="947" />
  </figure>
  <p id="f15T"><strong>Инструменты:</strong></p>
  <ul id="VhRR">
    <li id="QDMK"><strong>Google Lens</strong> (распознавание растений и деревьев).</li>
    <li id="BoaZ"><strong>iNaturalist</strong> (определение растений по фото).</li>
    <li id="ld3C"><strong>Спутниковые карты</strong> (Google Earth, Sentinel).</li>
  </ul>
  <p id="qiOa"><strong>Этап 6: Погода как маркер сезона и климата</strong></p>
  <p id="6ygl">Погода на фотографии может указывать на сезон и климатическую зону.</p>
  <figure id="QBAr" class="m_original">
    <img src="https://img3.teletype.in/files/61/f5/61f56fd5-d03d-4055-b208-772c6a5b5311.png" width="723" />
  </figure>
  <p id="IQ9x"><strong>Этап 7: Специфические маркеры (памятники, флаги, гербы)</strong></p>
  <p id="tqfW">Это самые сильные маркеры, если их удается идентифицировать.</p>
  <p id="OhRz"><strong>Что искать:</strong></p>
  <ul id="vbTH">
    <li id="yajD"><strong>Флаги:</strong> Государственный флаг часто виден на зданиях, аэродромах.</li>
    <li id="H1Ir"><strong>Гербы:</strong> На зданиях администрации, на гербовых печатях.</li>
    <li id="Wzbl"><strong>Памятники:</strong> Статуи, мемориалы, монументы.</li>
    <li id="4yT0"><strong>Вывески с названиями:</strong> Улицы, города, крупные сети магазинов.</li>
  </ul>
  <p id="Qrb5"><strong>Инструменты:</strong></p>
  <ul id="RmTd">
    <li id="TJun"><strong>Поиск по изображению (Reverse Image Search):</strong> Загружаем фото памятника в Google Images.</li>
    <li id="VGfI"><strong>Википедия:</strong> Поиск по описанию памятника.</li>
  </ul>
  <p id="3DNE"><strong>Практический пример: геолокация сложного фото</strong></p>
  <p id="DDIt"><em>Задача:</em> Определить место съемки по фотографии, где видны: здание с красной крышей, река с лодками, белая церковь с голубым куполом.</p>
  <ol id="iVcQ">
    <li id="HVEW"><strong>Язык:</strong> Надпись на здании - по-русски. - Россия или страны СНГ.</li>
    <li id="YHJc"><strong>Архитектура:</strong> Церковь с голубым куполом - православная. Белые стены, голубой купол - характерно для многих регионов, но чаще на севере России.</li>
    <li id="EQ66"><strong>Ландшафт:</strong> Река, лодки, на заднем плане лесистые холмы - северный регион.</li>
    <li id="vCRx"><strong>Поиск по фрагментам:</strong> Вывеска на здании - «Гостиница Рыбацкая». Поиск в Яндекс - есть отель с таким названием в городе Петрозаводск.</li>
    <li id="205m"><strong>Сравнение с панорамами:</strong> Яндекс.Панорамы показывают это здание на набережной Онежского озера в Петрозаводске.</li>
    <li id="4Rp2"><strong>Кросс-референс:</strong> В новостях Петрозаводска есть упоминания о реке и церкви, совпадающие по описанию.</li>
    <li id="I1Rs"><strong>Результат:</strong> Координаты: 61.79° N, 34.39° E (Петрозаводск, Россия).</li>
  </ol>
  <p id="zBeW"><strong>Этап 8: Поиск по уникальным элементам с помощью Google Lens и поисковиков</strong></p>
  <p id="38qO">Google Lens (мобильное приложение) позволяет сканировать объекты на фото и искать информацию о них. Это часто быстрее, чем вручную искать по тексту.</p>
  <p id="RcLF"><strong>Использование Google Lens:</strong></p>
  <ol id="sS7c">
    <li id="4vNE">Открыть фото в приложении Google.</li>
    <li id="5Nr9">Обвести объект (вывеску, памятник, здание).</li>
    <li id="vMuz">Посмотреть результаты поиска.</li>
  </ol>
  <p id="3qTY"><strong>Использование Yandex.Images:</strong></p>
  <ul id="iE0k">
    <li id="isKB">Загрузка фото и поиск по зданиям, вывескам, ландшафту. В русскоязычном сегменте часто точнее Google.</li>
  </ul>
  <p id="7Wgj"><strong>Этап 9: Составление карты-подсказки (Mind Map)</strong></p>
  <p id="e4Bl">Для системного анализа полезно создать карту-подсказку:</p>
  <pre id="hYUc">Фото
├── Язык → [страны]
├── Архитектура → [стиль]
├── Транспорт
│   ├── Номера → [страна/регион]
│   └── Тип машин → [эпоха / регион]
├── Ландшафт
│   ├── Растительность → [климатическая зона]
│   └── Водоемы → [регион]
├── Инфраструктура
│   ├── Столбы → [региональный стиль]
│   └── Знаки → [страна]
└── Погода → [сезон]</pre>
  <p id="0Y02"><strong>Инструментарий геолокации (сводная таблица)</strong></p>
  <figure id="6KVY" class="m_column">
    <img src="https://img2.teletype.in/files/15/a3/15a34164-0722-42c2-a98a-be3ebd345ad1.png" width="942" />
  </figure>
  <p id="pTHr"><strong>Ошибки при геолокации</strong></p>
  <figure id="Ow3u" class="m_column">
    <img src="https://img4.teletype.in/files/39/9a/399af534-eea6-4970-8edf-acbac15a0db4.png" width="945" />
  </figure>
  <p id="7wjo"></p>
  <p id="ynVP">Геолокация по фотографии — это детективная работа, требующая комбинации знаний из многих областей: географии, архитектуры, лингвистики, ботаники, культурологии. Ключевой принцип — «триангуляция»: никогда не полагаться на один признак. Комбинация языка, архитектуры, транспорта, инфраструктуры и ландшафта позволяет с высокой точностью определить место съемки даже по одному фото.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@specialist_infosec/Introduction-to-GEOINT</guid><link>https://teletype.in/@specialist_infosec/Introduction-to-GEOINT?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec</link><comments>https://teletype.in/@specialist_infosec/Introduction-to-GEOINT?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec#comments</comments><dc:creator>specialist_infosec</dc:creator><title>Введение в GEOINT: география как источник разведывательных данных</title><pubDate>Tue, 21 Jul 2026 16:52:48 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/34/47/344733f0-5796-43aa-8278-7f9420dc072f.png"></media:content><description><![CDATA[<img src="https://img4.teletype.in/files/b6/2e/b62eb4a1-160e-4585-a486-12258a8f332e.png"></img>Введение в GEOINT: география как источник разведывательных данных]]></description><content:encoded><![CDATA[
  <figure id="seQf" class="m_column">
    <img src="https://img4.teletype.in/files/b6/2e/b62eb4a1-160e-4585-a486-12258a8f332e.png" width="2199" />
  </figure>
  <p id="23VM"><strong>Введение в GEOINT: география как источник разведывательных данных</strong></p>
  <p id="mTQR">Геопространственный OSINT (GEOINT) — это направление открытой разведки, специализирующееся на сборе, анализе и интерпретации географической информации. В отличие от других направлений OSINT, GEOINT оперирует пространственными данными: координатами, картами, спутниковыми снимками, аэрофотосъемкой, панорамами улиц и даже астронавигационными методами. Геолокация по фотографии, анализ изменений на спутниковых снимках, создание собственных интерактивных карт — всё это инструменты GEOINT, которые позволяют исследователю ответить на вопросы «Где?», «Когда?», «Что изменилось?» и «Почему именно здесь?».</p>
  <p id="IHaF"><strong>Что такое GEOINT и чем он отличается от других направлений OSINT?</strong></p>
  <p id="cDma">GEOINT (Geospatial Intelligence) — это дисциплина, которая использует географические данные для поддержки принятия решений. В контексте открытой разведки это означает:</p>
  <ol id="edLk">
    <li id="w92d"><strong>Пассивное извлечение географической информации</strong> из фотографий, видео, спутниковых снимков, карт и других открытых источников.</li>
    <li id="MgBp"><strong>Анализ пространственных взаимосвязей</strong> между объектами, событиями и местами.</li>
    <li id="BvxL"><strong>Визуализацию данных на картах</strong> для выявления закономерностей и трендов.</li>
  </ol>
  <p id="fezq">В отличие от транспортного OSINT, где акцент делается на перемещениях и логистике, GEOINT фокусируется на <strong>статической географии</strong>: место съемки, ландшафт, инфраструктура, изменения во времени.</p>
  <p id="oPcz"><strong>Почему GEOINT важен для расследований?</strong></p>
  <figure id="k6CV" class="m_column">
    <img src="https://img4.teletype.in/files/fa/f7/faf7c645-5de6-41af-b1d3-8c51eceb1cfa.png" width="942" />
  </figure>
  <p id="UE1p"><strong>Основные источники данных в GEOINT</strong></p>
  <figure id="MNIS" class="m_column">
    <img src="https://img4.teletype.in/files/7b/22/7b226aa8-0e80-4645-8c99-1e76f0b59d53.png" width="943" />
  </figure>
  <p id="AIZ5"><strong>Методологическая схема GEOINT-расследования</strong></p>
  <ol id="r3lS">
    <li id="jbPU"><strong>Обнаружение контента:</strong> Найдено фото/видео с неизвестной локацией.</li>
    <li id="IbHi"><strong>Предварительный визуальный осмотр:</strong> Анализ видимых элементов — архитектура, язык, природа, транспорт.</li>
    <li id="LsPy"><strong>Извлечение метаданных:</strong> EXIF, GPS, дата съемки (если не удалены).</li>
    <li id="S9wY"><strong>Поиск по изображению (Reverse Image Search):</strong> Проверка, не публиковалось ли уже это фото с известной геолокацией.</li>
    <li id="jmx8"><strong>Идентификация ключевых ориентиров:</strong> Уникальные объекты, которые можно найти на карте.</li>
    <li id="FhF6"><strong>Сравнение с картами и панорамами:</strong> Google Maps, Яндекс.Панорамы, Mapillary.</li>
    <li id="0P4F"><strong>Анализ теней и освещения:</strong> Определение времени суток и сезона.</li>
    <li id="fybs"><strong>Кросс-референс с другими источниками:</strong> Метеоданные, новости, соцсети.</li>
    <li id="6cKa"><strong>Формулирование вывода:</strong> Точные координаты или район съемки, степень уверенности.</li>
  </ol>
  <p id="IJaj"><strong>Ключевые навыки GEOINT-специалиста</strong></p>
  <figure id="ERlb" class="m_column">
    <img src="https://img4.teletype.in/files/38/57/3857672a-b86f-4bfc-9a89-b7793491d0eb.png" width="940" />
  </figure>
  <p id="JgYI"><strong> Юридические ограничения и ответственность в GEOINT</strong></p>
  <ul id="BosU">
    <li id="4EaD">Анализ спутниковых снимков публичных сервисов (Sentinel, Google Earth) легален.</li>
    <li id="5zWC">Использование коммерческих спутниковых снимков (Maxar, Planet) для расследований допускается, но требует лицензии.</li>
    <li id="hBkT">Геолокация частных лиц без их согласия (по фото из соцсетей) может нарушать законы о персональных данных.</li>
    <li id="zf7r">Публикация точных координат секретных объектов (военные базы, ядерные объекты и объекты стратегического назначения) может быть запрещена законодательством.</li>
  </ul>
  <p id="EdZK">GEOINT — это многогранная дисциплина, объединяющая географию, картографию, спутниковые технологии, анализ визуальных данных и методы верификации. Она позволяет исследователю не просто видеть объект, а понимать его пространственный контекст: где он находится, какова его история, что изменилось вокруг. </p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@specialist_infosec/Technical-OSINT-cases</guid><link>https://teletype.in/@specialist_infosec/Technical-OSINT-cases?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec</link><comments>https://teletype.in/@specialist_infosec/Technical-OSINT-cases?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec#comments</comments><dc:creator>specialist_infosec</dc:creator><title>Кейсы технического OSINT: реальные примеры разведки перед пентестом, обнаружения забытых сервисов и выявления уязвимостей</title><pubDate>Tue, 07 Jul 2026 21:39:23 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/ab/46/ab46097e-c5b9-4ef9-8607-9610376389a3.png"></media:content><description><![CDATA[<img src="https://img4.teletype.in/files/f8/12/f81263ff-3ff1-428a-a22e-7be2623ecadb.png"></img>Технический OSINT — это не просто набор инструментов и методик, а практическая дисциплина, которая применяется в реальных сценариях: от оценки безопасности собственной инфраструктуры до разведки конкурентов и расследования инцидентов. Теория без практики мертва. В этой заключительной статье цикла мы разберем несколько реальных кейсов (обезличенных и агрегированных) из практики технической разведки. Каждый кейс демонстрирует применение методов и инструментов, описанных в предыдущих статьях, и показывает, как пассивный сбор данных может привести к обнаружению критических уязвимостей, забытых активов и инсайтов о стратегии организации.]]></description><content:encoded><![CDATA[
  <figure id="qg1C" class="m_column">
    <img src="https://img4.teletype.in/files/f8/12/f81263ff-3ff1-428a-a22e-7be2623ecadb.png" width="2199" />
  </figure>
  <h3 id="6ObK">Кейсы технического OSINT: реальные примеры разведки перед пентестом, обнаружения забытых сервисов и выявления уязвимостей</h3>
  <p id="Ar6x">Технический OSINT — это не просто набор инструментов и методик, а практическая дисциплина, которая применяется в реальных сценариях: от оценки безопасности собственной инфраструктуры до разведки конкурентов и расследования инцидентов. Теория без практики мертва. В этой заключительной статье цикла мы разберем несколько реальных кейсов (обезличенных и агрегированных) из практики технической разведки. Каждый кейс демонстрирует применение методов и инструментов, описанных в предыдущих статьях, и показывает, как пассивный сбор данных может привести к обнаружению критических уязвимостей, забытых активов и инсайтов о стратегии организации.</p>
  <p id="WRBs"><strong>КЕЙС 1: РАЗВЕДКА ПЕРЕД ПЕНТЕСТОМ (ПЛАНОВАЯ ОЦЕНКА ИНФРАСТРУКТУРЫ)</strong></p>
  <p id="26IF"><strong>Исходные данные:</strong> Компания «Альфа» (вымышленное название) заказала внешний пентест своего интернет-периметра. У команды пентеста есть только домен <code>alpha.com</code> и разрешение на активное сканирование. Однако перед любыми активными действиями они проводят пассивную разведку, чтобы не пропустить сервисы, которые не сканируются стандартными средствами, и чтобы правильно спланировать нагрузку.</p>
  <p id="gj2x"><strong>Ход работы:</strong></p>
  <ol id="3qFy">
    <li id="Vqbd"><strong>WHOIS-анализ:</strong> <code>alpha.com</code> зарегистрирован 15 лет назад, регистратор — Namecheap. Контакты скрыты PrivacyGuard. DNS-серверы: <code>ns1.alpha.com</code>, <code>ns2.alpha.com</code>. Это указывает на то, что компания использует собственные DNS-серверы, а значит, возможно, имеет внутреннюю инфраструктуру.</li>
    <li id="m1dq"><strong>DNS-разведка (dig):</strong></li>
    <ul id="ZySp">
      <li id="6ZdV"><code>A</code> → <code>192.0.2.10</code> (основной веб-сервер).</li>
      <li id="mnir"><code>MX</code> → <code>mail.alpha.com</code> (приоритет 10), резервный <code>mx.backup.net</code> (приоритет 20).</li>
      <li id="SuOj"><code>TXT</code> → <code>v=spf1 include:spf.protection.outlook.com -all</code> → используется Office 365.</li>
      <li id="peKh"><code>NS</code> → <code>ns1.alpha.com</code>, <code>ns2.alpha.com</code>.</li>
    </ul>
    <li id="j1pn"><strong>Поиск поддоменов (crt.sh + SecurityTrails):</strong> Найдено 45 поддоменов, в том числе:</li>
    <ul id="Yv1I">
      <li id="wQJR"><code>www.alpha.com</code> (основной сайт).</li>
      <li id="ItLe"><code>mail.alpha.com</code> (почтовый сервер).</li>
      <li id="FRmF"><code>remote.alpha.com</code> (удалённый доступ, предположительно VPN).</li>
      <li id="Fvp1"><code>jenkins.alpha.com</code> (система CI/CD).</li>
      <li id="Kuxm"><code>dev.alpha.com</code> (тестовый поддомен).</li>
      <li id="W16P"><code>old.alpha.com</code> (старый сайт).</li>
    </ul>
    <li id="jEdL"><strong>Анализ SSL-сертификатов:</strong> На <code>jenkins.alpha.com</code> сертификат выдан Let&#x27;s Encrypt, истекает через 10 дней. Это указывает на то, что сервис жив и активно используется (сертификат обновляется). На <code>old.alpha.com</code> сертификат истёк год назад — возможно, сервис уже неактивен, но всё ещё доступен по IP.</li>
    <li id="vg11"><strong>Shodan/Censys по IP-адресам поддоменов:</strong></li>
    <ul id="QYpk">
      <li id="jr90"><code>192.0.2.10</code> (основной сайт) → порты 80, 443. Сканирование баннеров показало Nginx 1.18.0, PHP 7.4.33.</li>
      <li id="J3kl"><code>198.51.100.22</code> (<a href="https://jenkins.alpha.com/" target="_blank">jenkins.alpha.com</a>) → порт 8080 открыт, Jenkins 2.289.1 (уязвимая версия, подверженная CVE-2023-23897).</li>
      <li id="8d8N"><code>203.0.113.45</code> (<a href="https://mail.alpha.com/" target="_blank">mail.alpha.com</a>) → порты 25, 587, 993 (SMTP, SMTP over TLS, IMAPS).</li>
      <li id="ze9z"><code>198.51.100.88</code> (<a href="https://remote.alpha.com/" target="_blank">remote.alpha.com</a>) → порт 443 с сертификатом, но баннер похож на OpenVPN или SSL-прокси.</li>
      <li id="aXcx"><code>192.0.2.50</code> (<a href="https://dev.alpha.com/" target="_blank">dev.alpha.com</a>) → порт 22 (SSH), 80, 443. Баннер SSH: OpenSSH 7.9 (устаревший).</li>
    </ul>
    <li id="NH9i"><strong>WHOIS по IP:</strong> Все IP-адреса принадлежат <code>ALPHA-AS</code> (AS12345), зарегистрированному в RIPE. Это означает, что компания использует собственные IP-адреса и не полагается на облачных провайдеров для этих сервисов.</li>
    <li id="eToZ"><strong>Технологический стек (Wappalyzer):</strong> Основной сайт на Drupal 9.5.3 (уязвимый, CVE-2023-XXXX). Используется jQuery 3.6.0, Bootstrap 4.6. Google Analytics ID <code>UA-123456-1</code>.</li>
    <li id="HYwj"><strong>GitHub-поиск:</strong> Организация <code>alpha-org</code> имеет несколько публичных репозиториев. Один из них — <code>alpha-backend</code> — содержит файл <code>.env.example</code> с реальными паролями (не были заменены на заглушки). В коде встречается строка <code>DB_PASSWORD=AlphaDb2023</code>.</li>
  </ol>
  <p id="upJK"><strong>Итог кейса:</strong></p>
  <figure id="ohHW" class="m_column">
    <img src="https://img1.teletype.in/files/40/71/4071981f-ebfc-4412-bb32-72483070af5b.png" width="1218" />
  </figure>
  <p id="9fI3"><strong>Результат:</strong> Пентест-команда заранее составила карту активов, выявила три критических вектора атаки и спланировала нагрузку на активные сканеры так, чтобы не вызвать сбоев в работе Jenkins и почтового сервера. Обнаруженный пароль был передан клиенту до начала пентеста, что позволило избежать потенциального взлома до тестирования.</p>
  <p id="PKtT"><strong>КЕЙС 2: ОБНАРУЖЕНИЕ ЗАБЫТОГО СЕРВИСА (JENKINS С УЯЗВИМОСТЬЮ)</strong></p>
  <p id="y9Tm"><strong>Исходные данные:</strong> Сотрудник отдела безопасности компании «Бета» проводит аудит собственной инфраструктуры. Он подозревает, что в сети есть забытые сервисы, которые не отслеживаются централизованно. В компании используется домен <code>beta.ru</code>.</p>
  <p id="2z5K"><strong>Ход работы:</strong></p>
  <ol id="RxaR">
    <li id="SQOy"><strong>Поиск поддоменов через crt.sh:</strong><br />Запрос <code>%.beta.ru</code> выдает 120 сертификатов. Среди них есть <code>ci.beta.ru</code>, который не фигурирует во внутренних документах.</li>
    <li id="NGxP"><strong>Проверка доступности (DNS):</strong><br /><code>ci.beta.ru</code> резолвится в <code>192.168.1.100</code> (внутренний IP, но компания использует NAT, и этот IP доступен из интернета через публичный адрес). Это явно внутренний сервер, который был случайно опубликован во внешний DNS.</li>
    <li id="aXc0"><strong>Shodan-поиск по IP (публичному адресу):</strong><br />Порт 8080 открыт, баннер: <code>Jenkins 2.263</code>. Версия Jenkins 2.263 уязвима к CVE-2022-12345 (удалённое выполнение кода через панель управления).</li>
    <li id="kEKF"><strong>Поиск в истории DNS:</strong> SecurityTrails показывает, что <code>ci.beta.ru</code> появился 3 года назад и с тех пор не менял IP. Это подтверждает, что сервис был создан для проекта, который давно завершён, но не выведен из эксплуатации.</li>
    <li id="a8MR"><strong>Проверка сертификата:</strong> Сертификат для <code>ci.beta.ru</code> выдан Let&#x27;s Encrypt и истекает через 45 дней. Это значит, что сервис всё ещё обслуживается (автоматическое обновление сертификатов), но никто не помнит, зачем он нужен.</li>
    <li id="erQL"><strong>Анализ заголовков:</strong> HTTP-заголовки показывают <code>X-Content-Type-Options: nosniff</code>, <code>X-Frame-Options: SAMEORIGIN</code>, что хорошо, но отсутствует CSP. Сам Jenkins доступен без аутентификации (что подтверждается при открытии страницы в браузере — панель администрирования видна).</li>
  </ol>
  <p id="cOWn"><strong>Итог кейса:</strong></p>
  <ul id="A5dX">
    <li id="FqpR">Обнаружен забытый Jenkins-сервер, доступный из интернета с открытой панелью управления.</li>
    <li id="wQLi">Версия Jenkins имеет критическую уязвимость, которая позволяет выполнить произвольные команды на сервере.</li>
    <li id="47cn">Компания «Бета» немедленно закрыла доступ к сервису, ограничив его внутренней сетью, и инициировала процедуру вывода из эксплуатации.</li>
    <li id="kpht">Это предотвратило возможную компрометацию всей инфраструктуры через данный сервер.</li>
  </ul>
  <p id="YVq6"><strong>Вывод:</strong> Даже если вы не проводите активное сканирование, пассивный OSINT (crt.sh, Shodan) может выявить забытые сервисы, о которых не знает даже служба безопасности.</p>
  <p id="EW9M"><strong>КЕЙС 3: РАССЛЕДОВАНИЕ УТЕЧКИ ДАННЫХ ЧЕРЕЗ GITHUB</strong></p>
  <p id="QW1Z"><strong>Исходные данные:</strong> В СМИ появилась новость, что компания «Гамма» (домен <code>gamma.tech</code>) пострадала от утечки данных клиентов. Отдел безопасности должен выяснить, как произошла утечка и какие данные скомпрометированы.</p>
  <p id="iaOm"><strong>Ход работы:</strong></p>
  <ol id="5BN1">
    <li id="iRBW"><strong>Поиск упоминаний компании в GitHub:</strong><br />Использование GitHub Code Search с операторами: <code>&quot;gamma.tech&quot; password</code>, <code>&quot;gamma.tech&quot; api_key</code>, <code>&quot;gamma.tech&quot; secret</code>, <code>&quot;gamma.tech&quot; token</code>. Найдено 15 файлов, содержащих упоминания домена.</li>
    <li id="9Lx2"><strong>Анализ найденных файлов:</strong></li>
    <ul id="F9Qp">
      <li id="gAGu">В репозитории <code>gamma-public</code> есть файл <code>config/database.yml</code>, содержащий строку: <code>password: GammaDB2023!</code>. Этот репозиторий был публичным.</li>
      <li id="Y3EB">В другом репозитории <code>gamma-util</code> в файле <code>creds.py</code> найден AWS-ключ: <code>aws_access_key_id = &quot;AKIAXXXXXXXXXXXXXXXX&quot;</code>, <code>aws_secret_access_key = &quot;abcdefghijklmnopqrstuvwxyz1234567890&quot;</code>. Ключ действителен.</li>
    </ul>
    <li id="mPBD"><strong>Проверка активности ключей:</strong></li>
    <ul id="Pmmt">
      <li id="eqUf">Подключение к AWS CLI с найденным ключом (в безопасной изолированной среде) показывает, что ключ имеет права на чтение S3-бакетов, включая бакет <code>gamma-customer-data</code>.</li>
      <li id="43PE">Список файлов в бакете содержит дампы баз данных с именами клиентов, email-адресами, телефонными номерами и даже хэшированными паролями (но слабый алгоритм хеширования).</li>
    </ul>
    <li id="EZiA"><strong>Поиск других утечек:</strong></li>
    <ul id="V7uN">
      <li id="WBB8">Использование Dehashed и Leak-Lookup по email-адресам сотрудников показывает, что несколько сотрудников использовали корпоративные email для регистрации на сторонних сервисах, пароли от которых были скомпрометированы.</li>
      <li id="S4X0">Это позволило злоумышленникам получить доступ к внутренним репозиториям и опубликовать их.</li>
    </ul>
    <li id="oOC4"><strong>Восстановление цепочки:</strong></li>
    <ul id="lOBo">
      <li id="0utY">Один из разработчиков случайно закоммитил файл с паролями в публичный репозиторий.</li>
      <li id="keYl">Злоумышленник нашёл этот файл через поиск по <code>gamma.tech</code>.</li>
      <li id="aq6X">Используя пароль, получил доступ к базе данных и скачал её.</li>
      <li id="IfWe">Также использовал AWS-ключ для доступа к S3-бакетам.</li>
    </ul>
  </ol>
  <p id="Fqj4"><strong>Итог кейса:</strong></p>
  <ul id="Piqe">
    <li id="3PA3">Причина утечки — человеческая ошибка (публикация секретов в открытом репозитории).</li>
    <li id="oLfl">Скомпрометированы данные более 50 000 клиентов.</li>
    <li id="8idn">Компания «Гамма» немедленно:</li>
    <ul id="Vw3J">
      <li id="gDhN">Отозвала все скомпрометированные ключи и пароли.</li>
      <li id="82q6">Удалила публичные репозитории с секретами.</li>
      <li id="8SAR">Внедрила политику автоматического сканирования всех публичных репозиториев на наличие секретов с помощью TruffleHog.</li>
      <li id="4GZY">Уведомила регулятора и клиентов об утечке (в соответствии с GDPR).</li>
    </ul>
  </ul>
  <p id="p1GB"><strong>Вывод:</strong> Поиск по открытым репозиториям — обязательный элемент мониторинга безопасности. Даже одна публичная строка с паролем может привести к катастрофической утечке.</p>
  <p id="uhJT"><strong>КЕЙС 4: МОНИТОРИНГ КОНКУРЕНТА (ОБНАРУЖЕНИЕ НОВОГО ПРОДУКТА)</strong></p>
  <p id="WWDJ"><strong>Исходные данные:</strong> Компания «Дельта» хочет узнать о планах своего конкурента «Эпсилон» по запуску нового сервиса. Конкурент использует домен <code>epsilon.io</code>.</p>
  <p id="KEfC"><strong>Ход работы:</strong></p>
  <ol id="VAKW">
    <li id="Xcfx"><strong>Настройка мониторинга:</strong></li>
    <ul id="77YR">
      <li id="3hwF">CertSpotter добавлен на домен <code>epsilon.io</code> и <code>*.epsilon.io</code>.</li>
      <li id="krsi">Ежедневный скрипт проверяет crt.sh на новые сертификаты для <code>epsilon.io</code>.</li>
      <li id="wz4w"><a href="https://changedetection.io/" target="_blank">Changedetection.io</a> отслеживает главную страницу <code>epsilon.io</code> и страницу <code>/careers</code> (появление новых вакансий).</li>
    </ul>
    <li id="iLna"><strong>Обнаружение через 2 недели:</strong></li>
    <ul id="hWNP">
      <li id="xpRC">CertSpotter отправляет уведомление: новый сертификат для <code>api.epsilon.io</code>.</li>
      <li id="UTkb">Сертификат выдан Let&#x27;s Encrypt, в SAN указаны <code>api.epsilon.io</code> и <code>*.api.epsilon.io</code>.</li>
    </ul>
    <li id="Qjgf"><strong>Разведка нового поддомена:</strong></li>
    <ul id="7qym">
      <li id="AJbQ"><code>api.epsilon.io</code> разрешается в IP <code>52.0.0.123</code>.</li>
      <li id="eyOq">WHOIS показывает, что IP принадлежит AWS (AS14618).</li>
      <li id="msxH">Порт 443 открыт, баннер HTTP содержит заголовок <code>Server: AmazonS3</code>. Это указывает на статический сайт, размещённый в S3.</li>
      <li id="xysO">Порт 80 тоже открыт, редиректит на 443.</li>
      <li id="EbId">Открыт также порт 3000, на котором виден заголовок <code>X-Powered-By: Express</code>. Это указывает на Node.js-приложение.</li>
      <li id="oRHJ">Shodan показывает, что порт 3000 доступен, но баннер содержит ошибку 404.</li>
    </ul>
    <li id="hByz"><strong>Анализ контента:</strong></li>
    <ul id="BVSD">
      <li id="kCkt">Страница <code>https://api.epsilon.io</code> содержит Swagger-документацию (openapi.json). В ней перечислены эндпоинты для управления заказами, пользователями и платежами.</li>
      <li id="gga5">Это явно новый API-сервис, который ещё не анонсирован.</li>
    </ul>
    <li id="xdr1"><strong>Связь с вакансиями:</strong></li>
    <ul id="uxJ7">
      <li id="9RWs">На странице <code>/careers</code> появилась вакансия «Senior Node.js Developer» для работы над «новым облачным продуктом».</li>
      <li id="6LW6">Это подтверждает, что компания запускает новый B2B-сервис.</li>
    </ul>
  </ol>
  <p id="uT1c"><strong>Итог кейса:</strong></p>
  <ul id="bILw">
    <li id="TT5A">Компания «Дельта» узнала о запуске нового продукта конкурента за месяц до официального анонса.</li>
    <li id="XUxw">Изучив Swagger-документацию, они смогли оценить функционал и скорректировать свою стратегию.</li>
    <li id="Nmps">Информация была передана в стратегический отдел для анализа рынка.</li>
  </ul>
  <p id="2h2R"><strong>Вывод:</strong> Мониторинг новых сертификатов и поддоменов позволяет выявлять неанонсированные проекты и получать рыночное преимущество.</p>
  <p id="lTLj"><strong>КЕЙС 5: ПОИСК СКРЫТЫХ ПОДДОМЕНОВ ЧЕРЕЗ CT-ЛОГИ И WAYBACK MACHINE</strong></p>
  <p id="DfBV"><strong>Исходные данные:</strong> Исследователь безопасности ищет все возможные поддомены компании <code>zeta.space</code> для составления карты инфраструктуры. В DNS и CT-логах есть поддомены <code>www</code>, <code>mail</code>, <code>vpn</code>. Но он подозревает, что есть ещё забытые.</p>
  <p id="YhaX"><strong>Ход работы:</strong></p>
  <ol id="Mwve">
    <li id="1K3i"><strong>crt.sh (стандартный запрос):</strong> <code>%.zeta.space</code> даёт 25 результатов, включая те, что уже известны.</li>
    <li id="3try"><strong>Wayback Machine CDX API:</strong>bashcurl &quot;https://web.archive.org/cdx/search/cdx?url=zeta.space/*&amp;output=json&amp;limit=100000&quot; &gt; all_pages.jsonВ полученном списке страниц исследователь находит упоминания URL <code>staging.zeta.space</code>, <code>dev.zeta.space</code>, <code>test.zeta.space</code>, которые отсутствуют в CT-логах.</li>
    <li id="C84S"><strong>Проверка доступности:</strong></li>
    <ul id="5ZlC">
      <li id="zDc1"><code>staging.zeta.space</code> резолвится в <code>203.0.113.77</code>.</li>
      <li id="AZUl">Порт 80 открыт, на нём видна тестовая страница с ошибкой PHP.</li>
      <li id="Qks6">Порт 443 закрыт (сертификата нет).</li>
    </ul>
    <li id="54Xy"><strong>Анализ контента на Wayback Machine:</strong></li>
    <ul id="Weiz">
      <li id="j4oV">Сохранённая копия <code>staging.zeta.space</code> от 2 лет назад содержит полную структуру каталогов: <code>/admin</code>, <code>/backup</code>, <code>/old</code>. В папке <code>/backup</code> лежит файл <code>db_backup_2022.sql</code>.</li>
    </ul>
    <li id="74dJ"><strong>Поиск по файлу в Wayback Machine:</strong></li>
    <ul id="iJO0">
      <li id="Kmb9">Исследователь скачивает <code>db_backup_2022.sql</code> (через архивную ссылку) и находит в нём email-адреса сотрудников, включая адреса IT-отдела.</li>
    </ul>
    <li id="JDmD"><strong>Использование email для дальнейшей разведки:</strong></li>
    <ul id="mvLm">
      <li id="KrTj">Через утечки (Dehashed) находится, что один из email использовался на форумах, где пароль был скомпрометирован.</li>
    </ul>
  </ol>
  <p id="pO9u"><strong>Итог кейса:</strong></p>
  <ul id="3o1X">
    <li id="6vHX">Обнаружены забытые поддомены (<code>staging</code>, <code>dev</code>, <code>test</code>), которые всё ещё доступны и содержат уязвимые версии ПО.</li>
    <li id="VMRd">Найден дамп базы данных, который может содержать конфиденциальную информацию.</li>
    <li id="aZOx">Получены email-адреса для дальнейшей социальной инженерии или проверки утечек.</li>
  </ul>
  <p id="oyqv"><strong>Вывод:</strong> Wayback Machine — мощный инструмент для обнаружения поддоменов, которые уже не существуют в DNS или CT-логах, но всё ещё могут быть доступны по IP или содержать следы в архивах.</p>
  <p id="70Cv"><strong>КЕЙС 6: КОМПЛЕКСНОЕ РАССЛЕДОВАНИЕ (ПОЛНАЯ КАРТИНА ЗА 1 ЧАС)</strong></p>
  <p id="1cxi"><strong>Исходные данные:</strong> Аналитик получает задачу: «Что мы знаем о компании <code>omega.tech</code>?» У него есть только домен.</p>
  <p id="gmyF"><strong>Ход работы (пошагово, с таймингами):</strong></p>
  <ol id="8E4D">
    <li id="PXk5"><strong>WHOIS (2 мин):</strong> <code>omega.tech</code> зарегистрирован через GoDaddy, дата создания 2015-03-10, NS: <code>ns1.omega.tech</code>, контакты скрыты.</li>
    <li id="1dUE"><strong>DNS (3 мин):</strong></li>
    <ul id="X3BW">
      <li id="8XIm">A → <code>54.0.0.10</code>.</li>
      <li id="PAbF">MX → <code>mail.omega.tech</code> (приоритет 10), <code>mx2.omega.tech</code> (20).</li>
      <li id="7OlB">TXT → <code>v=spf1 include:_spf.google.com ~all</code> (использует Google Workspace).</li>
    </ul>
    <li id="3UkB"><strong>Поддомены (crt.sh + SecurityTrails) (5 мин):</strong></li>
    <ul id="WKBt">
      <li id="7L36">Найдено 30 поддоменов, включая <code>jenkins</code>, <code>gitlab</code>, <code>monitoring</code>, <code>dev</code>.</li>
    </ul>
    <li id="P6NM"><strong>Анализ IP и ASN (5 мин):</strong></li>
    <ul id="ioQB">
      <li id="NXnD"><code>54.0.0.10</code> принадлежит AWS (AS14618), регион <code>us-east-1</code>.</li>
      <li id="QY1m">Другие IP поддоменов принадлежат тому же AWS-аккаунту (определено по схожести IP-диапазонов).</li>
    </ul>
    <li id="1boF"><strong>Shodan/Censys по IP (5 мин):</strong></li>
    <ul id="3ZgT">
      <li id="MzWL">На <code>54.0.0.10</code>: порты 80, 443 (Nginx 1.20.0, PHP 8.0).</li>
      <li id="JCPv">На <code>54.0.0.22</code> (jenkins): порт 8080 (Jenkins 2.303, уязвимый).</li>
      <li id="RsPE">На <code>54.0.0.33</code> (gitlab): порты 80, 443 (GitLab 14.5).</li>
    </ul>
    <li id="2Vd0"><strong>SSL-сертификаты (3 мин):</strong></li>
    <ul id="cL9J">
      <li id="7tY0">Все сертификаты выданы Let&#x27;s Encrypt, истекают через 20 дней. Это указывает на единую политику управления.</li>
    </ul>
    <li id="gsVE"><strong>Технологический стек (3 мин):</strong></li>
    <ul id="WVdf">
      <li id="0VpO">Wappalyzer: основной сайт на WordPress 5.9 с плагинами: WooCommerce, Yoast SEO, WP Rocket. Тема — OceanWP.</li>
    </ul>
    <li id="UK5t"><strong>GitHub-поиск (5 мин):</strong></li>
    <ul id="ZdTT">
      <li id="5cXy"><code>omega.tech</code> упоминается в 7 репозиториях. В одном из них есть файл <code>.env</code> с открытым ключом SMTP.</li>
    </ul>
    <li id="4v2y"><strong>Wayback Machine (5 мин):</strong></li>
    <ul id="s6Y9">
      <li id="7ziQ">Найдена старая версия сайта 2017 года с контактами и старым списком сотрудников.</li>
    </ul>
    <li id="Ilyt"><strong>Сборка карты инфраструктуры (остальное время):</strong></li>
    <ul id="wV56">
      <li id="J8Vy">Визуализация в Maltego: домен → IP → ASN → поддомены → сервисы.</li>
    </ul>
  </ol>
  <p id="D2aV"><strong>Итог за 1 час:</strong></p>
  <figure id="LmNz" class="m_column">
    <img src="https://img2.teletype.in/files/1b/18/1b18bc6d-9e4c-4a2f-a136-dbd9fb4d3a39.png" width="1218" />
  </figure>
  <p id="0zcf"><strong>Принятое решение:</strong> Команда безопасности немедленно приступила к анализу обнаруженных уязвимостей, а стратегический отдел использовал информацию о технологиях и инфраструктуре для оценки конкурентных рисков.</p>
  <p id="lGU6"><strong>ЭТИЧЕСКИЕ МОМЕНТЫ: ЧТО ДЕЛАТЬ С НАЙДЕННЫМ</strong></p>
  <p id="DYWx">Каждый из описанных кейсов поднимает этические вопросы. Вот несколько правил:</p>
  <ol id="7UDn">
    <li id="CmQo"><strong>Если вы нашли секрет (пароль, ключ), НЕ ИСПОЛЬЗУЙТЕ ЕГО</strong> для доступа к системе. Сообщите владельцу через официальный канал (security@company.com, программа Bug Bounty). В некоторых случаях можно использовать отчетные платформы (HackerOne, Bugcrowd).</li>
    <li id="mJyq"><strong>Не распространяйте найденные данные.</strong> Если вы обнаружили дамп базы данных с персональными данными, не копируйте их, не делитесь ими. Просто уведомите владельца.</li>
    <li id="iZWm"><strong>Соблюдайте законы о персональных данных (GDPR, 152-ФЗ).</strong> В некоторых юрисдикциях даже хранение найденных данных может быть нарушением.</li>
    <li id="BG8I"><strong>Если вы мониторите конкурента в коммерческих целях</strong>, используйте только публичные источники. Активное сканирование или взлом — это шпионаж и уголовное преступление.</li>
    <li id="2jNn"><strong>В пентесте с разрешением</strong> — все найденное документируется и передается заказчику. Не используйте уязвимости для получения несанкционированного доступа, даже если они есть.</li>
  </ol>
  <p id="B923"><strong>ЗАКЛЮЧЕНИЕ</strong></p>
  <p id="50bu">Технический OSINT — это системный подход к сбору информации об инфраструктуре организаций из открытых источников. Шестнадцать статей этого цикла охватили полный спектр: от DNS-разведки и поиска поддоменов до анализа SSL-сертификатов, портов, заголовков, уязвимостей и репозиториев кода. Мы рассмотрели как базовые инструменты (dig, whois, curl, Wappalyzer), так и продвинутые (Amass, Shodan, Censys, TruffleHog, Grafana, Dash). Мы научились автоматизировать сбор данных, строить дашборды и применять полученные знания в реальных кейсах.</p>
  <p id="RMbB">Главные выводы цикла:</p>
  <ul id="xxks">
    <li id="fIDL"><strong>Пассивная разведка безопасна и легальна</strong> (в отличие от активного сканирования), но даёт до 80% информации об инфраструктуре.</li>
    <li id="Lxjp"><strong>Комбинируйте источники</strong>: DNS, CT-логи, архивы, GitHub, Shodan — каждый добавляет свои уникальные данные.</li>
    <li id="tME0"><strong>Автоматизация — ключ к масштабу</strong>: ручной сбор годится для одного-двух доменов; для десятков и сотен нужны скрипты.</li>
    <li id="Cyqp"><strong>Интерпретация важнее сбора</strong>: найденные данные нужно анализировать, визуализировать и применять.</li>
    <li id="q9La"><strong>Этика — основа профессии</strong>: собранная информация должна использоваться для защиты, а не для атак.</li>
  </ul>
  <p id="a0IJ">Технический OSINT — это не просто навык, а образ мышления, при котором вы видите интернет как гигантскую базу данных, из которой можно извлекать ценную информацию, не нарушая законов и прав других людей.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@specialist_infosec/BhH2i_kZYn1</guid><link>https://teletype.in/@specialist_infosec/BhH2i_kZYn1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec</link><comments>https://teletype.in/@specialist_infosec/BhH2i_kZYn1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec#comments</comments><dc:creator>specialist_infosec</dc:creator><title>Создание дашборда для мониторинга инфраструктуры цели</title><pubDate>Tue, 07 Jul 2026 21:33:24 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/f2/21/f2211d1a-d72f-46a4-8345-81be1b638bcb.png"></media:content><description><![CDATA[<img src="https://img4.teletype.in/files/f1/7a/f17a6c0e-92d1-4610-8494-88cd300dabe7.png"></img>После того как все этапы технического OSINT автоматизированы и данные собираются в базу данных, возникает следующая задача: как сделать эту информацию доступной, наглядной и удобной для анализа. Отчёты в виде JSON или CSV хороши для машинной обработки, но для человека визуализация в виде графиков, карт, таблиц и интерактивных панелей значительно эффективнее. Дашборд (dashboard) — это единая панель управления, которая отображает ключевые показатели, изменения в инфраструктуре, новые поддомены, открытые порты, уязвимости и другие данные в реальном времени (или близком к нему). Данная статья посвящена созданию веб-дашборда для мониторинга инфраструктуры цели с использованием открытых инструментов.]]></description><content:encoded><![CDATA[
  <figure id="fn07" class="m_retina">
    <img src="https://img4.teletype.in/files/f1/7a/f17a6c0e-92d1-4610-8494-88cd300dabe7.png" width="1099" />
  </figure>
  <h3 id="L9sV">Создание дашборда для мониторинга инфраструктуры цели</h3>
  <p id="64EC">После того как все этапы технического OSINT автоматизированы и данные собираются в базу данных, возникает следующая задача: как сделать эту информацию доступной, наглядной и удобной для анализа. Отчёты в виде JSON или CSV хороши для машинной обработки, но для человека визуализация в виде графиков, карт, таблиц и интерактивных панелей значительно эффективнее. Дашборд (dashboard) — это единая панель управления, которая отображает ключевые показатели, изменения в инфраструктуре, новые поддомены, открытые порты, уязвимости и другие данные в реальном времени (или близком к нему). Данная статья посвящена созданию веб-дашборда для мониторинга инфраструктуры цели с использованием открытых инструментов.</p>
  <p id="aLhK"><strong>Зачем нужен дашборд в техническом OSINT?</strong></p>
  <figure id="umTR" class="m_column">
    <img src="https://img1.teletype.in/files/86/1b/861b4f42-c7ad-41dc-a04a-de2b1fc8d476.png" width="1212" />
  </figure>
  <p id="xvtX"><strong>Подходы к созданию дашборда</strong></p>
  <figure id="IJz6" class="m_column">
    <img src="https://img3.teletype.in/files/28/f1/28f1681c-0af0-479a-ad9f-81d4f7ec1db2.png" width="1111" />
  </figure>
  <p id="IL0v">В этой статье мы рассмотрим два основных подхода:</p>
  <ol id="0ICw">
    <li id="z4GP"><strong>Быстрый старт с Grafana</strong> — минимальная настройка, мощная визуализация.</li>
    <li id="yqIF"><strong>Гибкий дашборд на Python (Dash)</strong> — полный контроль над данными и интерфейсом.</li>
  </ol>
  <p id="LSAX"><strong>ВАРИАНТ 1: ДАШБОРД НА GRAFANA + SQLITE</strong></p>
  <p id="8b0x">Grafana — это популярная платформа для мониторинга и визуализации данных. Она поддерживает множество источников данных, включая SQLite, PostgreSQL, Prometheus, Elasticsearch. Для нашего случая SQLite подходит отлично — данные уже хранятся в ней.</p>
  <p id="LEt1"><strong>Архитектура:</strong></p>
  <pre id="BdHm">SQLite Database → Grafana (с плагином SQLite) → Дашборд с панелями</pre>
  <p id="WhaW"><strong>Установка Grafana:</strong></p>
  <pre id="vHzp"># Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y software-properties-common
sudo add-apt-repository &quot;deb https://packages.grafana.com/oss/deb stable main&quot;
sudo apt-get update
sudo apt-get install grafana

# Запуск
sudo systemctl enable grafana-server
sudo systemctl start grafana-server</pre>
  <p id="PaYb">Grafana будет доступна по адресу <code>http://localhost:3000</code> (логин/пароль по умолчанию: admin/admin).</p>
  <p id="ea1L"><strong>Подключение SQLite к Grafana:</strong></p>
  <p id="Apic">Стандартный Grafana не поддерживает SQLite напрямую. Нужно использовать плагин:</p>
  <ol id="jF0V">
    <li id="VqZE">Установить плагин <code>grafana-sqlite-datasource</code>:bashsudo grafana-cli plugins install frser-sqlite-datasource sudo systemctl restart grafana-server</li>
    <li id="TgYO">В веб-интерфейсе Grafana: Configuration → Data Sources → Add data source → SQLite.</li>
    <ul id="aswZ">
      <li id="GAfP">Указать путь к файлу БД: <code>/path/to/osint.db</code></li>
    </ul>
  </ol>
  <p id="Yg4b"><strong>Создание панелей в Grafana:</strong></p>
  <p id="HiT8"><strong>Панель 1: Количество поддоменов за последние 30 дней (гистограмма)</strong></p>
  <pre id="dMNn">SELECT 
    DATE(discovered_at) as date,
    COUNT(*) as count
FROM subdomains
WHERE discovered_at &gt; datetime(&#x27;now&#x27;, &#x27;-30 days&#x27;)
GROUP BY DATE(discovered_at)
ORDER BY date ASC</pre>
  <p id="dxa2"><strong>Панель 2: Таблица последних поддоменов</strong></p>
  <pre id="x8Lh">SELECT subdomain, discovered_at
FROM subdomains
ORDER BY discovered_at DESC
LIMIT 20</pre>
  <p id="m38b"><strong>Панель 3: Истекающие сертификаты (таблица)</strong></p>
  <pre id="DLnK">SELECT name, valid_to, 
       julianday(valid_to) - julianday(&#x27;now&#x27;) as days_left
FROM certificates
WHERE days_left &lt; 30 AND days_left &gt; 0
ORDER BY days_left ASC</pre>
  <p id="wT9Q"><strong>Панель 4: Распределение открытых портов (круговая диаграмма)</strong></p>
  <pre id="IYbz">SELECT port, COUNT(*) as count
FROM shodan_ports
GROUP BY port
ORDER BY count DESC
LIMIT 10</pre>
  <p id="Y2ig"><strong>Grafana преимущества:</strong></p>
  <ul id="IzpG">
    <li id="mdru">Быстрая настройка, красивые графики &quot;из коробки&quot;.</li>
    <li id="2I7e">Автообновление данных.</li>
    <li id="EpLL">Поддержка алертов (email, Telegram, Slack).</li>
  </ul>
  <p id="BwFZ"><strong>Grafana недостатки:</strong></p>
  <ul id="3Y8c">
    <li id="wAi1">Ограниченная кастомизация (сложно добавить нестандартную логику).</li>
    <li id="OPQk">Нужно устанавливать плагин для SQLite (или использовать PostgreSQL).</li>
  </ul>
  <p id="LqgI"><strong>ВАРИАНТ 2: ГИБКИЙ ДАШБОРД НА PYTHON (DASH + PLOTLY)</strong></p>
  <p id="BtIp">Dash — это веб-фреймворк от Plotly для создания аналитических дашбордов на Python. Он позволяет полностью контролировать логику обработки данных и визуализацию, не требуя знаний JavaScript.</p>
  <p id="fRl6"><strong>Архитектура:</strong></p>
  <pre id="WIXY">SQLite Database → Python (Dash) → Веб-интерфейс (HTML/CSS/JS)</pre>
  <p id="abS9"><strong>Установка зависимостей:</strong></p>
  <pre id="ERsQ">pip install dash plotly pandas</pre>
  <p id="gDnS"><strong>Структура проекта:</strong></p>
  <pre id="vyvA">dashboard/
├── app.py              # Главный файл приложения
├── data/
│   └── osint.db        # База данных
├── assets/
│   └── style.css       # Стили (опционально)
└── requirements.txt</pre>
  <p id="X8in"><strong>Создание дашборда (app.py):</strong></p>
  <pre id="Bq32">import dash
from dash import dcc, html, Input, Output, State
import plotly.graph_objects as go
import plotly.express as px
import sqlite3
import pandas as pd
from datetime import datetime, timedelta

# Инициализация приложения
app = dash.Dash(__name__, title=&quot;OSINT Dashboard&quot;)
server = app.server

# Функция подключения к БД
def get_db_connection():
    conn = sqlite3.connect(&#x27;data/osint.db&#x27;)
    conn.row_factory = sqlite3.Row
    return conn

# ============ ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ============

def get_subdomain_history(domain=None, days=30):
    &quot;&quot;&quot;Получение истории поддоменов за последние N дней&quot;&quot;&quot;
    conn = get_db_connection()
    query = &quot;&quot;&quot;
        SELECT DATE(discovered_at) as date, COUNT(*) as count
        FROM subdomains s
        JOIN domains d ON s.domain_id = d.id
        WHERE d.domain = ? AND discovered_at &gt; datetime(&#x27;now&#x27;, &#x27;-&#x27; || ? || &#x27; days&#x27;)
        GROUP BY DATE(discovered_at)
        ORDER BY date ASC
    &quot;&quot;&quot;
    df = pd.read_sql_query(query, conn, params=(domain, days))
    conn.close()
    return df

def get_recent_subdomains(domain=None, limit=20):
    &quot;&quot;&quot;Получение последних поддоменов&quot;&quot;&quot;
    conn = get_db_connection()
    query = &quot;&quot;&quot;
        SELECT s.subdomain, s.discovered_at
        FROM subdomains s
        JOIN domains d ON s.domain_id = d.id
        WHERE d.domain = ?
        ORDER BY s.discovered_at DESC
        LIMIT ?
    &quot;&quot;&quot;
    df = pd.read_sql_query(query, conn, params=(domain, limit))
    conn.close()
    return df

def get_expiring_certs(domain=None, days=30):
    &quot;&quot;&quot;Получение истекающих сертификатов&quot;&quot;&quot;
    conn = get_db_connection()
    query = &quot;&quot;&quot;
        SELECT name, issuer, valid_to,
               julianday(valid_to) - julianday(&#x27;now&#x27;) as days_left
        FROM certificates c
        JOIN domains d ON c.domain_id = d.id
        WHERE d.domain = ? AND days_left &lt; ? AND days_left &gt; 0
        ORDER BY days_left ASC
    &quot;&quot;&quot;
    df = pd.read_sql_query(query, conn, params=(domain, days))
    conn.close()
    return df

def get_port_distribution(domain=None):
    &quot;&quot;&quot;Распределение открытых портов&quot;&quot;&quot;
    conn = get_db_connection()
    query = &quot;&quot;&quot;
        SELECT port, COUNT(*) as count
        FROM shodan_ports sp
        JOIN domains d ON sp.domain_id = d.id
        WHERE d.domain = ?
        GROUP BY port
        ORDER BY count DESC
        LIMIT 15
    &quot;&quot;&quot;
    df = pd.read_sql_query(query, conn, params=(domain,))
    conn.close()
    return df

def get_tech_stack(domain=None):
    &quot;&quot;&quot;Технологический стек&quot;&quot;&quot;
    conn = get_db_connection()
    # Предположим, есть таблица technologies
    query = &quot;&quot;&quot;
        SELECT tech_name, COUNT(*) as count
        FROM technologies t
        JOIN domains d ON t.domain_id = d.id
        WHERE d.domain = ?
        GROUP BY tech_name
        ORDER BY count DESC
        LIMIT 15
    &quot;&quot;&quot;
    # Если таблицы нет — заглушка
    df = pd.DataFrame({&#x27;tech_name&#x27;: [&#x27;nginx&#x27;, &#x27;PHP&#x27;, &#x27;WordPress&#x27;, &#x27;jQuery&#x27;, &#x27;Bootstrap&#x27;],
                       &#x27;count&#x27;: [1, 1, 1, 1, 1]})
    conn.close()
    return df

def get_all_domains():
    &quot;&quot;&quot;Список всех отслеживаемых доменов&quot;&quot;&quot;
    conn = get_db_connection()
    df = pd.read_sql_query(&quot;SELECT domain FROM domains ORDER BY domain ASC&quot;, conn)
    conn.close()
    return df[&#x27;domain&#x27;].tolist()

# ============ МАКЕТ ДАШБОРДА ============

app.layout = html.Div([
    # Заголовок
    html.H1(&quot;📊 OSINT Infrastructure Dashboard&quot;, style={&#x27;textAlign&#x27;: &#x27;center&#x27;, &#x27;marginBottom&#x27;: 30}),
    
    # Выбор домена
    html.Div([
        html.Label(&quot;Выберите домен:&quot;),
        dcc.Dropdown(
            id=&#x27;domain-selector&#x27;,
            options=[{&#x27;label&#x27;: d, &#x27;value&#x27;: d} for d in get_all_domains()],
            value=&#x27;example.com&#x27; if &#x27;example.com&#x27; in get_all_domains() else get_all_domains()[0] if get_all_domains() else None,
            style={&#x27;width&#x27;: &#x27;50%&#x27;, &#x27;margin&#x27;: &#x27;10px 0&#x27;}
        )
    ], style={&#x27;padding&#x27;: &#x27;0 20px&#x27;}),
    
    # Верхние карточки (метрики)
    html.Div([
        html.Div([
            html.H3(&quot;🌐 Всего поддоменов&quot;),
            html.H2(id=&#x27;total-subdomains&#x27;, style={&#x27;color&#x27;: &#x27;#2196F3&#x27;})
        ], className=&#x27;metric-card&#x27;),
        html.Div([
            html.H3(&quot;🔄 Изменений за 7 дней&quot;),
            html.H2(id=&#x27;changes-7d&#x27;, style={&#x27;color&#x27;: &#x27;#FF9800&#x27;})
        ], className=&#x27;metric-card&#x27;),
        html.Div([
            html.H3(&quot;⏳ Истекающих сертификатов (&lt;30 дн)&quot;),
            html.H2(id=&#x27;expiring-certs&#x27;, style={&#x27;color&#x27;: &#x27;#F44336&#x27;})
        ], className=&#x27;metric-card&#x27;),
        html.Div([
            html.H3(&quot;🔓 Открытых портов&quot;),
            html.H2(id=&#x27;total-ports&#x27;, style={&#x27;color&#x27;: &#x27;#4CAF50&#x27;})
        ], className=&#x27;metric-card&#x27;),
    ], style={&#x27;display&#x27;: &#x27;flex&#x27;, &#x27;gap&#x27;: &#x27;20px&#x27;, &#x27;padding&#x27;: &#x27;0 20px&#x27;, &#x27;flexWrap&#x27;: &#x27;wrap&#x27;}),
    
    # Графики (сетка 2x2)
    html.Div([
        # График 1: История поддоменов
        html.Div([
            dcc.Graph(id=&#x27;subdomain-history&#x27;)
        ], className=&#x27;graph-card&#x27;),
        
        # График 2: Распределение портов
        html.Div([
            dcc.Graph(id=&#x27;port-distribution&#x27;)
        ], className=&#x27;graph-card&#x27;),
        
        # График 3: Истекающие сертификаты
        html.Div([
            dcc.Graph(id=&#x27;cert-expiry&#x27;)
        ], className=&#x27;graph-card&#x27;),
        
        # График 4: Технологический стек
        html.Div([
            dcc.Graph(id=&#x27;tech-stack&#x27;)
        ], className=&#x27;graph-card&#x27;),
    ], style={&#x27;display&#x27;: &#x27;grid&#x27;, &#x27;gridTemplateColumns&#x27;: &#x27;1fr 1fr&#x27;, &#x27;gap&#x27;: &#x27;20px&#x27;, &#x27;padding&#x27;: &#x27;20px&#x27;}),
    
    # Таблица последних поддоменов
    html.Div([
        html.H3(&quot;🆕 Последние поддомены&quot;),
        html.Div(id=&#x27;recent-subdomains-table&#x27;, style={&#x27;overflowX&#x27;: &#x27;auto&#x27;})
    ], style={&#x27;padding&#x27;: &#x27;0 20px&#x27;, &#x27;marginTop&#x27;: 20}),
    
    # Автообновление (раз в 60 секунд)
    dcc.Interval(
        id=&#x27;interval-component&#x27;,
        interval=60 * 1000,  # 60 секунд
        n_intervals=0
    )
], style={&#x27;maxWidth&#x27;: &#x27;1400px&#x27;, &#x27;margin&#x27;: &#x27;0 auto&#x27;, &#x27;fontFamily&#x27;: &#x27;Arial, sans-serif&#x27;})

# ============ КОЛБЭКИ ============

@app.callback(
    [
        Output(&#x27;total-subdomains&#x27;, &#x27;children&#x27;),
        Output(&#x27;changes-7d&#x27;, &#x27;children&#x27;),
        Output(&#x27;expiring-certs&#x27;, &#x27;children&#x27;),
        Output(&#x27;total-ports&#x27;, &#x27;children&#x27;),
        Output(&#x27;subdomain-history&#x27;, &#x27;figure&#x27;),
        Output(&#x27;port-distribution&#x27;, &#x27;figure&#x27;),
        Output(&#x27;cert-expiry&#x27;, &#x27;figure&#x27;),
        Output(&#x27;tech-stack&#x27;, &#x27;figure&#x27;),
        Output(&#x27;recent-subdomains-table&#x27;, &#x27;children&#x27;),
    ],
    [Input(&#x27;domain-selector&#x27;, &#x27;value&#x27;),
     Input(&#x27;interval-component&#x27;, &#x27;n_intervals&#x27;)]
)
def update_dashboard(domain, n_intervals):
    if not domain:
        return &quot;—&quot;, &quot;—&quot;, &quot;—&quot;, &quot;—&quot;, {}, {}, {}, {}, &quot;Выберите домен&quot;
    
    # 1. Количество поддоменов
    conn = get_db_connection()
    total_sub = pd.read_sql_query(
        &quot;SELECT COUNT(*) as cnt FROM subdomains s JOIN domains d ON s.domain_id = d.id WHERE d.domain = ?&quot;,
        conn, params=(domain,)
    ).iloc[0][&#x27;cnt&#x27;]
    conn.close()
    
    # 2. Изменения за 7 дней (новые поддомены)
    conn = get_db_connection()
    changes_7d = pd.read_sql_query(
        &quot;SELECT COUNT(*) as cnt FROM subdomains s JOIN domains d ON s.domain_id = d.id WHERE d.domain = ? AND discovered_at &gt; datetime(&#x27;now&#x27;, &#x27;-7 days&#x27;)&quot;,
        conn, params=(domain,)
    ).iloc[0][&#x27;cnt&#x27;]
    conn.close()
    
    # 3. Истекающие сертификаты
    expiring_df = get_expiring_certs(domain)
    expiring_count = len(expiring_df)
    
    # 4. Открытые порты
    ports_df = get_port_distribution(domain)
    total_ports = ports_df[&#x27;count&#x27;].sum() if not ports_df.empty else 0
    
    # 5. График истории поддоменов
    history_df = get_subdomain_history(domain)
    fig_history = px.line(
        history_df, x=&#x27;date&#x27;, y=&#x27;count&#x27;,
        title=&#x27;Поддомены по дням&#x27;,
        labels={&#x27;date&#x27;: &#x27;Дата&#x27;, &#x27;count&#x27;: &#x27;Количество&#x27;}
    ) if not history_df.empty else go.Figure()
    
    # 6. Распределение портов
    fig_ports = px.pie(
        ports_df, values=&#x27;count&#x27;, names=&#x27;port&#x27;,
        title=&#x27;Распределение портов&#x27;
    ) if not ports_df.empty else go.Figure()
    
    # 7. Истекающие сертификаты (бар-чарт)
    if not expiring_df.empty:
        fig_certs = px.bar(
            expiring_df, x=&#x27;name&#x27;, y=&#x27;days_left&#x27;,
            title=&#x27;Дней до истечения сертификата&#x27;,
            labels={&#x27;name&#x27;: &#x27;Домен&#x27;, &#x27;days_left&#x27;: &#x27;Дней&#x27;},
            color=&#x27;days_left&#x27;,
            color_continuous_scale=&#x27;RdYlGn_r&#x27;
        )
    else:
        fig_certs = go.Figure()
        fig_certs.add_annotation(text=&quot;Нет истекающих сертификатов&quot;, showarrow=False)
    
    # 8. Технологический стек
    tech_df = get_tech_stack(domain)
    fig_tech = px.bar(
        tech_df, x=&#x27;count&#x27;, y=&#x27;tech_name&#x27;,
        orientation=&#x27;h&#x27;,
        title=&#x27;Технологии&#x27;,
        labels={&#x27;count&#x27;: &#x27;Количество&#x27;, &#x27;tech_name&#x27;: &#x27;Технология&#x27;}
    ) if not tech_df.empty else go.Figure()
    
    # 9. Таблица последних поддоменов
    recent_df = get_recent_subdomains(domain)
    if not recent_df.empty:
        table = html.Table([
            html.Thead(html.Tr([
                html.Th(&quot;Поддомен&quot;, style={&#x27;textAlign&#x27;: &#x27;left&#x27;}),
                html.Th(&quot;Обнаружен&quot;, style={&#x27;textAlign&#x27;: &#x27;left&#x27;})
            ])),
            html.Tbody([
                html.Tr([
                    html.Td(row[&#x27;subdomain&#x27;]),
                    html.Td(row[&#x27;discovered_at&#x27;])
                ]) for _, row in recent_df.iterrows()
            ])
        ], style={&#x27;width&#x27;: &#x27;100%&#x27;, &#x27;borderCollapse&#x27;: &#x27;collapse&#x27;})
    else:
        table = &quot;Нет данных&quot;
    
    return (
        total_sub,
        changes_7d,
        expiring_count,
        total_ports,
        fig_history,
        fig_ports,
        fig_certs,
        fig_tech,
        table
    )

# ============ ЗАПУСК ============

if __name__ == &#x27;__main__&#x27;:
    app.run_server(debug=True, host=&#x27;0.0.0.0&#x27;, port=8050)</pre>
  <p id="nAIo"><strong>Добавление CSS-стилей (assets/style.css):</strong></p>
  <pre id="g3lI">.metric-card {
    background: white;
    border-radius: 10px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    padding: 20px;
    flex: 1;
    min-width: 150px;
    text-align: center;
}
.metric-card h3 {
    margin: 0 0 10px 0;
    font-size: 14px;
    color: #666;
}
.metric-card h2 {
    margin: 0;
    font-size: 32px;
}

.graph-card {
    background: white;
    border-radius: 10px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.05);
    padding: 15px;
}

table {
    border: 1px solid #ddd;
}
table th {
    background: #f5f5f5;
    padding: 10px;
    border-bottom: 2px solid #ddd;
}
table td {
    padding: 8px 10px;
    border-bottom: 1px solid #eee;
}
table tr:hover {
    background: #f9f9f9;
}</pre>
  <p id="cvr4"><strong>Запуск дашборда:</strong></p>
  <pre id="37Gm">python app.py</pre>
  <p id="VMDS">Дашборд будет доступен по адресу: <code>http://localhost:8050</code></p>
  <p id="itio"><strong>ВАРИАНТ 3: БЫСТРЫЙ СТАТИЧЕСКИЙ ОТЧЁТ (HTML)</strong></p>
  <p id="g1bF">Если веб-сервер не требуется, можно генерировать статический HTML-отчёт с графиками.</p>
  <p id="zwox"><strong>Использование библиотеки <code>matplotlib</code> + <code>mpld3</code> для интерактивных графиков в HTML:</strong></p>
  <pre id="5bo1">import matplotlib.pyplot as plt
import mpld3
import sqlite3
import pandas as pd

def generate_report(domain):
    # Получение данных
    history_df = get_subdomain_history(domain)
    ports_df = get_port_distribution(domain)
    
    # Генерация графиков
    fig1, ax1 = plt.subplots()
    ax1.plot(history_df[&#x27;date&#x27;], history_df[&#x27;count&#x27;])
    ax1.set_title(&#x27;Поддомены по дням&#x27;)
    fig1_html = mpld3.fig_to_html(fig1)
    
    fig2, ax2 = plt.subplots()
    ax2.pie(ports_df[&#x27;count&#x27;], labels=ports_df[&#x27;port&#x27;], autopct=&#x27;%1.1f%%&#x27;)
    ax2.set_title(&#x27;Распределение портов&#x27;)
    fig2_html = mpld3.fig_to_html(fig2)
    
    # Сборка HTML
    html = f&quot;&quot;&quot;
    &lt;html&gt;
    &lt;head&gt;&lt;title&gt;Отчёт по {domain}&lt;/title&gt;&lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;OSINT Отчёт: {domain}&lt;/h1&gt;
        &lt;div&gt;{fig1_html}&lt;/div&gt;
        &lt;div&gt;{fig2_html}&lt;/div&gt;
    &lt;/body&gt;
    &lt;/html&gt;
    &quot;&quot;&quot;
    with open(f&#x27;report_{domain}.html&#x27;, &#x27;w&#x27;) as f:
        f.write(html)</pre>
  <p id="4nCr"><strong>ВАРИАНТ 4: ИСПОЛЬЗОВАНИЕ SPIDERFOOT</strong></p>
  <p id="SGmD">SpiderFoot — это OSINT-фреймворк с собственным веб-интерфейсом. Он автоматически собирает данные из множества источников и визуализирует их в виде графа связей.</p>
  <p id="GfuL"><strong>Установка SpiderFoot:</strong></p>
  <pre id="rJct"># Docker (рекомендуемый способ)
docker run -p 5000:5000 spiderfoot/spiderfoot</pre>
  <p id="Qbfm"><strong>Особенности SpiderFoot:</strong></p>
  <ul id="zuMP">
    <li id="izRC">Графовое представление связей (домены, IP, email, хэши).</li>
    <li id="21lP">Автоматический сбор из 200+ модулей.</li>
    <li id="W8Iu">Встроенный дашборд с картами и таймлайнами.</li>
  </ul>
  <p id="OII0">требует времени на настройку, может быть избыточным для простых задач.</p>
  <p id="4GKF"><strong>ВАРИАНТ 5: MALTEGO ДЛЯ ВИЗУАЛИЗАЦИИ СВЯЗЕЙ</strong></p>
  <p id="Zosd">Maltego — это инструмент для визуализации связей между сущностями (доменами, IP, личностями). В сочетании с трансформами он превращает собранные данные в интерактивный граф.</p>
  <p id="HnKT"><strong>Как использовать:</strong></p>
  <ul id="NchE">
    <li id="O4Am">Экспортировать данные из SQLite в CSV.</li>
    <li id="Puwk">Импортировать в Maltego как &quot;сущности&quot; (Entities).</li>
    <li id="OsAX">Использовать трансформы для дальнейшего обогащения.</li>
  </ul>
  <p id="3Z1A"><strong>СРАВНЕНИЕ ИНСТРУМЕНТОВ ДЛЯ ДАШБОРДОВ</strong></p>
  <figure id="qnDS" class="m_column">
    <img src="https://img2.teletype.in/files/16/e5/16e51c88-41e9-4eb5-b150-03abdaeacc22.png" width="1021" />
  </figure>
  <hr />
  <p id="r1Qd"><strong>Практический пример: использование дашборда для мониторинга</strong></p>
  <p id="L0aZ"><em>Сценарий:</em> аналитик мониторит 5 доменов конкурентов.</p>
  <ol id="Ia4M">
    <li id="JRkY"><strong>Ежедневный сбор</strong> → данные попадают в SQLite через автоматизированный конвейер.</li>
    <li id="IxKa"><strong>Дашборд (Dash)</strong> → отображает для каждого домена:</li>
    <ul id="BBUi">
      <li id="ItnT">Количество поддоменов (график роста).</li>
      <li id="JnVt">Новые поддомены за неделю (таблица).</li>
      <li id="gwAt">Открытые порты и сервисы.</li>
      <li id="LHFo">Истекающие сертификаты (алерт).</li>
    </ul>
    <li id="VAEp"><strong>Обнаружение:</strong> на дашборде появляется новый поддомен <code>newapp.competitor.com</code>. Аналитик видит, что это произошло сегодня. Через Shodan выясняется, что на нём открыт порт 443 с сертификатом от AWS. Вывод: конкурент запускает новый облачный сервис.</li>
    <li id="2Mbo"><strong>Реакция:</strong> информация передаётся в стратегический отдел.</li>
  </ol>
  <p id="McSX"><strong>РЕКОМЕНДАЦИИ ПО ВЫБОРУ</strong></p>
  <figure id="hq6v" class="m_column">
    <img src="https://img2.teletype.in/files/9e/ba/9eba62b3-2fc1-4351-aaf4-12c16beaa60a.png" width="1216" />
  </figure>
  <p id="iX4P">Дашборд — это завершающий элемент системы технического OSINT, превращающий сырые данные в понятную и удобную для анализа информацию. Выбор инструмента зависит от задач: Grafana подходит для быстрого старта и оперативного мониторинга, Dash/Plotly — для глубокой кастомизации, Maltego — для визуализации связей. Интеграция дашборда с автоматизированным конвейером (из предыдущей статьи) создаёт полноценную систему непрерывной разведки, где данные собираются, обрабатываются и отображаются в реальном времени.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@specialist_infosec/Automation-of-technical-OSINT</guid><link>https://teletype.in/@specialist_infosec/Automation-of-technical-OSINT?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec</link><comments>https://teletype.in/@specialist_infosec/Automation-of-technical-OSINT?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec#comments</comments><dc:creator>specialist_infosec</dc:creator><title>Автоматизация технического OSINT: сбор данных в скриптах</title><pubDate>Tue, 07 Jul 2026 21:25:14 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/ae/90/ae9083f8-4e43-48b6-9e57-763c4eb57308.png"></media:content><description><![CDATA[<img src="https://img3.teletype.in/files/6d/73/6d732144-e975-4bb8-aa26-7ef8c3fd4900.png"></img>Ручное выполнение всех этапов технического OSINT для одного домена может занять несколько часов. При необходимости мониторинга десятков или сотен целей ручной сбор становится невозможным. Автоматизация превращает разрозненные запросы к API, парсинг веб-страниц и анализ данных в единый воспроизводимый конвейер, который можно запускать по расписанию. Правильно построенная система автоматизации позволяет получать актуальную информацию о целях без участия человека, экономить время и избегать ошибок. Данная статья посвящена методам и практическим примерам автоматизации технического OSINT с использованием Python и Bash.]]></description><content:encoded><![CDATA[
  <figure id="hZDx" class="m_retina">
    <img src="https://img3.teletype.in/files/6d/73/6d732144-e975-4bb8-aa26-7ef8c3fd4900.png" width="1099" />
  </figure>
  <h3 id="2VPe">Автоматизация технического OSINT: сбор данных в скриптах (Python, Bash)</h3>
  <p id="XflP">Ручное выполнение всех этапов технического OSINT для одного домена может занять несколько часов. При необходимости мониторинга десятков или сотен целей ручной сбор становится невозможным. Автоматизация превращает разрозненные запросы к API, парсинг веб-страниц и анализ данных в единый воспроизводимый конвейер, который можно запускать по расписанию. Правильно построенная система автоматизации позволяет получать актуальную информацию о целях без участия человека, экономить время и избегать ошибок. Данная статья посвящена методам и практическим примерам автоматизации технического OSINT с использованием Python и Bash.</p>
  <p id="QXBl"><strong>Зачем автоматизировать технический OSINT?</strong></p>
  <figure id="81Ld" class="m_column">
    <img src="https://img1.teletype.in/files/c0/d2/c0d2d0a4-c511-4f78-a6bc-fb5386806413.png" width="1081" />
  </figure>
  <p id="Q7AX"><strong>Подходы к автоматизации</strong></p>
  <figure id="HNa1" class="m_column">
    <img src="https://img1.teletype.in/files/c5/1a/c51a00d1-fef9-43a9-a828-c02683c298ed.png" width="1213" />
  </figure>
  <p id="wjc1"><strong>Этап 1: Структура проекта</strong></p>
  <p id="DeEP">Рекомендуемая структура каталога для автоматизации:</p>
  <pre id="06MT">osint-automation/
├── config/
│   ├── config.yaml            # API ключи, настройки
│   └── targets.txt            # Список доменов (по одному на строку)
├── modules/
│   ├── dns.py                 # Сбор DNS-записей
│   ├── whois.py               # WHOIS-информация
│   ├── subdomains.py          # Поиск поддоменов (crt.sh, SecurityTrails)
│   ├── certificates.py        # SSL/TLS сертификаты
│   ├── shodan.py              # Shodan-данные
│   ├── tech_stack.py          # Wappalyzer/BuiltWith технологии
│   ├── github.py              # Поиск секретов в GitHub
│   └── changes.py             # Сравнение с предыдущим состоянием
├── utils/
│   ├── logger.py              # Логирование
│   ├── notifier.py            # Отправка уведомлений (Telegram, Email)
│   └── db.py                  # Работа с SQLite
├── data/                      # Хранилище результатов (JSON, SQLite)
├── main.py                    # Главный скрипт
├── requirements.txt           # Зависимости Python
└── run.sh                     # Bash-обёртка для cron</pre>
  <p id="ATyE"><strong>Этап 2: Настройка конфигурации (config.yaml)</strong></p>
  <pre id="QNHF"># config.yaml
api_keys:
  shodan: &quot;SHODAN_API_KEY&quot;
  securitytrails: &quot;SECURITYTRAILS_API_KEY&quot;
  github: &quot;GITHUB_TOKEN&quot;
  virustotal: &quot;VIRUSTOTAL_API_KEY&quot;  # опционально
  censys:
    api_id: &quot;CENSYS_API_ID&quot;
    api_secret: &quot;CENSYS_API_SECRET&quot;

notifications:
  telegram:
    token: &quot;TELEGRAM_BOT_TOKEN&quot;
    chat_id: &quot;TELEGRAM_CHAT_ID&quot;
  email:
    smtp_server: &quot;smtp.gmail.com&quot;
    smtp_port: 587
    username: &quot;user@gmail.com&quot;
    password: &quot;app_password&quot;
    to: &quot;recipient@example.com&quot;

database:
  path: &quot;data/osint.db&quot;

targets:
  file: &quot;config/targets.txt&quot;

logging:
  level: &quot;INFO&quot;
  file: &quot;data/osint.log&quot;

scan:
  # интервал повторных запросов для Shodan (если нужно ждать)
  shodan_delay: 1  # секунды между запросами
  timeout: 30  # таймаут для HTTP-запросов</pre>
  <p id="CiQd"><strong>Этап 3: Сбор DNS-записей (modules/dns.py)</strong></p>
  <pre id="XsPK">import dns.resolver
import json
from utils.logger import get_logger

logger = get_logger(__name__)

def get_dns_records(domain):
    &quot;&quot;&quot;
    Сбор основных DNS-записей: A, AAAA, MX, TXT, NS, CNAME, SOA
    &quot;&quot;&quot;
    records = {}
    record_types = [&#x27;A&#x27;, &#x27;AAAA&#x27;, &#x27;MX&#x27;, &#x27;TXT&#x27;, &#x27;NS&#x27;, &#x27;CNAME&#x27;, &#x27;SOA&#x27;]
    
    for rtype in record_types:
        try:
            answers = dns.resolver.resolve(domain, rtype)
            records[rtype] = [str(r) for r in answers]
        except dns.resolver.NoAnswer:
            records[rtype] = []
        except dns.resolver.NXDOMAIN:
            records[rtype] = []
        except Exception as e:
            logger.warning(f&quot;DNS {rtype} для {domain}: {e}&quot;)
            records[rtype] = []
    
    return records

def get_reverse_dns(ip):
    &quot;&quot;&quot;
    Обратный DNS (PTR) для IP-адреса
    &quot;&quot;&quot;
    try:
        answers = dns.resolver.resolve_address(ip)
        return [str(r) for r in answers]
    except:
        return []

# Пример использования
if __name__ == &quot;__main__&quot;:
    records = get_dns_records(&quot;example.com&quot;)
    print(json.dumps(records, indent=2))</pre>
  <p id="bgEY"><strong>Этап 4: WHOIS-информация (modules/whois.py)</strong></p>
  <pre id="RSVZ">import whois
import json
from datetime import datetime

def get_whois_info(domain):
    &quot;&quot;&quot;
    Получение WHOIS-информации о домене
    &quot;&quot;&quot;
    try:
        w = whois.whois(domain)
        return {
            &#x27;domain_name&#x27;: w.domain_name,
            &#x27;registrar&#x27;: w.registrar,
            &#x27;creation_date&#x27;: str(w.creation_date),
            &#x27;expiration_date&#x27;: str(w.expiration_date),
            &#x27;updated_date&#x27;: str(w.updated_date),
            &#x27;name_servers&#x27;: w.name_servers,
            &#x27;emails&#x27;: w.emails,
            &#x27;org&#x27;: w.org,
            &#x27;country&#x27;: w.country,
        }
    except Exception as e:
        return {&#x27;error&#x27;: str(e)}</pre>
  <p id="bHjT"><strong>Этап 5: Поиск поддоменов через crt.sh (modules/subdomains.py)</strong></p>
  <pre id="sTXe">import requests
import json
from utils.logger import get_logger

logger = get_logger(__name__)

def get_subdomains_crtsh(domain):
    &quot;&quot;&quot;
    Получение поддоменов из Certificate Transparency логов через crt.sh
    &quot;&quot;&quot;
    url = f&quot;https://crt.sh/?q=%25.{domain}&amp;output=json&quot;
    subdomains = set()
    try:
        resp = requests.get(url, timeout=30)
        if resp.status_code == 200:
            data = resp.json()
            for entry in data:
                name = entry.get(&#x27;name_value&#x27;, &#x27;&#x27;)
                if name.endswith(f&#x27;.{domain}&#x27;) or name == domain:
                    # убираем wildcard
                    if name.startswith(&#x27;*.&#x27;):
                        name = name[2:]
                    subdomains.add(name.lower())
        else:
            logger.warning(f&quot;crt.sh ошибка: {resp.status_code}&quot;)
    except Exception as e:
        logger.error(f&quot;crt.sh исключение: {e}&quot;)
    
    return sorted(subdomains)

def get_subdomains_securitytrails(domain, api_key):
    &quot;&quot;&quot;
    Получение поддоменов через SecurityTrails API (требуется ключ)
    &quot;&quot;&quot;
    if not api_key:
        return []
    url = f&quot;https://api.securitytrails.com/v1/domain/{domain}/subdomains&quot;
    headers = {&quot;APIKEY&quot;: api_key}
    try:
        resp = requests.get(url, headers=headers, timeout=30)
        if resp.status_code == 200:
            data = resp.json()
            subdomains = data.get(&#x27;subdomains&#x27;, [])
            return [f&quot;{s}.{domain}&quot; for s in subdomains]
    except Exception as e:
        logger.error(f&quot;SecurityTrails ошибка: {e}&quot;)
    return []

def get_all_subdomains(domain, securitytrails_key=None):
    &quot;&quot;&quot;
    Агрегация поддоменов из всех источников
    &quot;&quot;&quot;
    all_subs = set()
    all_subs.update(get_subdomains_crtsh(domain))
    if securitytrails_key:
        all_subs.update(get_subdomains_securitytrails(domain, securitytrails_key))
    return sorted(all_subs)</pre>
  <p id="2heD"><strong>Этап 6: Анализ SSL/TLS сертификатов (modules/certificates.py)</strong></p>
  <pre id="rNR9">import requests
import json
from datetime import datetime

def get_certificates_crtsh(domain):
    &quot;&quot;&quot;
    Получение информации о сертификатах для домена
    &quot;&quot;&quot;
    url = f&quot;https://crt.sh/?q=%25.{domain}&amp;output=json&quot;
    certs = []
    try:
        resp = requests.get(url, timeout=30)
        if resp.status_code == 200:
            data = resp.json()
            for entry in data:
                if len(entry) &lt; 6:
                    continue
                certs.append({
                    &#x27;name&#x27;: entry[1],
                    &#x27;issuer&#x27;: entry[2],
                    &#x27;valid_from&#x27;: entry[3],
                    &#x27;valid_to&#x27;: entry[4],
                    &#x27;ca&#x27;: entry[5]
                })
    except Exception as e:
        pass
    return certs

def get_certificate_expiry(certs):
    &quot;&quot;&quot;
    Определение ближайшего истекающего сертификата
    &quot;&quot;&quot;
    now = datetime.now()
    expiring = []
    for cert in certs:
        try:
            valid_to = datetime.strptime(cert[&#x27;valid_to&#x27;], &#x27;%Y-%m-%dT%H:%M:%S&#x27;)
            days_left = (valid_to - now).days
            if days_left &lt; 30:
                expiring.append({
                    &#x27;name&#x27;: cert[&#x27;name&#x27;],
                    &#x27;expires&#x27;: cert[&#x27;valid_to&#x27;],
                    &#x27;days_left&#x27;: days_left
                })
        except:
            pass
    return expiring</pre>
  <p id="EOVj"><strong>Этап 7: Сбор данных из Shodan (modules/shodan.py)</strong></p>
  <pre id="biXv">import shodan
from utils.logger import get_logger

logger = get_logger(__name__)

def get_shodan_host(ip, api_key):
    &quot;&quot;&quot;
    Получение информации о хосте из Shodan
    &quot;&quot;&quot;
    if not api_key:
        return {}
    try:
        api = shodan.Shodan(api_key)
        host = api.host(ip)
        return {
            &#x27;ip&#x27;: host.get(&#x27;ip_str&#x27;),
            &#x27;organization&#x27;: host.get(&#x27;org&#x27;),
            &#x27;isp&#x27;: host.get(&#x27;isp&#x27;),
            &#x27;country&#x27;: host.get(&#x27;country_name&#x27;),
            &#x27;ports&#x27;: host.get(&#x27;ports&#x27;, []),
            &#x27;vulns&#x27;: host.get(&#x27;vulns&#x27;, []),
            &#x27;data&#x27;: host.get(&#x27;data&#x27;, [])  # баннеры
        }
    except shodan.APIError as e:
        logger.error(f&quot;Shodan ошибка для {ip}: {e}&quot;)
        return {}
    except Exception as e:
        logger.error(f&quot;Shodan исключение для {ip}: {e}&quot;)
        return {}

def get_shodan_host_by_domain(domain, api_key):
    &quot;&quot;&quot;
    Поиск хостов, связанных с доменом, через Shodan
    &quot;&quot;&quot;
    if not api_key:
        return []
    try:
        api = shodan.Shodan(api_key)
        results = api.search(f&quot;hostname:{domain}&quot;)
        hosts = []
        for match in results.get(&#x27;matches&#x27;, []):
            hosts.append({
                &#x27;ip&#x27;: match.get(&#x27;ip_str&#x27;),
                &#x27;port&#x27;: match.get(&#x27;port&#x27;),
                &#x27;data&#x27;: match.get(&#x27;data&#x27;, &#x27;&#x27;)[:200]
            })
        return hosts
    except shodan.APIError as e:
        logger.error(f&quot;Shodan search ошибка: {e}&quot;)
        return []</pre>
  <p id="2cUw"><strong>Этап 8: Поиск в GitHub (modules/github.py)</strong></p>
  <pre id="1MIV">import requests
from utils.logger import get_logger

logger = get_logger(__name__)

def search_github_secrets(domain, token):
    &quot;&quot;&quot;
    Поиск упоминаний домена и подозрительных паттернов в GitHub
    &quot;&quot;&quot;
    if not token:
        return []
    
    # Комбинированный запрос: упоминания домена + файлы .env, пароли, ключи
    queries = [
        f&#x27;&quot;{domain}&quot; extension:env&#x27;,
        f&#x27;&quot;{domain}&quot; password&#x27;,
        f&#x27;&quot;{domain}&quot; api_key&#x27;,
        f&#x27;&quot;{domain}&quot; secret&#x27;,
        f&#x27;&quot;{domain}&quot; token&#x27;,
    ]
    
    headers = {
        &quot;Authorization&quot;: f&quot;token {token}&quot;,
        &quot;Accept&quot;: &quot;application/vnd.github.v3+json&quot;
    }
    
    all_results = []
    for query in queries:
        url = &quot;https://api.github.com/search/code&quot;
        params = {&quot;q&quot;: query, &quot;per_page&quot;: 20}
        try:
            resp = requests.get(url, headers=headers, params=params, timeout=20)
            if resp.status_code == 200:
                data = resp.json()
                for item in data.get(&#x27;items&#x27;, []):
                    all_results.append({
                        &#x27;repo&#x27;: item[&#x27;repository&#x27;][&#x27;full_name&#x27;],
                        &#x27;path&#x27;: item[&#x27;path&#x27;],
                        &#x27;url&#x27;: item[&#x27;html_url&#x27;],
                        &#x27;query&#x27;: query
                    })
            else:
                logger.warning(f&quot;GitHub ошибка ({query}): {resp.status_code}&quot;)
        except Exception as e:
            logger.error(f&quot;GitHub исключение: {e}&quot;)
    
    return all_results</pre>
  <p id="V7Yf"><strong>Этап 9: Технологический стек (modules/tech_stack.py)</strong></p>
  <pre id="fXQI">import subprocess
import json
from utils.logger import get_logger

logger = get_logger(__name__)

def get_tech_wappalyzer(url):
    &quot;&quot;&quot;
    Определение технологий через Wappalyzer CLI
    Требуется установить: npm install -g wappalyzer
    &quot;&quot;&quot;
    try:
        result = subprocess.run(
            [&#x27;wappalyzer&#x27;, url],
            capture_output=True,
            text=True,
            timeout=60
        )
        if result.returncode == 0:
            return json.loads(result.stdout)
        else:
            logger.warning(f&quot;Wappalyzer ошибка: {result.stderr}&quot;)
            return {}
    except Exception as e:
        logger.error(f&quot;Wappalyzer исключение: {e}&quot;)
        return {}

def get_tech_builtwith(url, api_key=None):
    &quot;&quot;&quot;
    Использование BuiltWith API (требуется ключ)
    &quot;&quot;&quot;
    if not api_key:
        return {}
    endpoint = f&quot;https://api.builtwith.com/free1/api.json?KEY={api_key}&amp;LOOKUP={url}&quot;
    try:
        import requests
        resp = requests.get(endpoint, timeout=30)
        if resp.status_code == 200:
            return resp.json()
    except Exception as e:
        logger.error(f&quot;BuiltWith ошибка: {e}&quot;)
    return {}</pre>
  <p id="wp81"><strong>Этап 10: Общая база данных (utils/db.py)</strong></p>
  <p id="CIzm">Используем SQLite для хранения истории изменений.</p>
  <pre id="RDKl">import sqlite3
import json
from datetime import datetime

class OSINTDatabase:
    def __init__(self, db_path):
        self.conn = sqlite3.connect(db_path)
        self.create_tables()
    
    def create_tables(self):
        cursor = self.conn.cursor()
        cursor.execute(&#x27;&#x27;&#x27;
            CREATE TABLE IF NOT EXISTS domains (
                id INTEGER PRIMARY KEY,
                domain TEXT UNIQUE,
                first_seen TEXT,
                last_updated TEXT
            )
        &#x27;&#x27;&#x27;)
        cursor.execute(&#x27;&#x27;&#x27;
            CREATE TABLE IF NOT EXISTS subdomains (
                id INTEGER PRIMARY KEY,
                domain_id INTEGER,
                subdomain TEXT,
                discovered_at TEXT,
                FOREIGN KEY(domain_id) REFERENCES domains(id)
            )
        &#x27;&#x27;&#x27;)
        cursor.execute(&#x27;&#x27;&#x27;
            CREATE TABLE IF NOT EXISTS dns_records (
                id INTEGER PRIMARY KEY,
                domain_id INTEGER,
                record_type TEXT,
                record_value TEXT,
                discovered_at TEXT,
                FOREIGN KEY(domain_id) REFERENCES domains(id)
            )
        &#x27;&#x27;&#x27;)
        cursor.execute(&#x27;&#x27;&#x27;
            CREATE TABLE IF NOT EXISTS certificates (
                id INTEGER PRIMARY KEY,
                domain_id INTEGER,
                name TEXT,
                issuer TEXT,
                valid_from TEXT,
                valid_to TEXT,
                discovered_at TEXT,
                FOREIGN KEY(domain_id) REFERENCES domains(id)
            )
        &#x27;&#x27;&#x27;)
        cursor.execute(&#x27;&#x27;&#x27;
            CREATE TABLE IF NOT EXISTS shodan_ports (
                id INTEGER PRIMARY KEY,
                domain_id INTEGER,
                ip TEXT,
                port INTEGER,
                data TEXT,
                discovered_at TEXT,
                FOREIGN KEY(domain_id) REFERENCES domains(id)
            )
        &#x27;&#x27;&#x27;)
        cursor.execute(&#x27;&#x27;&#x27;
            CREATE TABLE IF NOT EXISTS changes (
                id INTEGER PRIMARY KEY,
                domain TEXT,
                change_type TEXT,
                old_value TEXT,
                new_value TEXT,
                detected_at TEXT
            )
        &#x27;&#x27;&#x27;)
        self.conn.commit()
    
    def get_domain_id(self, domain):
        cursor = self.conn.cursor()
        cursor.execute(&quot;SELECT id FROM domains WHERE domain = ?&quot;, (domain,))
        row = cursor.fetchone()
        if row:
            return row[0]
        # Создаём новый
        cursor.execute(&quot;INSERT INTO domains (domain, first_seen, last_updated) VALUES (?, ?, ?)&quot;,
                       (domain, datetime.now().isoformat(), datetime.now().isoformat()))
        self.conn.commit()
        return cursor.lastrowid
    
    def save_subdomains(self, domain_id, subdomains):
        cursor = self.conn.cursor()
        for sub in subdomains:
            cursor.execute(&quot;INSERT OR IGNORE INTO subdomains (domain_id, subdomain, discovered_at) VALUES (?, ?, ?)&quot;,
                           (domain_id, sub, datetime.now().isoformat()))
        self.conn.commit()
    
    def save_dns_records(self, domain_id, records):
        cursor = self.conn.cursor()
        for rtype, values in records.items():
            for val in values:
                cursor.execute(&quot;INSERT INTO dns_records (domain_id, record_type, record_value, discovered_at) VALUES (?, ?, ?, ?)&quot;,
                               (domain_id, rtype, val, datetime.now().isoformat()))
        self.conn.commit()
    
    def save_certificates(self, domain_id, certs):
        cursor = self.conn.cursor()
        for cert in certs:
            cursor.execute(&quot;INSERT INTO certificates (domain_id, name, issuer, valid_from, valid_to, discovered_at) VALUES (?, ?, ?, ?, ?, ?)&quot;,
                           (domain_id, cert.get(&#x27;name&#x27;), cert.get(&#x27;issuer&#x27;), cert.get(&#x27;valid_from&#x27;), cert.get(&#x27;valid_to&#x27;), datetime.now().isoformat()))
        self.conn.commit()
    
    def log_change(self, domain, change_type, old_value, new_value):
        cursor = self.conn.cursor()
        cursor.execute(&quot;INSERT INTO changes (domain, change_type, old_value, new_value, detected_at) VALUES (?, ?, ?, ?, ?)&quot;,
                       (domain, change_type, old_value, new_value, datetime.now().isoformat()))
        self.conn.commit()
    
    def close(self):
        self.conn.close()</pre>
  <p id="DNFE"><strong>Этап 11: Главный конвейер (main.py)</strong></p>
  <pre id="3wKi">#!/usr/bin/env python3
import os
import sys
import json
import yaml
from datetime import datetime
from utils.logger import setup_logger
from utils.db import OSINTDatabase
from utils.notifier import send_telegram
from modules.dns import get_dns_records
from modules.whois import get_whois_info
from modules.subdomains import get_all_subdomains
from modules.certificates import get_certificates_crtsh, get_certificate_expiry
from modules.shodan import get_shodan_host_by_domain
from modules.tech_stack import get_tech_wappalyzer
from modules.github import search_github_secrets
from modules.changes import compare_and_notify

# Загрузка конфига
with open(&#x27;config/config.yaml&#x27;, &#x27;r&#x27;) as f:
    config = yaml.safe_load(f)

logger = setup_logger(config[&#x27;logging&#x27;][&#x27;level&#x27;], config[&#x27;logging&#x27;][&#x27;file&#x27;])
db = OSINTDatabase(config[&#x27;database&#x27;][&#x27;path&#x27;])

def process_domain(domain):
    logger.info(f&quot;Обработка {domain}&quot;)
    
    # Получаем ID домена в БД
    domain_id = db.get_domain_id(domain)
    
    # 1. WHOIS
    whois_data = get_whois_info(domain)
    logger.info(f&quot;WHOIS для {domain}: {whois_data.get(&#x27;registrar&#x27;, &#x27;unknown&#x27;)}&quot;)
    
    # 2. DNS записи
    dns = get_dns_records(domain)
    db.save_dns_records(domain_id, dns)
    logger.info(f&quot;DNS: A={len(dns.get(&#x27;A&#x27;, []))}, MX={len(dns.get(&#x27;MX&#x27;, []))}&quot;)
    
    # 3. Поддомены
    sec_key = config[&#x27;api_keys&#x27;].get(&#x27;securitytrails&#x27;)
    subdomains = get_all_subdomains(domain, sec_key)
    db.save_subdomains(domain_id, subdomains)
    logger.info(f&quot;Найдено {len(subdomains)} поддоменов&quot;)
    
    # 4. Сертификаты
    certs = get_certificates_crtsh(domain)
    db.save_certificates(domain_id, certs)
    expiring = get_certificate_expiry(certs)
    if expiring:
        logger.warning(f&quot;Сертификаты истекают: {expiring}&quot;)
    
    # 5. Shodan (по поддоменам, ограничимся первыми 10 IP)
    shodan_key = config[&#x27;api_keys&#x27;].get(&#x27;shodan&#x27;)
    if shodan_key and subdomains:
        for sub in subdomains[:10]:
            hosts = get_shodan_host_by_domain(sub, shodan_key)
            # сохраняем в БД
            for h in hosts:
                pass  # упрощённо
    
    # 6. Технологический стек
    if subdomains:
        main_url = f&quot;https://{domain}&quot;
        tech = get_tech_wappalyzer(main_url)
        logger.info(f&quot;Технологии: {tech.get(&#x27;technologies&#x27;, [])}&quot;)
    
    # 7. GitHub секреты
    github_token = config[&#x27;api_keys&#x27;].get(&#x27;github&#x27;)
    if github_token:
        secrets = search_github_secrets(domain, github_token)
        if secrets:
            logger.warning(f&quot;Найдены потенциальные секреты в GitHub: {len(secrets)}&quot;)
            send_telegram(
                config[&#x27;notifications&#x27;][&#x27;telegram&#x27;][&#x27;token&#x27;],
                config[&#x27;notifications&#x27;][&#x27;telegram&#x27;][&#x27;chat_id&#x27;],
                f&quot;Обнаружены секреты для {domain}: {len(secrets)} файлов&quot;
            )
    
    # 8. Сравнение изменений
    compare_and_notify(domain, config, db)
    
    logger.info(f&quot;Обработка {domain} завершена&quot;)

def main():
    targets_file = config[&#x27;targets&#x27;][&#x27;file&#x27;]
    with open(targets_file, &#x27;r&#x27;) as f:
        domains = [line.strip() for line in f if line.strip()]
    
    for domain in domains:
        try:
            process_domain(domain)
        except Exception as e:
            logger.error(f&quot;Ошибка для {domain}: {e}&quot;)
    
    db.close()
    logger.info(&quot;Все задачи выполнены&quot;)

if __name__ == &quot;__main__&quot;:
    main()</pre>
  <p id="dokO"><strong>Этап 12: Сравнение изменений и уведомления (modules/changes.py)</strong></p>
  <pre id="xs0S">import json
import hashlib
import os
from utils.notifier import send_telegram

def load_previous_state(domain, data_dir=&#x27;data/states&#x27;):
    &quot;&quot;&quot;Загрузка предыдущего состояния из JSON&quot;&quot;&quot;
    os.makedirs(data_dir, exist_ok=True)
    state_file = os.path.join(data_dir, f&quot;{domain}_state.json&quot;)
    if os.path.exists(state_file):
        with open(state_file, &#x27;r&#x27;) as f:
            return json.load(f)
    return {}

def save_current_state(domain, state, data_dir=&#x27;data/states&#x27;):
    state_file = os.path.join(data_dir, f&quot;{domain}_state.json&quot;)
    with open(state_file, &#x27;w&#x27;) as f:
        json.dump(state, f, indent=2)

def compare_and_notify(domain, config, db):
    &quot;&quot;&quot;Сравнение текущего состояния с предыдущим и уведомление об изменениях&quot;&quot;&quot;
    # Собираем текущее состояние в словарь
    current = {
        &#x27;subdomains&#x27;: sorted(db.get_subdomains(domain)),
        &#x27;dns&#x27;: db.get_dns_records(domain),
        &#x27;certs&#x27;: db.get_certificates(domain),
    }
    
    previous = load_previous_state(domain)
    
    changes = []
    
    # Сравнение поддоменов
    if previous:
        old_subs = set(previous.get(&#x27;subdomains&#x27;, []))
        new_subs = set(current.get(&#x27;subdomains&#x27;, []))
        added = new_subs - old_subs
        removed = old_subs - new_subs
        if added:
            changes.append(f&quot;Новые поддомены: {&#x27;, &#x27;.join(added)}&quot;)
        if removed:
            changes.append(f&quot;Удалены: {&#x27;, &#x27;.join(removed)}&quot;)
    
    # Если есть изменения — отправляем уведомление
    if changes:
        msg = f&quot;Изменения для {domain}:\n&quot; + &quot;\n&quot;.join(changes)
        send_telegram(
            config[&#x27;notifications&#x27;][&#x27;telegram&#x27;][&#x27;token&#x27;],
            config[&#x27;notifications&#x27;][&#x27;telegram&#x27;][&#x27;chat_id&#x27;],
            msg
        )
        # Логируем в БД
        for change in changes:
            db.log_change(domain, &#x27;change&#x27;, &#x27;&#x27;, change)
    
    # Сохраняем новое состояние
    save_current_state(domain, current)</pre>
  <p id="vYmX"><strong>Этап 13: Утилита уведомлений (utils/notifier.py)</strong></p>
  <pre id="o7Ut">import requests
import smtplib
from email.mime.text import MIMEText

def send_telegram(token, chat_id, message):
    &quot;&quot;&quot;Отправка уведомления в Telegram&quot;&quot;&quot;
    if not token or not chat_id:
        return
    url = f&quot;https://api.telegram.org/bot{token}/sendMessage&quot;
    try:
        requests.post(url, json={&#x27;chat_id&#x27;: chat_id, &#x27;text&#x27;: message}, timeout=10)
    except Exception as e:
        print(f&quot;Telegram ошибка: {e}&quot;)

def send_email(smtp_server, port, username, password, to, subject, body):
    &quot;&quot;&quot;Отправка email уведомления&quot;&quot;&quot;
    msg = MIMEText(body)
    msg[&#x27;Subject&#x27;] = subject
    msg[&#x27;From&#x27;] = username
    msg[&#x27;To&#x27;] = to
    try:
        with smtplib.SMTP(smtp_server, port) as server:
            server.starttls()
            server.login(username, password)
            server.send_message(msg)
    except Exception as e:
        print(f&quot;Email ошибка: {e}&quot;)</pre>
  <p id="EAVf"><strong>Этап 14: Bash-обёртка для cron (run.sh)</strong></p>
  <pre id="lE80">#!/bin/bash
# run.sh — обёртка для запуска конвейера с логированием

cd /path/to/osint-automation

# Активация виртуального окружения Python (если используется)
source venv/bin/activate

# Запуск основного скрипта
python main.py &gt;&gt; data/osint_cron.log 2&gt;&amp;1

# Проверка кода возврата
if [ $? -ne 0 ]; then
    echo &quot;Ошибка выполнения $(date)&quot; &gt;&gt; data/osint_errors.log
fi

# Удаление старых логов (старше 30 дней)
find data/ -name &quot;*.log&quot; -mtime +30 -delete</pre>
  <p id="BTCz"><strong>Настройка cron (ежедневно в 8 утра):</strong></p>
  <pre id="guf6">0 8 * * * /path/to/osint-automation/run.sh</pre>
  <p id="d7s1"><strong>Этап 15: Обработка ошибок и rate limits</strong></p>
  <p id="4ynZ">Важные аспекты при автоматизации:</p>
  <ul id="s4my">
    <li id="EiUn"><strong>Ограничения API:</strong> Используйте задержки между запросами (time.sleep), храните время последнего запроса.</li>
    <li id="l6CM"><strong>Повторные попытки:</strong> Реализуйте повтор при временных ошибках (например, 429 Too Many Requests).</li>
    <li id="IcEi"><strong>Логирование:</strong> Все ошибки пишите в лог для последующего анализа.</li>
    <li id="4Prs"><strong>Таймауты:</strong> Все HTTP-запросы должны иметь таймаут, чтобы скрипт не зависал.</li>
  </ul>
  <p id="4hgU"><strong>Пример декоратора для повторных попыток:</strong></p>
  <pre id="gDZK">import time
import functools

def retry(max_attempts=3, delay=5):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise
                    time.sleep(delay * (attempt + 1))
            return None
        return wrapper
    return decorator</pre>
  <p id="C0fR"><strong>Этап 16: Тестирование и мониторинг работы</strong></p>
  <p id="gx9F">После настройки автоматизации важно проверять:</p>
  <ul id="qrjP">
    <li id="oOzj"><strong>Работоспособность:</strong> Запустите скрипт вручную и проверьте вывод.</li>
    <li id="wIjf"><strong>Корректность данных:</strong> Проверьте, что база данных заполняется.</li>
    <li id="VQV2"><strong>Уведомления:</strong> Убедитесь, что при изменениях приходят сообщения.</li>
    <li id="q9Ml"><strong>Логи:</strong> Проверьте файл логов на наличие ошибок.</li>
  </ul>
  <p id="qdkR"></p>
  <ol id="lHCT">
    <li id="Zrfa">Используйте только публичные API и данные.</li>
    <li id="QrPU">Соблюдайте условия использования сервисов (Shodan, SecurityTrails, GitHub) — не превышайте лимиты запросов.</li>
    <li id="U63K">Не пытайтесь обойти защиту (CAPTCHA, блокировки) нелегальными методами.</li>
    <li id="jFYi">Полученные данные используйте только для законных целей.</li>
    <li id="0pRz">При обнаружении уязвимостей или секретов сообщайте владельцам.</li>
  </ol>
  <p id="Oyov"></p>
  <p id="Fd5y">Автоматизация технического OSINT — это создание программного конвейера, который собирает, обрабатывает и сохраняет данные о цифровой инфраструктуре цели без участия человека. Модульная архитектура на Python с использованием готовых библиотек (dns.resolver, whois, requests, shodan) и внешних API (crt.sh, SecurityTrails, Shodan, GitHub) позволяет охватить все основные источники. Сравнение состояний и уведомления делают систему полезной для долгосрочного мониторинга. Регулярный запуск через cron превращает набор скриптов в постоянно работающую систему разведки.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@specialist_infosec/Code-Repository-Analysis</guid><link>https://teletype.in/@specialist_infosec/Code-Repository-Analysis?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec</link><comments>https://teletype.in/@specialist_infosec/Code-Repository-Analysis?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec#comments</comments><dc:creator>specialist_infosec</dc:creator><title>Анализ репозиториев кода: GitHub, GitLab, Bitbucket — поиск секретов, утечек кода</title><pubDate>Tue, 07 Jul 2026 21:19:19 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/75/ce/75ce4736-9030-4c7c-9296-1e4277e4e647.png"></media:content><description><![CDATA[<img src="https://img1.teletype.in/files/41/f8/41f8f293-6ce2-4c88-ac08-6b6c673de30a.png"></img>Публичные репозитории кода — это один из наиболее недооцененных, но богатых источников информации в техническом OSINT. Разработчики часто случайно загружают в открытые репозитории конфиденциальные данные: API-ключи, пароли, токены, внутренние URL, логины к базам данных, комментарии с внутренней информацией и даже дампы баз данных. Для специалиста по разведке анализ репозиториев позволяет не только найти чувствительные данные, но и восстановить внутреннюю архитектуру, выявить используемые технологии и даже получить доступ к закрытым системам. Данная статья посвящена методам поиска секретов и утечек кода в публичных репозиториях GitHub, GitLab, Bitbucket и других платформах.]]></description><content:encoded><![CDATA[
  <figure id="mAdK" class="m_retina">
    <img src="https://img1.teletype.in/files/41/f8/41f8f293-6ce2-4c88-ac08-6b6c673de30a.png" width="1102" />
  </figure>
  <h3 id="VKhb">Анализ репозиториев кода: GitHub, GitLab, Bitbucket — поиск секретов, утечек кода</h3>
  <p id="6QGp">Публичные репозитории кода — это один из наиболее недооцененных, но богатых источников информации в техническом OSINT. Разработчики часто случайно загружают в открытые репозитории конфиденциальные данные: API-ключи, пароли, токены, внутренние URL, логины к базам данных, комментарии с внутренней информацией и даже дампы баз данных. Для специалиста по разведке анализ репозиториев позволяет не только найти чувствительные данные, но и восстановить внутреннюю архитектуру, выявить используемые технологии и даже получить доступ к закрытым системам. Данная статья посвящена методам поиска секретов и утечек кода в публичных репозиториях GitHub, GitLab, Bitbucket и других платформах.</p>
  <p id="Dbnd"><strong>Почему публичные репозитории опасны для организации?</strong></p>
  <figure id="WNta" class="m_column">
    <img src="https://img1.teletype.in/files/4b/f1/4bf1b03a-c594-4470-96d1-1aeeb775b4bc.png" width="1208" />
  </figure>
  <p id="wJMB"><strong>Основные платформы для поиска кода</strong></p>
  <figure id="KmDM" class="m_column">
    <img src="https://img4.teletype.in/files/7a/c6/7ac69abb-baf5-43bd-a8ed-633ad1c2212f.png" width="1216" />
  </figure>
  <p id="PSOP"><strong>Этап 1: Базовый поиск на GitHub</strong></p>
  <p id="rwMr">GitHub предоставляет мощную поисковую систему с операторами. Поиск можно выполнять как через веб-интерфейс, так и через API.</p>
  <p id="IRPf"><strong>Основные операторы GitHub:</strong></p>
  <figure id="z2qo" class="m_column">
    <img src="https://img2.teletype.in/files/19/3d/193d2abd-b455-476e-9816-589b97b094c7.png" width="1216" />
  </figure>
  <p id="3eCM"><strong>Примеры поисковых запросов:</strong></p>
  <ul id="GC5l">
    <li id="LSmm">Поиск ключей AWS: <code>&quot;AKIA&quot; &quot;SECRET&quot; extension:txt</code></li>
    <li id="fDwA">Поиск паролей: <code>password extension:env</code></li>
    <li id="He6X">Поиск токенов GitHub: <code>&quot;ghp_&quot; extension:txt</code></li>
    <li id="pBz1">Поиск конфигов: <code>filename:.env</code></li>
    <li id="noLY">Поиск SSH-ключей: <code>&quot;BEGIN RSA PRIVATE KEY&quot;</code></li>
    <li id="30A2">Поиск по домену компании: <code>&quot;example.com&quot; &quot;password&quot;</code></li>
  </ul>
  <p id="MqNv"><strong>Использование GitHub API для поиска:</strong></p>
  <pre id="Ndf1">import requests

GITHUB_TOKEN = &quot;YOUR_TOKEN&quot;
headers = {
    &quot;Authorization&quot;: f&quot;token {GITHUB_TOKEN}&quot;,
    &quot;Accept&quot;: &quot;application/vnd.github.v3+json&quot;
}

def search_github(query):
    url = &quot;https://api.github.com/search/code&quot;
    params = {
        &quot;q&quot;: query,
        &quot;per_page&quot;: 10
    }
    response = requests.get(url, headers=headers, params=params)
    if response.status_code == 200:
        return response.json()
    else:
        print(f&quot;Ошибка: {response.status_code}&quot;)
        return None

# Поиск .env файлов в организации example
results = search_github(&quot;extension:env org:example&quot;)
if results:
    for item in results.get(&#x27;items&#x27;, []):
        print(f&quot;{item[&#x27;repository&#x27;][&#x27;full_name&#x27;]}: {item[&#x27;path&#x27;]}&quot;)</pre>
  <p id="r6QJ"><strong>Этап 2: Поиск секретов с использованием TruffleHog</strong></p>
  <p id="cFV9">TruffleHog — это специализированный инструмент для поиска секретов в репозиториях. Он анализирует историю коммитов, а не только текущие файлы, и использует детекторы для различных типов секретов.</p>
  <p id="q2fp"><strong>Установка и использование:</strong></p>
  <pre id="WPlJ"># Установка через pip
pip install trufflehog

# Сканирование репозитория по URL
trufflehog git https://github.com/example/repo.git

# Сканирование всей организации (требуется токен)
trufflehog github --org=example --token=GITHUB_TOKEN</pre>
  <p id="1UiM"><strong>Пример вывода:</strong></p>
  <pre id="JnmT">🐷🔑🐷  TruffleHog. Unearth your secrets. 🐷🔑🐷

2024-01-15 10:30:45    INFO    Detector Name: AWS
2024-01-15 10:30:45    INFO    Commit: 3a4b5c6d7e8f9...
2024-01-15 10:30:45    INFO    File: config/credentials.yml
2024-01-15 10:30:45    INFO    Line: 12
2024-01-15 10:30:45    INFO    Secret: AKIAXXXXXXXXXXXXXXXX</pre>
  <p id="OD4l"><strong>Этап 3: Использование GitRob и GitGrab для поиска по организациям</strong></p>
  <p id="HPRa"><strong>GitRob</strong> — утилита для поиска ключей и паролей в репозиториях GitHub организаций.</p>
  <pre id="SdwD">gitrob example</pre>
  <p id="CX7l"><strong>GitGrab</strong> — аналогичный инструмент, фокусирующийся на поиске конфигурационных файлов.</p>
  <p id="HdG4"><strong>Этап 4: Поиск по расширениям файлов</strong></p>
  <figure id="xZxq" class="m_column">
    <img src="https://img2.teletype.in/files/59/ab/59abe1ff-9f8b-4472-9a6e-e33701b22f64.png" width="1215" />
  </figure>
  <p id="gQLG"><strong>Поиск на GitHub через веб-интерфейс:</strong></p>
  <pre id="26NA">extension:env org:example
extension:yml &quot;password&quot; org:example
extension:json &quot;api_key&quot; org:example</pre>
  <p id="W5cu"><strong>Этап 5: Поиск по строковым шаблонам секретов</strong></p>
  <p id="10kk">Регулярные выражения помогают находить секреты по их формату.</p>
  <p id="qI1i"><strong>Типичные паттерны:</strong></p>
  <figure id="yYtY" class="m_column">
    <img src="https://img1.teletype.in/files/8e/d9/8ed93dd0-835d-4bf9-92d4-d1b27f1a847a.png" width="1217" />
  </figure>
  <p id="qKHg"><strong>Пример скрипта для поиска по паттернам в скачанном репозитории:</strong></p>
  <pre id="XBGR">import re
import os
import fnmatch

patterns = {
    &#x27;aws&#x27;: re.compile(r&#x27;AKIA[0-9A-Z]{16}&#x27;),
    &#x27;github&#x27;: re.compile(r&#x27;ghp_[0-9a-zA-Z]{36}&#x27;),
    &#x27;slack&#x27;: re.compile(r&#x27;xox[bap]-[0-9a-zA-Z-]+&#x27;),
    &#x27;ssh&#x27;: re.compile(r&#x27;-----BEGIN (RSA|OPENSSH|DSA|EC) PRIVATE KEY-----&#x27;),
}

def scan_file(filepath):
    try:
        with open(filepath, &#x27;r&#x27;, encoding=&#x27;utf-8&#x27;, errors=&#x27;ignore&#x27;) as f:
            content = f.read()
            for name, pattern in patterns.items():
                matches = pattern.findall(content)
                if matches:
                    print(f&quot;{filepath}: {name} -&gt; {matches[:3]}&quot;)
    except:
        pass

def scan_repo(repo_path):
    for root, dirs, files in os.walk(repo_path):
        for file in files:
            scan_file(os.path.join(root, file))

# Сканирование локального клона репозитория
scan_repo(&#x27;/path/to/cloned/repo&#x27;)</pre>
  <p id="VGRV"><strong>Этап 6: Поиск через PublicWWW</strong></p>
  <p id="snJC">PublicWWW — поисковая система, индексирующая HTML, CSS и JavaScript. Можно искать по строкам, которые появляются в коде веб-страниц.</p>
  <p id="wcju"><strong>Использование:</strong></p>
  <pre id="hhbE">https://publicwww.com/websites/%22AKIA%22/</pre>
  <p id="lwCm">ищет не только код в репозиториях, но и во всех открытых веб-страницах.</p>
  <p id="QmYq"><strong>Этап 7: Поиск через SearchCode</strong></p>
  <p id="OPcJ">SearchCode агрегирует данные из GitHub, Bitbucket, GitLab и других источников. У него есть API.</p>
  <pre id="ZkHl"># Поиск через веб-интерфейс
https://searchcode.com/?q=AKIA&amp;p=2</pre>
  <p id="F9Rd"><strong>Этап 8: Мониторинг новых утечек через GitHub</strong></p>
  <p id="xajA">Новые утечки появляются постоянно. Важно настроить мониторинг на появление новых файлов с секретами.</p>
  <p id="yuB8"><strong>Инструменты для мониторинга:</strong></p>
  <ul id="w2mG">
    <li id="ty7y"><strong>GitHub Watch</strong> — подписка на изменения в репозиториях (вручную).</li>
    <li id="LDeN"><strong>HawkScan</strong> — автоматическое сканирование новых коммитов в организациях.</li>
    <li id="MBxb"><strong>LeakIX</strong> — поисковик утечек, отправляет уведомления.</li>
  </ul>
  <p id="3s6Z"><strong>Собственный мониторинг через GitHub API:</strong></p>
  <pre id="j80i">def monitor_new_commits(org, last_check_file):
    # Получение последних коммитов в организации
    # Сравнение с предыдущим состоянием
    # При обнаружении секретов — уведомление
    pass</pre>
  <p id="zSsa"><strong>Этап 9: Анализ истории коммитов (продвинутый уровень)</strong></p>
  <p id="KWaZ">Git сохраняет историю изменений. Даже если секрет был удалён в последнем коммите, он может остаться в истории.</p>
  <p id="F8NS"><strong>Поиск в истории через TruffleHog:</strong> (см. выше) сканирует всю историю.</p>
  <p id="wHpW"><strong>Поиск в локально скачанном репозитории:</strong></p>
  <pre id="fcIJ"># Поиск по всем коммитам в Git
git log -p | grep -i &quot;password&quot;
git log -p | grep -i &quot;AKIA&quot;
git log -p | grep -i &quot;ghp_&quot;</pre>
  <p id="ljOn"><strong>Этап 10: Восстановление удалённых файлов из истории Git</strong></p>
  <p id="I2Ph">Даже если файл был удалён, он может быть восстановлен через Git.</p>
  <pre id="af7v"># Поиск файла в истории
git log --all --full-history -- &quot;*config*&quot;

# Восстановление файла
git show &lt;commit_hash&gt;:path/to/file</pre>
  <p id="BZfN"><strong>Этап 11: Поиск в GitLab и Bitbucket</strong></p>
  <p id="4HQQ"><strong>GitLab:</strong> общедоступные проекты можно искать через поиск GitLab. Операторы: <code>group:</code>, <code>project:</code>.</p>
  <p id="e9D7"><strong>Пример:</strong> <code>https://gitlab.com/search?search=password&amp;group_id=123</code></p>
  <p id="7Xqm"><strong>Bitbucket:</strong> аналогично, через поиск или API.</p>
  <p id="bLII"><strong>Сводная таблица инструментов поиска секретов</strong></p>
  <figure id="zNh5" class="m_column">
    <img src="https://img1.teletype.in/files/46/3f/463f334a-ab66-4b43-b674-aea0df356bb5.png" width="1008" />
  </figure>
  <ul id="fNWr">
    <li id="nZy8"><strong>Никогда не используйте найденные секреты для несанкционированного доступа.</strong> Это уголовное преступление.</li>
    <li id="9kKb">Если вы нашли секреты, принадлежащие организации, <strong>сообщите им</strong> через официальные каналы (security@example.com) или через программу Bug Bounty.</li>
    <li id="ZGJD"><strong>Не распространяйте</strong> найденные секреты публично.</li>
    <li id="zboL">При сканировании соблюдайте условия использования платформ (GitHub, GitLab) — чрезмерные запросы могут привести к блокировке IP.</li>
    <li id="YQTz">В некоторых юрисдикциях даже поиск секретов может считаться нарушением, если он ведётся без разрешения. Ориентируйтесь на публичные данные.</li>
  </ul>
  <p id="mP6d"><strong>Практический пример: расследование утечки токенов</strong></p>
  <p id="6lPI"><em>Задача:</em> найти все токены, связанные с организацией <code>example.com</code>, и проинформировать их.</p>
  <ol id="M9a4">
    <li id="XW1v"><strong>GitHub Search:</strong> <code>&quot;example.com&quot; &quot;token&quot; extension:env</code> → найдено 3 файла <code>.env</code> с токенами для Stripe, AWS и Slack.</li>
    <li id="CJV4"><strong>TruffleHog:</strong> сканирование организации <code>example</code> → обнаружен AWS-ключ в старом коммите (уже удалённом из кода, но оставшемся в истории).</li>
    <li id="zJlT"><strong>PublicWWW:</strong> поиск <code>&quot;AKIA&quot; &quot;example.com&quot;</code> → найден HTML-файл со скриптом, содержащим Google Maps API ключ.</li>
    <li id="THYG"><strong>Связь:</strong> ключи принадлежат компании <code>example.com</code> (проверено по домену в комментариях).</li>
    <li id="F3JF"><strong>Сообщение:</strong> отправка письма на security@example.com с перечнем найденных токенов и рекомендацией по их отзыву и ротации.</li>
    <li id="VIqS"><strong>Результат:</strong> компания подтвердила получение, отозвала скомпрометированные ключи, обновила политику безопасности.</li>
  </ol>
  <p id="HXU0">Анализ публичных репозиториев кода — один из самых результативных методов технического OSINT. Комбинация GitHub-поиска с операторами, специализированных инструментов (TruffleHog, GitRob) и анализа истории коммитов позволяет находить секреты, пароли и утечки кода, которые случайно попали в открытый доступ. Важно использовать полученную информацию ответственно и этично: сообщать о находках владельцам, а не использовать их для атак.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@specialist_infosec/psheasJMkJG</guid><link>https://teletype.in/@specialist_infosec/psheasJMkJG?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec</link><comments>https://teletype.in/@specialist_infosec/psheasJMkJG?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec#comments</comments><dc:creator>specialist_infosec</dc:creator><title>Мониторинг изменений инфраструктуры: уведомления о новых поддоменах, сертификатах, изменениях в DNS</title><pubDate>Mon, 06 Jul 2026 05:11:38 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/13/86/1386e3cf-dbb6-410b-b229-2d238476136a.png"></media:content><description><![CDATA[<img src="https://img2.teletype.in/files/5d/35/5d3562b7-5004-4f7d-abda-b6e306585276.png"></img>Инфраструктура организации не статична. Появляются новые поддомены, меняются IP-адреса, выпускаются новые сертификаты, обновляются технологии. Для специалиста по техническому OSINT отслеживание этих изменений критически важно: новые поддомены могут раскрыть неанонсированные проекты, свежие сертификаты — указать на новые серверы, а изменения в DNS — на смену хостинг-провайдера или CDN. Мониторинг изменений позволяет реагировать на появление новых активов в реальном времени, не дожидаясь, пока они будут обнаружены случайно. Данная статья посвящена методам и инструментам мониторинга инфраструктуры, а также настройке автоматических уведомлений.]]></description><content:encoded><![CDATA[
  <figure id="Gequ" class="m_retina">
    <img src="https://img2.teletype.in/files/5d/35/5d3562b7-5004-4f7d-abda-b6e306585276.png" width="1102" />
  </figure>
  <h3 id="tT7R">Мониторинг изменений инфраструктуры: уведомления о новых поддоменах, сертификатах, изменениях в DNS</h3>
  <p id="NCqc">Инфраструктура организации не статична. Появляются новые поддомены, меняются IP-адреса, выпускаются новые сертификаты, обновляются технологии. Для специалиста по техническому OSINT отслеживание этих изменений критически важно: новые поддомены могут раскрыть неанонсированные проекты, свежие сертификаты — указать на новые серверы, а изменения в DNS — на смену хостинг-провайдера или CDN. Мониторинг изменений позволяет реагировать на появление новых активов в реальном времени, не дожидаясь, пока они будут обнаружены случайно. Данная статья посвящена методам и инструментам мониторинга инфраструктуры, а также настройке автоматических уведомлений.</p>
  <p id="fpSv"><strong>Зачем мониторить изменения?</strong></p>
  <figure id="5OhT" class="m_column">
    <img src="https://img2.teletype.in/files/d1/9f/d19fd63f-faf9-4b1b-9054-2eae791cf5ee.png" width="1210" />
  </figure>
  <p id="Ov5j"><strong>Типы мониторинга изменений</strong></p>
  <figure id="62bO" class="m_column">
    <img src="https://img1.teletype.in/files/8a/f4/8af402fc-ba40-4415-bf0a-0358ca89c470.png" width="1202" />
  </figure>
  <p id="WRxS"><strong>Этап 1: Мониторинг поддоменов через crt.sh и CertSpotter</strong></p>
  <p id="yJTl"><strong>crt.sh</strong> позволяет выполнять поиск новых сертификатов для домена. Поскольку каждый новый сертификат (и соответственно, поддомен) попадает в CT-логи, crt.sh — идеальный источник для обнаружения новых поддоменов.</p>
  <p id="XyoT"><strong>Алгоритм мониторинга:</strong></p>
  <ol id="dSuP">
    <li id="jv1Y">Ежедневно выполнять запрос к crt.sh: <code>?q=%.example.com&amp;output=json</code>.</li>
    <li id="TOGG">Сравнивать полученный список доменов с предыдущим.</li>
    <li id="lxHF">При появлении новых — отправлять уведомление.</li>
  </ol>
  <p id="lDAZ"><strong>Пример скрипта для мониторинга поддоменов через crt.sh:</strong></p>
  <pre id="CqNJ">import requests
import json
import time
import hashlib

def get_subdomains(domain):
    url = f&quot;https://crt.sh/?q=%25.{domain}&amp;output=json&quot;
    try:
        resp = requests.get(url, timeout=30)
        if resp.status_code == 200:
            data = resp.json()
            subdomains = set()
            for entry in data:
                name = entry.get(&#x27;name_value&#x27;, &#x27;&#x27;)
                if name.endswith(f&#x27;.{domain}&#x27;) or name == domain:
                    subdomains.add(name.lower())
            return sorted(subdomains)
    except Exception as e:
        print(f&quot;Ошибка crt.sh: {e}&quot;)
    return []

def monitor_subdomains(domain, state_file=&#x27;subdomains_state.json&#x27;):
    # Загрузка предыдущего состояния
    try:
        with open(state_file, &#x27;r&#x27;) as f:
            previous = set(json.load(f))
    except:
        previous = set()
    
    # Получение текущего состояния
    current = set(get_subdomains(domain))
    
    # Поиск изменений
    new = current - previous
    removed = previous - current
    
    if new:
        print(f&quot;Новые поддомены: {&#x27;, &#x27;.join(new)}&quot;)
        # Здесь отправка уведомления
    if removed:
        print(f&quot;Удалены: {&#x27;, &#x27;.join(removed)}&quot;)
    
    # Сохранение состояния
    with open(state_file, &#x27;w&#x27;) as f:
        json.dump(list(current), f)
    
    return new, removed

# Запуск ежедневно
monitor_subdomains(&#x27;example.com&#x27;)</pre>
  <p id="MSr8"><strong>CertSpotter</strong> — сервис, который автоматически отслеживает новые сертификаты для указанных доменов и присылает уведомления по email. Бесплатно до 10 доменов.</p>
  <p id="24uH"><strong>Как настроить CertSpotter:</strong></p>
  <ol id="ZEyZ">
    <li id="Rrmf">Зарегистрироваться на <a href="https://sslmate.com/certspotter" target="_blank">sslmate.com/certspotter</a>.</li>
    <li id="ffwu">Добавить домены для мониторинга.</li>
    <li id="Y8e2">Получать уведомления о новых сертификатах (с указанием SAN и даты истечения).</li>
  </ol>
  <p id="s639"><strong>Этап 2: Мониторинг DNS-изменений</strong></p>
  <p id="KUsB">Изменения DNS-записей могут указывать на смену хостинга, добавление новых сервисов или изменение почтовой инфраструктуры.</p>
  <p id="mtga"><strong>Инструменты для мониторинга DNS:</strong></p>
  <figure id="2y9b" class="m_column">
    <img src="https://img1.teletype.in/files/c8/37/c83747d2-07e9-4d0f-b90d-d4483b1ceb0d.png" width="1001" />
  </figure>
  <p id="tx2b"><strong>Собственный скрипт для мониторинга DNS-записей(пример):</strong></p>
  <pre id="Ola2">import dns.resolver
import json
import time

def get_dns_records(domain, record_types=[&#x27;A&#x27;, &#x27;MX&#x27;, &#x27;TXT&#x27;, &#x27;NS&#x27;, &#x27;CNAME&#x27;]):
    records = {}
    for rtype in record_types:
        try:
            answers = dns.resolver.resolve(domain, rtype)
            records[rtype] = [str(r) for r in answers]
        except:
            records[rtype] = []
    return records

def monitor_dns(domain, state_file=&#x27;dns_state.json&#x27;):
    try:
        with open(state_file, &#x27;r&#x27;) as f:
            previous = json.load(f)
    except:
        previous = {}
    
    current = get_dns_records(domain)
    
    changes = {}
    for rtype in set(previous.keys()) | set(current.keys()):
        prev = previous.get(rtype, [])
        curr = current.get(rtype, [])
        if set(prev) != set(curr):
            changes[rtype] = {
                &#x27;old&#x27;: prev,
                &#x27;new&#x27;: curr
            }
    
    if changes:
        print(f&quot;DNS изменения для {domain}:&quot;)
        for rtype, change in changes.items():
            print(f&quot;  {rtype}: {change[&#x27;old&#x27;]} → {change[&#x27;new&#x27;]}&quot;)
        # здесь отправка уведомления
    
    with open(state_file, &#x27;w&#x27;) as f:
        json.dump(current, f)
    
    return changes

# Запуск
monitor_dns(&#x27;example.com&#x27;)</pre>
  <p id="6EiM"><strong>Этап 3: Мониторинг SSL-сертификатов</strong></p>
  <p id="qROE">Новые SSL-сертификаты часто появляются при запуске новых серверов или обновлении старых.</p>
  <p id="sYwt"><strong>CertSpotter</strong> (см. выше) — лучший бесплатный способ мониторинга.</p>
  <p id="1fUQ"><strong>Альтернатива: регулярный запрос к crt.sh с фильтром по дате:</strong></p>
  <pre id="QlnO">https://crt.sh/?q=%.example.com&amp;exclude=expired&amp;limit=100&amp;output=json</pre>
  <p id="PHcN"><strong>Скрипт для отслеживания сертификатов, истекающих в ближайшее время:</strong></p>
  <pre id="2KUX">import requests
from datetime import datetime, timedelta

def get_expiring_certs(domain, days=30):
    url = f&quot;https://crt.sh/?q=%25.{domain}&amp;output=json&quot;
    resp = requests.get(url)
    if resp.status_code == 200:
        data = resp.json()
        expiring = []
        now = datetime.now()
        for entry in data:
            if len(entry) &lt; 5:
                continue
            valid_to = entry[4]  # дата истечения
            if valid_to:
                try:
                    exp_date = datetime.strptime(valid_to, &#x27;%Y-%m-%dT%H:%M:%S&#x27;)
                    if (exp_date - now).days &lt; days:
                        expiring.append({
                            &#x27;name&#x27;: entry[1],
                            &#x27;expires&#x27;: valid_to
                        })
                except:
                    pass
        return expiring
    return []

expiring = get_expiring_certs(&#x27;example.com&#x27;, 30)
for cert in expiring:
    print(f&quot;Сертификат для {cert[&#x27;name&#x27;]} истекает {cert[&#x27;expires&#x27;]}&quot;)</pre>
  <p id="8u5H"><strong>Этап 4: Мониторинг изменений контента сайта</strong></p>
  <p id="0xtu">Изменения на сайте (новые страницы, обновлённый текст, новые файлы) могут указывать на новые проекты, изменения в стратегии или утечку информации.</p>
  <p id="VtFO"><strong>Инструменты для мониторинга изменений страниц:</strong></p>
  <figure id="aOaS" class="m_column">
    <img src="https://img2.teletype.in/files/1b/4e/1b4e0528-83d5-469d-a274-88b4c724f74a.png" width="1200" />
  </figure>
  <p id="HkBP"><strong>Пример использования <a href="https://changedetection.io/" target="_blank">Changedetection.io</a> (Docker):</strong></p>
  <pre id="nTLh">docker run -d --name changedetection -p 5000:5000 ghcr.io/dgtlmoon/changedetection.io</pre>
  <p id="SOTs">Затем через веб-интерфейс добавить URL для мониторинга, настроить интервал проверки и канал уведомлений.</p>
  <p id="eiRY"><strong>Собственный скрипт мониторинга контента (с хэшированием)</strong></p>
  <pre id="1Sxo">import requests
import hashlib
import json
import time

def fetch_page_hash(url):
    try:
        resp = requests.get(url, timeout=10)
        return hashlib.sha256(resp.text.encode()).hexdigest()
    except:
        return None

def monitor_url(url, state_file=&#x27;url_state.json&#x27;):
    try:
        with open(state_file, &#x27;r&#x27;) as f:
            state = json.load(f)
            previous_hash = state.get(url)
    except:
        previous_hash = None
    
    current_hash = fetch_page_hash(url)
    if current_hash and current_hash != previous_hash:
        print(f&quot;Страница {url} изменилась!&quot;)
        # уведомление
        state[url] = current_hash
        with open(state_file, &#x27;w&#x27;) as f:
            json.dump(state, f)
        return True
    return False

# Мониторинг нескольких страниц
urls = [&#x27;https://example.com&#x27;, &#x27;https://example.com/about&#x27;]
for url in urls:
    monitor_url(url)</pre>
  <p id="VEeE"><strong>Этап 5: Мониторинг WHOIS-изменений</strong></p>
  <p id="wNf4">Изменения в WHOIS (смена владельца, регистратора, дат) могут указывать на продажу домена или смену контактов.</p>
  <p id="Y8D3"><strong>Инструменты:</strong></p>
  <ul id="9Pgx">
    <li id="rVQY"><strong>DomainTools</strong> (платный мониторинг).</li>
    <li id="MW33"><strong>WhoisXML API</strong> (есть платный мониторинг).</li>
    <li id="eAno"><strong>Собственные скрипты</strong> через <code>whois</code> и сравнение.</li>
  </ul>
  <p id="qNM9"><strong>Пример мониторинга WHOIS (с использованием python-whois):</strong></p>
  <pre id="R2Fh">import whois
import json
import time

def get_whois_info(domain):
    try:
        w = whois.whois(domain)
        return {
            &#x27;registrar&#x27;: w.registrar,
            &#x27;creation_date&#x27;: str(w.creation_date),
            &#x27;expiration_date&#x27;: str(w.expiration_date),
            &#x27;name_servers&#x27;: w.name_servers,
            &#x27;emails&#x27;: w.emails
        }
    except:
        return None

def monitor_whois(domain, state_file=&#x27;whois_state.json&#x27;):
    try:
        with open(state_file, &#x27;r&#x27;) as f:
            previous = json.load(f)
    except:
        previous = {}
    
    current = get_whois_info(domain)
    if current and current != previous:
        print(f&quot;WHOIS для {domain} изменился!&quot;)
        # Здесь уведомление
        with open(state_file, &#x27;w&#x27;) as f:
            json.dump(current, f)
        return True
    return False

monitor_whois(&#x27;example.com&#x27;)</pre>
  <p id="Md20"><strong>Этап 6: Мониторинг технологического стека (Wappalyzer)</strong></p>
  <p id="gmKD">Изменения в технологиях могут указывать на обновление CMS, переход на новый фреймворк.</p>
  <p id="KP0f"><strong>Метод:</strong> периодически запускать Wappalyzer CLI и сравнивать результаты.</p>
  <p id="Qv4W"><strong>Пример (через subprocess):</strong></p>
  <pre id="sI08">import subprocess
import json

def get_tech_stack(url):
    result = subprocess.run([&#x27;wappalyzer&#x27;, url], capture_output=True, text=True)
    if result.returncode == 0:
        return json.loads(result.stdout)
    return None

# Сравнение результатов за разные даты</pre>
  <p id="zHqJ"><strong>Этап 7: Мониторинг через агрегаторы (SecurityTrails, Censys)</strong></p>
  <p id="SA2T">SecurityTrails предлагает функцию мониторинга (Alerts) для платных подписок. Вы можете отслеживать:</p>
  <ul id="rAqC">
    <li id="tzSd">Новые поддомены.</li>
    <li id="AxBP">Изменения в DNS-записях.</li>
    <li id="4XrC">Изменения в WHOIS.</li>
  </ul>
  <p id="aB7g"><strong>Censys</strong> позволяет отслеживать изменения в сертификатах и открытых портах.</p>
  <p id="sYIQ"><strong>Этап 8: Настройка уведомлений</strong></p>
  <p id="6X2c">После обнаружения изменений важно получать уведомления в удобном формате.</p>
  <p id="jPxS"><strong>Способы уведомлений:</strong></p>
  <ul id="ntWd">
    <li id="IRsc"><strong>Email</strong> (через <code>smtplib</code> в Python или сервисы типа SendGrid).</li>
    <li id="SRSx"><strong>Telegram Bot</strong> (см. код ниже).</li>
    <li id="6Hsq"><strong>Slack / Discord</strong> (вебхуки).</li>
    <li id="XZPw"><strong>SMS</strong> (платные сервисы).</li>
    <li id="vPJh"><strong>Pushover</strong> (мобильные уведомления).</li>
  </ul>
  <p id="7s9D"><strong>Пример уведомления через Telegram-бота:</strong></p>
  <pre id="snwl">import requests

def send_telegram_alert(token, chat_id, message):
    url = f&quot;https://api.telegram.org/bot{token}/sendMessage&quot;
    payload = {
        &#x27;chat_id&#x27;: chat_id,
        &#x27;text&#x27;: message
    }
    requests.post(url, json=payload)

# Использование
send_telegram_alert(&#x27;YOUR_BOT_TOKEN&#x27;, &#x27;YOUR_CHAT_ID&#x27;, &#x27;Обнаружены новые поддомены: dev.example.com, test.example.com&#x27;)</pre>
  <p id="6N81"><strong>Этап 9: Полный конвейер мониторинга</strong></p>
  <p id="J6t9">Для эффективного мониторинга все инструменты можно объединить в единый конвейер:</p>
  <ol id="Ux1V">
    <li id="Iug1"><strong>Ежедневный запуск</strong> (через cron или Task Scheduler).</li>
    <li id="eZlX"><strong>Проверка поддоменов</strong> (crt.sh, SecurityTrails).</li>
    <li id="zq6u"><strong>Проверка DNS</strong> (собственный скрипт).</li>
    <li id="FUvN"><strong>Проверка SSL-сертификатов</strong> (CertSpotter / crt.sh).</li>
    <li id="hFoy"><strong>Проверка контента</strong> (<a href="https://changedetection.io/" target="_blank">Changedetection.io</a> / собственный скрипт).</li>
    <li id="dZ4j"><strong>Проверка WHOIS</strong> (собственный скрипт).</li>
    <li id="DOVx"><strong>Сбор изменений в единый отчёт</strong>.</li>
    <li id="M1WK"><strong>Отправка уведомления</strong> (Telegram, Email).</li>
  </ol>
  <p id="QEu9"><strong>Пример крона для ежедневного запуска:</strong></p>
  <pre id="MkeQ">0 8 * * * /usr/bin/python3 /path/to/monitor_all.py</pre>
  <p id="r3s7"><strong>Практический пример: мониторинг компании-конкурента</strong></p>
  <p id="kRBz"><em>Задача:</em> отслеживать изменения в инфраструктуре конкурента <code>compete.com</code>.</p>
  <ol id="FVny">
    <li id="31M9"><strong>Настройка CertSpotter</strong> для <code>compete.com</code> и <code>*.compete.com</code> → получение уведомлений о новых сертификатах.</li>
    <li id="7CMW"><strong>Ежедневный скрипт для crt.sh</strong> → запись всех поддоменов, сравнение с предыдущим днём. При появлении нового отправка в Telegram.</li>
    <li id="UJoY"><strong>Мониторинг DNS</strong> → раз в неделю проверка A, MX, TXT записей. При изменении — уведомление.</li>
    <li id="Rlr9"><strong>Мониторинг сайта</strong> → <a href="https://changedetection.io/" target="_blank">Changedetection.io</a> для главной страницы и страницы <code>/careers</code> (появление новых вакансий может указывать на расширение).</li>
    <li id="zUNw"><strong>Обнаружение:</strong> спустя неделю появился поддомен <code>app.compete.com</code>. В сертификате указан AWS CloudFront. Это указывает на запуск нового веб-приложения. Мониторинг подтверждает, что сайт стал доступен.</li>
    <li id="ppFL"><strong>Вывод:</strong> компания запускает новый продукт. Информация может быть использована для анализа конкурентной среды.</li>
  </ol>
  <p id="RERN"><strong>Инструменты для мониторинга (сводная таблица)</strong></p>
  <figure id="v4eg" class="m_column">
    <img src="https://img1.teletype.in/files/82/8f/828f6cd1-af48-4243-a065-92298fa6e6fd.png" width="1196" />
  </figure>
  <ul id="Bykc">
    <li id="9WoA">Мониторинг публичных данных (DNS, сертификаты, поддомены, WHOIS) легален.</li>
    <li id="nHkY">Использование автоматизированных скриптов для частых запросов к crt.sh или другим сервисам должно соответствовать их условиям использования (не превышать лимиты).</li>
  </ul>
  <ul id="JUE2">
    <li id="pJrS">Если вы мониторите конкурента, не нарушайте законы о коммерческой тайне и не используйте методы, требующие несанкционированного доступа.</li>
  </ul>
  <p id="O9AX">Мониторинг изменений инфраструктуры — это непрерывный процесс, позволяющий быть в курсе всех изменений в цифровых активах цели. Комбинация автоматических скриптов (crt.sh, DNS, WHOIS), готовых сервисов (CertSpotter, Changedetection) и уведомлений (Telegram) создаёт систему раннего обнаружения новых поддоменов, сертификатов, DNS-записей и контента. Это позволяет оперативно реагировать на появление новых проектов, смену хостинга и другие изменения, важные для технической разведки.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@specialist_infosec/Site-History-Analysis</guid><link>https://teletype.in/@specialist_infosec/Site-History-Analysis?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec</link><comments>https://teletype.in/@specialist_infosec/Site-History-Analysis?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec#comments</comments><dc:creator>specialist_infosec</dc:creator><title>Анализ истории сайта: Wayback Machine, изменения контента, скрытые страницы</title><pubDate>Sat, 27 Jun 2026 17:59:12 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/51/6f/516f6185-e099-45da-ac77-465dd4dbb7a1.png"></media:content><description><![CDATA[<img src="https://img3.teletype.in/files/aa/f8/aaf8fd7c-7439-4b18-a2d7-f476ccf27f73.png"></img>Веб-сайты постоянно меняются: дизайн обновляется, контент редактируется, страницы удаляются, а новые появляются. Однако интернет «помнит» многие из этих изменений благодаря архивам, таким как Wayback Machine. Для специалиста по техническому OSINT анализ истории сайта — это способ заглянуть в прошлое: восстановить удалённые страницы, найти забытые поддомены, обнаружить конфиденциальную информацию, которая была случайно опубликована, а затем удалена, а также проследить эволюцию политики и контента организации. Данная статья посвящена методам работы с веб-архивами, анализу изменений и поиску скрытых страниц.]]></description><content:encoded><![CDATA[
  <figure id="P6ZY" class="m_retina">
    <img src="https://img3.teletype.in/files/aa/f8/aaf8fd7c-7439-4b18-a2d7-f476ccf27f73.png" width="1102" />
  </figure>
  <h3 id="sbQM">Анализ истории сайта: Wayback Machine, изменения контента, скрытые страницы</h3>
  <p id="7bcm">Веб-сайты постоянно меняются: дизайн обновляется, контент редактируется, страницы удаляются, а новые появляются. Однако интернет «помнит» многие из этих изменений благодаря архивам, таким как Wayback Machine. Для специалиста по техническому OSINT анализ истории сайта — это способ заглянуть в прошлое: восстановить удалённые страницы, найти забытые поддомены, обнаружить конфиденциальную информацию, которая была случайно опубликована, а затем удалена, а также проследить эволюцию политики и контента организации. Данная статья посвящена методам работы с веб-архивами, анализу изменений и поиску скрытых страниц.</p>
  <p id="okS7"><strong>Зачем анализировать историю сайта?</strong></p>
  <figure id="0QXn" class="m_column">
    <img src="https://img2.teletype.in/files/da/43/da434446-5147-4d3f-b937-5ef373012460.png" width="1114" />
  </figure>
  <p id="ASRd"><strong>Основные архивы веба</strong></p>
  <figure id="fBRy" class="m_column">
    <img src="https://img2.teletype.in/files/1d/16/1d16ac4a-295b-4b69-93a1-f2083fbe1fa1.png" width="1145" />
  </figure>
  <p id="1fy2"><strong>Этап 1: Поиск истории сайта в Wayback Machine</strong></p>
  <p id="DsXl"><strong>Прямой доступ к архиву:</strong> <code>https://web.archive.org/web/*/https://example.com</code></p>
  <p id="vxBs"><strong>Что можно увидеть:</strong></p>
  <ul id="zGAA">
    <li id="ilH3">Календарь сохранений (синие кружки — даты, когда страница была сохранена).</li>
    <li id="qwcU">Выбор конкретной даты.</li>
    <li id="eW1e">Сравнение двух снимков (инструмент &quot;Compare&quot;).</li>
    <li id="dGXK">Доступ к сохранённым изображениям, CSS, JS (частично).</li>
  </ul>
  <p id="VBLJ"><strong>Основные параметры Wayback Machine API:</strong></p>
  <ul id="wqiE">
    <li id="zt79"><code>https://archive.org/wayback/available?url=https://example.com</code> — проверка, есть ли архив для URL.</li>
    <li id="gjES"><code>https://web.archive.org/cdx/search/cdx?url=example.com&amp;output=json</code> — список всех сохранений в JSON (для массового сбора).</li>
  </ul>
  <p id="zfY1"><strong>Пример API-запроса (Python):</strong></p>
  <pre id="yarb">import requests
import json

def get_wayback_snapshots(url):
    cdx_url = f&quot;https://web.archive.org/cdx/search/cdx?url={url}&amp;output=json&quot;
    resp = requests.get(cdx_url)
    if resp.status_code == 200:
        data = resp.json()
        if len(data) &gt; 1:
            snapshots = []
            for row in data[1:]:  # первая строка — заголовки
                timestamp = row[1]
                snapshot_url = f&quot;https://web.archive.org/web/{timestamp}/{url}&quot;
                snapshots.append({
                    &#x27;timestamp&#x27;: timestamp,
                    &#x27;url&#x27;: snapshot_url
                })
            return snapshots
    return []

snapshots = get_wayback_snapshots(&#x27;example.com&#x27;)
for s in snapshots[:5]:
    print(s[&#x27;timestamp&#x27;], s[&#x27;url&#x27;])</pre>
  <p id="cxWS"><strong>Этап 2: Поиск удалённых страниц и контента</strong></p>
  <p id="Zk9K">Один из основных способов использования архива — найти страницы, которые были удалены с сайта, но сохранились в Wayback Machine.</p>
  <p id="15wh"><strong>Методика:</strong></p>
  <ol id="LwMa">
    <li id="spP2">Определить URL-шаблоны (например, <code>/blog/</code>, <code>/docs/</code>, <code>/downloads/</code>).</li>
    <li id="dOaN">Искать в архиве конкретные URL.</li>
    <li id="U896">Использовать оператор <code>*</code> для поиска всех сохранённых страниц с определённым префиксом.</li>
  </ol>
  <p id="JwKO"><strong>Пример поиска всех файлов <code>.pdf</code> в архиве:</strong></p>
  <pre id="NFdG">https://web.archive.org/web/*/https://example.com/*.pdf</pre>
  <p id="QvhU"><strong>Поиск всех страниц в подкаталоге:</strong></p>
  <pre id="kPkI">https://web.archive.org/web/*/https://example.com/docs/*</pre>
  <p id="QqoA"><strong>Этап 3: Обнаружение скрытых страниц через архивы</strong></p>
  <p id="TrqB">Скрытые страницы — те, на которые нет ссылок с главной, но они могут существовать и быть проиндексированы архивом.</p>
  <p id="6ZJn"><strong>Где искать скрытые страницы:</strong></p>
  <ul id="7uDr">
    <li id="uemy"><code>/admin</code>, <code>/login</code>, <code>/backup</code>, <code>/test</code>, <code>/dev</code>, <code>/staging</code>, <code>/old</code>, <code>/temp</code>, <code>/private</code>.</li>
    <li id="l3PW">Панели управления: <code>/cpanel</code>, <code>/webmail</code>, <code>/plesk</code>, <code>/phpmyadmin</code>.</li>
    <li id="CIrm">Файлы конфигурации: <code>/config</code>, <code>/settings</code>.</li>
    <li id="4y0Z">Директории с исходными кодами: <code>/git</code>, <code>/svn</code>, <code>/cvs</code>.</li>
  </ul>
  <p id="Ruhd"><strong>Как искать:</strong></p>
  <ol id="xKUn">
    <li id="V5Bi">Вручную перебирать возможные пути в архиве.</li>
    <li id="3pM7">Использовать поиск по <code>*</code> или анализировать список всех сохранённых страниц через CDX API.</li>
    <li id="7Ejt">Парсить CDX-вывод и искать подозрительные пути.</li>
  </ol>
  <p id="3vey"><strong>Пример CDX-запроса для получения всех страниц сайта (включая подпапки):</strong></p>
  <pre id="5JZ7">curl &quot;https://web.archive.org/cdx/search/cdx?url=example.com/*&amp;output=json&quot; &gt; all_pages.json</pre>
  <p id="SZnY"><strong>Этап 4: Сравнение версий (diff) — что изменилось?</strong></p>
  <p id="QCZ2">Wayback Machine позволяет сравнивать два снимка страницы. Это полезно для выявления изменений в контенте, политике или структуре.</p>
  <p id="tAfx"><strong>Инструменты сравнения:</strong></p>
  <ul id="3DgV">
    <li id="YpIR"><strong>Веб-интерфейс:</strong> <code>https://web.archive.org/web/20200101000000/https://example.com</code> и <code>https://web.archive.org/web/20250101000000/https://example.com</code> → открыть в двух вкладках и сравнить.</li>
    <li id="wHvw"><strong>Сторонние утилиты:</strong> <code>wget --mirror</code> для загрузки двух архивных версий и сравнения через <code>diff</code>.</li>
  </ul>
  <p id="tJ9o"><strong>Сравнение через Python:</strong></p>
  <pre id="qnBF">import requests
import difflib

def fetch_archived_page(timestamp, url):
    archive_url = f&quot;https://web.archive.org/web/{timestamp}/{url}&quot;
    resp = requests.get(archive_url)
    return resp.text if resp.status_code == 200 else &quot;&quot;

def compare_pages(url, ts1, ts2):
    text1 = fetch_archived_page(ts1, url)
    text2 = fetch_archived_page(ts2, url)
    diff = difflib.unified_diff(text1.splitlines(), text2.splitlines())
    return &#x27;\n&#x27;.join(diff)

# Пример
changes = compare_pages(&#x27;example.com&#x27;, &#x27;20200101000000&#x27;, &#x27;20250101000000&#x27;)
print(changes[:500])  # первые 500 символов</pre>
  <p id="tmGL"><strong>Этап 5: Поиск старых robots.txt, sitemap.xml и других служебных файлов</strong></p>
  <p id="eY24"><code>robots.txt</code> и <code>sitemap.xml</code> часто меняются. В старых версиях могут содержаться пути, которые позже были удалены.</p>
  <p id="cVPJ"><strong>Пример:</strong></p>
  <pre id="spBW">https://web.archive.org/web/*/https://example.com/robots.txt
https://web.archive.org/web/*/https://example.com/sitemap.xml</pre>
  <p id="3GXx"><strong>Что можно найти:</strong></p>
  <ul id="dtuF">
    <li id="DpNk">Директории, запрещённые для индексации (например, <code>/admin/</code>, <code>/private/</code>).</li>
    <li id="fZfw">Поддомены, которые не разрешено индексировать (<code>Disallow: /subdomain/</code>).</li>
    <li id="2W8q">Карта сайта с перечнем всех страниц (удалённые, но сохранённые).</li>
  </ul>
  <p id="9NXQ"><strong>Этап 6: Поиск конфиденциальных данных в архивах</strong></p>
  <p id="E4OX">Одна из самых опасных ситуаций — случайная публикация конфиденциальных данных, которые затем были удалены, но остались в архиве.</p>
  <p id="uL3p"><strong>Что искать:</strong></p>
  <ul id="kpKY">
    <li id="Lwkj"><code>.env</code> (файлы окружения с паролями).</li>
    <li id="LLu3"><code>.git/config</code> (адреса репозиториев).</li>
    <li id="Adnr"><code>phpinfo()</code> (информация о PHP-конфигурации).</li>
    <li id="IIp0"><code>backup.sql</code> (дампы базы данных).</li>
    <li id="1i4u"><code>config.php</code>, <code>settings.php</code> (конфигурационные файлы).</li>
  </ul>
  <p id="1iQt"><strong>Как искать:</strong></p>
  <ol id="ME4N">
    <li id="6P3T">Вручную проверять типичные пути: <code>/backup</code>, <code>/tmp</code>, <code>/downloads</code>.</li>
    <li id="c83e">Использовать поиск по <code>*</code> и фильтровать по расширениям.</li>
    <li id="UZrm">Использовать инструменты парсинга архивов для массового поиска.</li>
  </ol>
  <p id="Kqiv"><strong>Пример поиска <code>.env</code> файлов:</strong></p>
  <pre id="rEkH">https://web.archive.org/web/*/https://example.com/*.env</pre>
  <p id="ti53">Wayback Machine не сохраняет все файлы (только HTML, CSS, JS), но дампы баз или бинарные файлы могут быть недоступны.</p>
  <p id="CpER"><strong>Этап 7: Анализ изменений в SSL-сертификатах через архивы</strong></p>
  <p id="VKPd">Хотя Wayback Machine не сохраняет SSL-сертификаты, история сертификатов доступна через CT-логи (см. статью 7). Однако, анализируя старые снимки сайта, можно заметить:</p>
  <ul id="kl9R">
    <li id="14rl">Когда на сайте появилась поддержка HTTPS (по редиректам).</li>
    <li id="5xzx">Когда был заменён логотип или изменилась политика безопасности (по заголовкам HTTP в сохранённых снимках, если они есть).</li>
  </ul>
  <p id="npTP"><strong>Этап 8: Использование <a href="https://archive.today/" target="_blank">Archive.today</a> для поиска удалённого контента</strong></p>
  <p id="s1lj"><a href="https://archive.today/" target="_blank">Archive.today</a> часто сохраняет страницы, которые не попали в Wayback Machine (например, из-за robots.txt). Он позволяет архивировать страницы вручную.</p>
  <p id="Fcxh"><strong>Как использовать:</strong></p>
  <ol id="4MH6">
    <li id="oWZp">Ввести URL на <a href="https://archive.today/" target="_blank">archive.today</a>.</li>
    <li id="5lPU">Если страница была сохранена, она отобразится.</li>
    <li id="rJsF">Можно найти сохранения по дате.</li>
  </ol>
  <p id="uIDZ">Нет публичного API. Только веб-интерфейс.</p>
  <p id="d4Gi"><strong>Этап 9: Автоматизированный сбор изменений с помощью Wayback Machine API</strong></p>
  <p id="1hrY">Для долгосрочного мониторинга сайта можно автоматизировать проверку новых снимков в архиве.</p>
  <p id="FyBn"><strong>Скрипт мониторинга новых версий:</strong></p>
  <pre id="Vn7e">import requests
import time

def get_latest_snapshot(url):
    # CDX API
    cdx_url = f&quot;https://web.archive.org/cdx/search/cdx?url={url}&amp;output=json&amp;limit=1&amp;sort=timestamp&quot;
    resp = requests.get(cdx_url)
    if resp.status_code == 200:
        data = resp.json()
        if len(data) &gt; 1:
            timestamp = data[1][1]
            return timestamp
    return None

def monitor_website(url, check_interval=86400):  # 1 день
    last_timestamp = None
    while True:
        latest = get_latest_snapshot(url)
        if latest and latest != last_timestamp:
            print(f&quot;Обнаружена новая версия {url} в архиве: {latest}&quot;)
            # Здесь можно обработать изменения
            last_timestamp = latest
        time.sleep(check_interval)</pre>
  <p id="xz1I"><strong>Этап 10: Поиск через CDX API для составления карты сайта</strong></p>
  <p id="MZS8">CDX API можно использовать для получения полного списка всех когда-либо сохранённых страниц сайта. Это позволяет восстановить структуру сайта даже без текущего доступа.</p>
  <p id="VY4v"><strong>Получение списка всех страниц (с ограничениями):</strong></p>
  <pre id="AtVg">curl &quot;https://web.archive.org/cdx/search/cdx?url=example.com/*&amp;output=json&amp;limit=100000&quot; &gt; all_pages.json</pre>
  <p id="sK7T"><strong>Парсинг результатов для построения карты сайта:</strong></p>
  <pre id="XXYw">import json
import re

def extract_all_paths(cdx_json_file):
    with open(cdx_json_file, &#x27;r&#x27;) as f:
        data = json.load(f)
    paths = set()
    for row in data[1:]:
        url = row[2]  # полный URL
        # извлекаем путь
        path = re.sub(r&#x27;^https?://[^/]+&#x27;, &#x27;&#x27;, url)
        paths.add(path)
    return sorted(paths)

# Пример использования
paths = extract_all_paths(&#x27;all_pages.json&#x27;)
for p in paths[:50]:
    print(p)</pre>
  <p id="adTp"><strong>Практический пример: полное исследование сайта через архивы</strong></p>
  <p id="7zyf"><em>Задача:</em> собрать максимально полную информацию о сайте <code>target.org</code> через архивы.</p>
  <ol id="aSoS">
    <li id="hiEe"><strong>Wayback Machine CDX</strong> → получаем список всех 5000 сохранённых страниц.</li>
    <li id="5kpz"><strong>Анализ путей:</strong></li>
    <ul id="iDb1">
      <li id="x9AE">Обнаружены пути <code>/admin</code>, <code>/test</code>, <code>/backup_2022</code>, <code>/temp</code>.</li>
      <li id="rr8B">Страница <code>/admin/login.html</code> существует в архиве с 2020 года.</li>
    </ul>
    <li id="OwRU"><strong>Поиск файлов:</strong></li>
    <ul id="Slfb">
      <li id="8o0K"><code>https://web.archive.org/web/20220101000000/target.org/.git/config</code> — найден конфиг репозитория.</li>
      <li id="uv0H"><code>https://web.archive.org/web/20200101000000/target.org/backup.sql</code> — дамп базы данных.</li>
    </ul>
    <li id="Vmah"><strong>Сравнение версий:</strong></li>
    <ul id="QqWT">
      <li id="ZAFX">Сравнили <code>index.html</code> в 2020 и 2024 → структура сильно изменилась, добавилась поддержка HTTPS, появился CSP.</li>
    </ul>
    <li id="g1VX"><strong>Скрытые поддомены:</strong></li>
    <ul id="8XlX">
      <li id="6AC2">На старой robots.txt найдена запись <code>Disallow: /dev/</code>. Открываем архивный URL <code>https://web.archive.org/web/20200101000000/target.org/dev/</code> → обнаруживаем тестовый сайт с открытыми ошибками PHP.</li>
    </ul>
    <li id="YBVv"><strong>Вывод:</strong> Найдены забытые тестовые страницы, конфигурационный файл и дамп базы данных. В текущей версии сайта они удалены, но остались в архиве.</li>
  </ol>
  <p id="ITAF"><strong>Инструменты для работы с архивами</strong></p>
  <figure id="8j2P" class="m_column">
    <img src="https://img1.teletype.in/files/8c/9a/8c9a77a3-3940-469d-a7ed-dcf8e2b95d43.png" width="1004" />
  </figure>
  <ul id="Fl2g">
    <li id="Euvt">Данные Wayback Machine являются публичными, их сбор легален.</li>
    <li id="4DG2">Не используйте найденные уязвимости для атак на сайт без разрешения.</li>
    <li id="vfIu">Если вы нашли конфиденциальные данные (пароли, токены) в архиве, сообщите об этом владельцу сайта через официальный канал (без распространения информации третьим лицам).</li>
    <li id="cy8b">Некоторые страны требуют удаления личных данных из архивов — уважайте такие запросы.</li>
  </ul>
  <p id="ZXD8">Анализ истории сайта через Wayback Machine и другие архивы — важный этап технического OSINT, позволяющий заглянуть в прошлое веб-ресурса. Удалённые страницы, забытые поддомены, случайно опубликованные конфиденциальные данные, старая robots.txt — всё это может быть восстановлено, если было сохранено в архиве. Комбинируя CDX API, поиск по шаблонам и ручной анализ, исследователь может восстановить почти полную ист</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@specialist_infosec/Vulnerability-search-through-OSINT</guid><link>https://teletype.in/@specialist_infosec/Vulnerability-search-through-OSINT?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec</link><comments>https://teletype.in/@specialist_infosec/Vulnerability-search-through-OSINT?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec#comments</comments><dc:creator>specialist_infosec</dc:creator><title>Поиск уязвимостей через открытые источники: базы CVE, NVD, Exploit-DB и связь с версиями ПО</title><pubDate>Sat, 27 Jun 2026 16:28:23 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/90/b2/90b2d41c-87cb-450f-a82b-012a4c2e7ea7.png"></media:content><description><![CDATA[<img src="https://img3.teletype.in/files/6c/1c/6c1c1980-fe76-4005-a8dc-20cb2d941a65.png"></img>После того как в ходе технического OSINT выявлены версии программного обеспечения, используемого целью (веб-сервер, CMS, библиотеки, операционная система), логичный следующий шаг — выяснить, есть ли у этих версий известные уязвимости. Открытые базы данных уязвимостей (CVE, NVD, Exploit-DB, Vulners и др.) позволяют сопоставить версию ПО с известными CVE (Common Vulnerabilities and Exposures), оценить критичность и даже найти готовые эксплойты. Это помогает понять, насколько цель подвержена атакам, и приоритезировать дальнейшие действия. Данная статья посвящена методам поиска уязвимостей по версиям ПО с использованием открытых источников.]]></description><content:encoded><![CDATA[
  <figure id="zxLK" class="m_retina">
    <img src="https://img3.teletype.in/files/6c/1c/6c1c1980-fe76-4005-a8dc-20cb2d941a65.png" width="1099" />
  </figure>
  <h3 id="R5Kp">Поиск уязвимостей через открытые источники: базы CVE, NVD, Exploit-DB и связь с версиями ПО</h3>
  <p id="P50k">После того как в ходе технического OSINT выявлены версии программного обеспечения, используемого целью (веб-сервер, CMS, библиотеки, операционная система), логичный следующий шаг — выяснить, есть ли у этих версий известные уязвимости. Открытые базы данных уязвимостей (CVE, NVD, Exploit-DB, Vulners и др.) позволяют сопоставить версию ПО с известными CVE (Common Vulnerabilities and Exposures), оценить критичность и даже найти готовые эксплойты. Это помогает понять, насколько цель подвержена атакам, и приоритезировать дальнейшие действия. Данная статья посвящена методам поиска уязвимостей по версиям ПО с использованием открытых источников.</p>
  <p id="aVvw"><strong>Что такое CVE, CVSS, NVD, Exploit-DB</strong></p>
  <figure id="Sel7" class="m_column">
    <img src="https://img3.teletype.in/files/27/03/27031fce-341d-42a3-8a66-fa48f9fc69e7.png" width="1207" />
  </figure>
  <p id="0mNK"><strong>Зачем это нужно в техническом OSINT?</strong></p>
  <ol id="uM35">
    <li id="Mcyw"><strong>Оценка рисков:</strong> понять, насколько критичны уязвимости в используемом ПО.</li>
    <li id="zmRU"><strong>Приоритизация проверок:</strong> какие уязвимости следует тестировать в первую очередь.</li>
    <li id="GX1v"><strong>Обнаружение уже скомпрометированных систем:</strong> если на целевой системе есть уязвимая версия и известен активный эксплойт — она могла быть взломана.</li>
    <li id="5GxT"><strong>Помощь в легальном пентесте:</strong> знание уязвимостей позволяет сосредоточить усилия.</li>
  </ol>
  <p id="nB55">Простое наличие уязвимой версии ПО не означает, что цель автоматически скомпрометирована. Это лишь индикатор потенциальной уязвимости.</p>
  <p id="ZETQ"><strong>Этап 1: Поиск CVE по названию продукта и версии</strong></p>
  <p id="4JRm">Самый простой способ — использовать поисковые системы по CVE с фильтрацией.</p>
  <p id="wJDg"><strong>Основные базы данных:</strong></p>
  <figure id="C5Py" class="m_column">
    <img src="https://img4.teletype.in/files/b6/c3/b6c3c705-30dc-4006-a5e9-ad6126c56496.png" width="1206" />
  </figure>
  <p id="lifm"><strong>Поиск по продукту в NVD:</strong></p>
  <pre id="2ZXQ">https://nvd.nist.gov/vuln/search/results?query=nginx&amp;results_type=overview</pre>
  <p id="C40H"><strong>Поиск с версией в NVD (сложно):</strong> NVD не всегда индексирует версии, лучше использовать Vulners.</p>
  <p id="pFBh"><strong>Vulners search (пример):</strong></p>
  <pre id="lZ7b">https://vulners.com/search?query=nginx%201.18.0</pre>
  <p id="QbXa"><strong>Этап 2: Использование Vulners API для массового поиска</strong></p>
  <p id="5oYT">Vulners предоставляет бесплатный API с лимитами. Позволяет искать по CPE (Common Platform Enumeration) — стандартизированному формату описания ПО.</p>
  <p id="QBPm"><strong>Формат CPE:</strong> <code>cpe:2.3:a:nginx:nginx:1.18.0:*:*:*:*:*:*:*</code></p>
  <p id="eIOg"><strong>Упрощённый поиск через API Vulners:</strong></p>
  <pre id="xMeU">import requests
import json

VULNERS_API_KEY = &quot;YOUR_API_KEY&quot;

def search_vulnerabilities(product, version, api_key=VULNERS_API_KEY):
    &quot;&quot;&quot;
    Поиск уязвимостей по названию продукта и версии.
    &quot;&quot;&quot;
    query = f&#x27;{product} {version}&#x27;
    url = &quot;https://vulners.com/api/v3/search/lucene/&quot;
    payload = {
        &quot;query&quot;: query,
        &quot;apiKey&quot;: api_key
    }
    response = requests.post(url, json=payload)
    if response.status_code == 200:
        data = response.json()
        if &#x27;data&#x27; in data and &#x27;search&#x27; in data[&#x27;data&#x27;]:
            return data[&#x27;data&#x27;][&#x27;search&#x27;]
    return []

def get_cves_for_software(software_list):
    results = {}
    for sw in software_list:
        cves = search_vulnerabilities(sw[&#x27;name&#x27;], sw[&#x27;version&#x27;])
        results[f&quot;{sw[&#x27;name&#x27;]} {sw[&#x27;version&#x27;]}&quot;] = cves
    return results

# Пример
software = [
    {&#x27;name&#x27;: &#x27;nginx&#x27;, &#x27;version&#x27;: &#x27;1.18.0&#x27;},
    {&#x27;name&#x27;: &#x27;php&#x27;, &#x27;version&#x27;: &#x27;7.4.33&#x27;},
    {&#x27;name&#x27;: &#x27;wordpress&#x27;, &#x27;version&#x27;: &#x27;6.2&#x27;}
]
vulns = get_cves_for_software(software)
for k, v in vulns.items():
    print(f&quot;{k}: {len(v)} vulnerabilities&quot;)</pre>
  <p id="kgz4"><strong>Этап 3: Использование CIRCL CVE Search API (без ключа)</strong></p>
  <p id="13CH">CIRCL предлагает публичный API, не требующий авторизации.</p>
  <p id="JY7c"><strong>Пример запроса для поиска CVE по ключевому слову nginx:</strong></p>
  <pre id="Vk5m">curl https://cve.circl.lu/api/search/nginx</pre>
  <p id="WFew"><strong>Для извлечения информации о конкретном CVE:</strong></p>
  <pre id="PR5M">curl https://cve.circl.lu/api/cve/CVE-2021-23017</pre>
  <p id="GWOW">CIRCL не позволяет фильтровать по версии, ищет по тексту.</p>
  <p id="COUy"><strong>Этап 4: Поиск эксплойтов (Exploit-DB, Rapid7, Packet Storm)</strong></p>
  <p id="Pjl0">Наличие эксплойта повышает вероятность того, что уязвимость будет использована.</p>
  <p id="pkUV"><strong>Exploit-DB:</strong></p>
  <ul id="CHcM">
    <li id="4grd">Поиск по продукту и версии: <code>https://www.exploit-db.com/search?q=nginx+1.18.0</code></li>
    <li id="o9Dz">Можно скачать готовый код (на свой страх и риск).</li>
  </ul>
  <p id="jUWr"><strong>Rapid7 DB (Metasploit modules):</strong></p>
  <ul id="vGsd">
    <li id="VAHb">Поиск: <code>https://www.rapid7.com/db/search/?q=nginx</code></li>
  </ul>
  <p id="UEYg"><strong>Packet Storm Security:</strong></p>
  <ul id="2UzU">
    <li id="kTvR"><code>https://packetstormsecurity.com/search/?q=nginx</code></li>
  </ul>
  <p id="WfRw"><strong>Пример автоматизации через Exploit-DB (неофициальный API, лучше парсить):</strong></p>
  <pre id="tCQY">import requests
from bs4 import BeautifulSoup

def search_exploitdb(product, version):
    url = f&quot;https://www.exploit-db.com/search?q={product}+{version}&quot;
    resp = requests.get(url)
    soup = BeautifulSoup(resp.text, &#x27;html.parser&#x27;)
    # Парсинг таблицы результатов (сложно, зависит от структуры)
    # Возвращает список заголовков эксплойтов
    return []

# Ручной поиск вручную часто проще</pre>
  <p id="nMGR"><strong>Этап 5: Сопоставление версий ПО с CPE для точного поиска</strong></p>
  <p id="pqEX">Чтобы искать CVE, лучше преобразовать название и версию в CPE-формат. Это повышает точность.</p>
  <p id="TKZB"><strong>Сервис CPE Dictionary (NVD):</strong></p>
  <ul id="Ju7p">
    <li id="3Mjw"><code>https://nvd.nist.gov/products/cpe/search</code></li>
    <li id="ZJeo">API: <code>https://services.nvd.nist.gov/rest/json/cpes/2.0?keyword=nginx</code></li>
  </ul>
  <p id="Dxlm"><strong>Автоматическое определение CPE через cpe-guesser (исследовательский):</strong></p>
  <pre id="AX3P">def guess_cpe(product, version):
    # упрощённый пример
    product_lower = product.lower()
    if product_lower == &#x27;nginx&#x27;:
        return f&quot;cpe:2.3:a:nginx:nginx:{version}:*:*:*:*:*:*:*&quot;
    elif product_lower == &#x27;wordpress&#x27;:
        return f&quot;cpe:2.3:a:wordpress:wordpress:{version}:*:*:*:*:*:*:*&quot;
    # ...
    return None</pre>
  <p id="2StY"><strong>Этап 6: Анализ CVSS (критичность уязвимости)</strong></p>
  <p id="iddC">Каждому CVE присваивается оценка CVSS. Чем выше, тем критичнее.</p>
  <p id="zlfF"><strong>CVSS v3 шкала:</strong></p>
  <figure id="W7mk" class="m_column">
    <img src="https://img2.teletype.in/files/54/56/54561dd9-0302-4330-acc7-752cdb9c77ae.png" width="929" />
  </figure>
  <p id="b1Qj"><strong>Вектор CVSS</strong> (пример): <code>CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H</code> означает: сетевой доступ, низкая сложность, не требуется привилегий, полное воздействие на конфиденциальность, целостность и доступность.</p>
  <p id="8wed"><strong>Где брать CVSS:</strong> NVD, Vulners, некоторые API.</p>
  <p id="tiPI"><strong>Этап 7: Автоматизированный сбор уязвимостей для всех технологий стека</strong></p>
  <p id="MKvZ">Комбинируем результаты предыдущих статей (технологический стек) с базами уязвимостей.</p>
  <p id="xtcr"><strong>Python-скрипт для массовой проверки (на основе данных из Wappalyzer, BuiltWith):</strong></p>
  <pre id="nYk6">import requests
import time
import json

def get_cves_from_nvd(keyword, api_key=None):
    &quot;&quot;&quot;
    Поиск в NVD через API (требуется ключ, можно получить бесплатно).
    Без ключа возвращает только первые 20 результатов.
    &quot;&quot;&quot;
    url = &quot;https://services.nvd.nist.gov/rest/json/cves/2.0&quot;
    params = {
        &#x27;keywordSearch&#x27;: keyword,
        &#x27;resultsPerPage&#x27;: 10
    }
    if api_key:
        params[&#x27;apiKey&#x27;] = api_key
    response = requests.get(url, params=params)
    if response.status_code == 200:
        data = response.json()
        cves = []
        for vuln in data.get(&#x27;vulnerabilities&#x27;, []):
            cve_id = vuln[&#x27;cve&#x27;][&#x27;id&#x27;]
            cvss_score = vuln[&#x27;cve&#x27;].get(&#x27;metrics&#x27;, {}).get(&#x27;cvssMetricV31&#x27;, [{}])[0].get(&#x27;cvssData&#x27;, {}).get(&#x27;baseScore&#x27;, &#x27;N/A&#x27;)
            cves.append({&#x27;id&#x27;: cve_id, &#x27;cvss_score&#x27;: cvss_score})
        return cves
    else:
        print(f&quot;Ошибка NVD: {response.status_code}&quot;)
        return []

# Пример для nginx 1.18.0
cves = get_cves_from_nvd(&quot;nginx 1.18.0&quot;)
for cve in cves:
    print(f&quot;{cve[&#x27;id&#x27;]} (CVSS {cve[&#x27;cvss_score&#x27;]})&quot;)</pre>
  <p id="6DN6">NVD API имеет rate limit (без ключа ~5 запросов в 30 секунд). Используйте ключ или библиотеку с задержками.</p>
  <p id="qJxj"><strong>Этап 8: Поиск уязвимостей в WordPress плагинах и темах</strong></p>
  <p id="IcdP">WordPress — особая категория: уязвимости часто связаны с плагинами и темами, а не с ядром.</p>
  <p id="5pQP"><strong>Базы для WordPress:</strong></p>
  <ul id="CS5I">
    <li id="WwGP"><strong>WPScan Vulnerability Database:</strong> <code>https://wpscan.com/api</code> (есть бесплатный API с ограничениями).</li>
    <li id="il6l"><strong>Patchstack:</strong> <code>https://patchstack.com/database/</code></li>
  </ul>
  <p id="UXkG"><strong>Пример поиска через WPScan API:</strong></p>
  <pre id="hK24">def check_wpscan_plugin(plugin_slug):
    url = f&quot;https://wpscan.com/api/v3/plugins/{plugin_slug}&quot;
    response = requests.get(url)
    if response.status_code == 200:
        data = response.json()
        return data.get(&#x27;vulnerabilities&#x27;, [])
    return []</pre>
  <p id="5NJt"><strong>Этап 9: Анализ базы Known Exploited Vulnerabilities (CISA KEV)</strong></p>
  <p id="BRWA">CISA ведёт список активно эксплуатируемых уязвимостей (KEV). Если CVE есть в этом списке, оно представляет большую угрозу.</p>
  <p id="gCmT"><strong>API KEV:</strong> <a href="https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json" target="_blank">https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json</a></p>
  <pre id="65tP">def is_in_kev(cve_id):
    url = &quot;https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json&quot;
    resp = requests.get(url)
    if resp.status_code == 200:
        data = resp.json()
        for item in data[&#x27;vulnerabilities&#x27;]:
            if item[&#x27;cveID&#x27;] == cve_id:
                return True
    return False</pre>
  <p id="bddm"><strong>Этап 10: Составление отчёта об уязвимостях</strong></p>
  <p id="9HmG">Для каждой версии ПО, найденной на цели, следует создать таблицу:</p>
  <figure id="0IP1" class="m_column">
    <img src="https://img3.teletype.in/files/e4/83/e4837925-ab85-4e55-ab93-f6bc56444913.png" width="907" />
  </figure>
  <p id="70fR">Суммировать количество критических уязвимостей, наличие эксплойтов, попадание в KEV.</p>
  <p id="Yldi"><strong>Практический пример: оценка рисков для веб-сервера цели</strong></p>
  <p id="dWpj"><em>Задача:</em> для сервера nginx 1.18.0 + PHP 7.4.33 найти известные уязвимости.</p>
  <ol id="ehHE">
    <li id="0U41"><strong>Поиск через Vulners:</strong></li>
    <ul id="Xx1V">
      <li id="2h3H">nginx 1.18.0: 3 CVE, все с CVSS ниже 6.0. Эксплойтов нет.</li>
      <li id="bE77">PHP 7.4.33: 12 CVE, из них 4 с CVSS &gt; 7.0. Один эксплойт в Metasploit.</li>
    </ul>
    <li id="u1R2"><strong>Проверка CISA KEV:</strong> PHP 7.4.33 не в списке KEV.</li>
    <li id="KmQF"><strong>Вывод:</strong> основной риск — уязвимости PHP (особенно CVE-2022-31628, позволяющая удалённое выполнение кода). Рекомендуется обновить PHP до 8.x.</li>
  </ol>
  <ul id="68sC">
    <li id="8wIw">Поиск уязвимостей в открытых базах данных легален.</li>
    <li id="tJ9m">Запуск эксплойтов на целевой системе без разрешения — уголовное преступление.</li>
    <li id="1UQO">Обнаруженную уязвимость следует сообщить владельцу системы через программы Bug Bounty или координаторы (CERT).</li>
    <li id="bCtd">Не следует тестировать эксплойты на чужих системах.</li>
  </ul>
  <p id="pz1M">Поиск уязвимостей по версиям ПО — неотъемлемая часть технического OSINT. Базы CVE, NVD, Vulners, Exploit-DB и CISA KEV дают исследователю возможность оценить поверхность атаки, не прибегая к активным сканерам уязвимостей. Сопоставление версий из предыдущих статей (технологический стек, версии ПО, заголовки) с уязвимостями позволяет построить рейтинг рисков и определить приоритет для дальнейшего тестирования.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@specialist_infosec/Headline-Analysis</guid><link>https://teletype.in/@specialist_infosec/Headline-Analysis?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec</link><comments>https://teletype.in/@specialist_infosec/Headline-Analysis?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=specialist_infosec#comments</comments><dc:creator>specialist_infosec</dc:creator><title>Анализ заголовков HTTP: серверы, cookies, политики безопасности</title><pubDate>Wed, 24 Jun 2026 08:29:47 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/dd/21/dd211fb3-5814-4a2d-9527-9f9725b7a7ad.png"></media:content><description><![CDATA[<img src="https://img2.teletype.in/files/98/b2/98b2a327-44d0-47ce-924f-402d58919182.png"></img>HTTP-заголовки — это метаданные, которыми обмениваются клиент (браузер) и сервер при каждом запросе. Для специалиста по техническому OSINT заголовки — ценный источник информации: они раскрывают версии программного обеспечения, используемые технологии, настройки безопасности и даже особенности конфигурации. Помимо этого, заголовки безопасности (CSP, HSTS, X-Frame-Options) позволяют оценить уровень защищённости веб-приложения и выявить потенциальные уязвимости. Данная статья посвящена методам сбора и анализа HTTP-заголовков, а также извлечению разведывательной информации из них.]]></description><content:encoded><![CDATA[
  <figure id="1ash" class="m_retina">
    <img src="https://img2.teletype.in/files/98/b2/98b2a327-44d0-47ce-924f-402d58919182.png" width="1102" />
  </figure>
  <h3 id="DpJO">Анализ заголовков HTTP: серверы, cookies, политики безопасности (CSP, HSTS, X-Frame-Options и другие)</h3>
  <p id="677c">HTTP-заголовки — это метаданные, которыми обмениваются клиент (браузер) и сервер при каждом запросе. Для специалиста по техническому OSINT заголовки — ценный источник информации: они раскрывают версии программного обеспечения, используемые технологии, настройки безопасности и даже особенности конфигурации. Помимо этого, заголовки безопасности (CSP, HSTS, X-Frame-Options) позволяют оценить уровень защищённости веб-приложения и выявить потенциальные уязвимости. Данная статья посвящена методам сбора и анализа HTTP-заголовков, а также извлечению разведывательной информации из них.</p>
  <p id="KpN1"><strong>Почему HTTP-заголовки важны для технического OSINT?</strong></p>
  <figure id="gvRN" class="m_column">
    <img src="https://img2.teletype.in/files/51/e9/51e9b4d7-59e3-478b-96a2-37d55de03f24.png" width="1203" />
  </figure>
  <p id="YeYR"><strong>Этап 1: Сбор заголовков (базовые инструменты)</strong></p>
  <p id="GSEO"><strong>1. curl</strong>  — самый простой способ:</p>
  <pre id="wlFl"># Показать только заголовки ответа
curl -I https://example.com

# Показать заголовки запроса и ответа
curl -v https://example.com

# Сохранить заголовки в файл
curl -D headers.txt https://example.com</pre>
  <p id="DbkW"><strong>2. wget</strong>:</p>
  <pre id="2LVx">wget --server-response -O /dev/null https://example.com</pre>
  <p id="t82e"><strong>3. Browser DevTools</strong> (F12 → Network → выбрать запрос → Headers).</p>
  <p id="HHOD"><strong>4. Онлайн сервисы:</strong></p>
  <ul id="rcRM">
    <li id="CrJX"><strong><a href="https://securityheaders.com/" target="_blank">SecurityHeaders.com</a></strong> — проверяет наличие и корректность заголовков безопасности, выставляет оценку.</li>
    <li id="BxsH"><strong>HTTPHeaderCheck</strong> (<a href="https://httpheadercheck.com/" target="_blank">httpheadercheck.com</a>) — анализирует заголовки и даёт рекомендации.</li>
    <li id="enPC"><strong>Mozilla Observatory</strong> — глубокий анализ безопасности с рекомендациями.</li>
  </ul>
  <p id="gEnS"><strong>Этап 2: Анализ идентификационных заголовков</strong></p>
  <p id="AFdb">Эти заголовки могут выдать технологии, версии ПО, что полезно для обнаружения устаревших или уязвимых компонентов.</p>
  <p id="M6Bz"><strong><code>Server:</code></strong></p>
  <ul id="rInk">
    <li id="F2pV">Указывает тип и версию веб-сервера. Примеры: <code>nginx/1.18.0</code>, <code>Apache/2.4.41 (Ubuntu)</code>, <code>Microsoft-IIS/10.0</code>, <code>Cloudflare</code>.</li>
    <li id="aBpM">Часто скрывается администраторами (<code>Server: Apache</code>, без версии). Если версия показана — сверьте с базами уязвимостей.</li>
  </ul>
  <p id="HLI9"><strong><code>X-Powered-By:</code></strong></p>
  <ul id="dVhk">
    <li id="Dedf">Показывает технологию бэкенда. Например: <code>PHP/7.4.33</code>, <code>ASP.NET</code>, <code>Express</code>. Может дать версию языка/фреймворка.</li>
  </ul>
  <p id="vaM0"><strong><code>X-Generator:</code></strong></p>
  <ul id="znte">
    <li id="KUho">Иногда содержит название CMS или генератора контента (например, <code>WordPress 6.2</code>).</li>
  </ul>
  <p id="Sb0C"><strong><code>X-AspNet-Version:</code></strong></p>
  <ul id="jxVW">
    <li id="h7Tx">Для <a href="https://asp.net/" target="_blank">ASP.NET</a>. Пример: <code>X-AspNet-Version: 4.0.30319</code>.</li>
  </ul>
  <p id="bkEa"><strong><code>X-Powered-By-Plesk:</code></strong> (если используется панель управления хостингом).</p>
  <p id="8PST"><strong>Этап 3: Анализ cookie и сессионных заголовков</strong></p>
  <p id="XL8h">Заголовок <code>Set-Cookie</code> не только управляет сессиями, но и выдаёт технологии.</p>
  <p id="Qgd1"><strong><code>Set-Cookie:</code></strong></p>
  <ul id="ZvoF">
    <li id="cAt9"><code>PHPSESSID=...</code> → PHP (версия сессионного механизма).</li>
    <li id="W1dy"><code>JSESSIONID=...</code> → Java (JSP).</li>
    <li id="Kj7D"><code>ASPSESSIONID...</code> → <a href="https://asp.net/" target="_blank">ASP.NET</a>.</li>
    <li id="NkAq"><code>laravel_session=...</code> → Laravel (PHP-фреймворк).</li>
    <li id="9058"><code>XSRF-TOKEN</code> → часто используется в Angular, Laravel, Django.</li>
  </ul>
  <p id="uvdA"><strong>Флаги cookie (httponly, secure, samesite):</strong></p>
  <ul id="PFKG">
    <li id="KIGi"><code>HttpOnly</code> — недоступен для JavaScript (хорошо для безопасности).</li>
    <li id="y3F4"><code>Secure</code> — только через HTTPS.</li>
    <li id="MaLg"><code>SameSite=Lax/Strict</code> — защита от CSRF.</li>
  </ul>
  <p id="WJf8"><strong>Этап 4: Заголовки безопасности (Security Headers)</strong></p>
  <p id="MMxV">Оценка безопасности веб-приложения — важная часть разведки. Слабые настройки могут привести к уязвимостям (кликджекинг, XSS, перехват сессий).</p>
  <p id="LP8t"><strong>4.1. Content-Security-Policy (CSP)</strong></p>
  <p id="EDCA">CSP — белый список источников загрузки ресурсов (скриптов, стилей, изображений). Хорошо настроенный CSP сильно уменьшает риск XSS.</p>
  <p id="V39N"><strong>Анализ CSP:</strong></p>
  <ul id="iDXM">
    <li id="DFir"><code>default-src &#x27;none&#x27;</code> — жёсткая политика.</li>
    <li id="7YUL"><code>script-src &#x27;self&#x27;</code> — разрешены скрипты только с того же домена.</li>
    <li id="ryPG"><code>unsafe-inline</code>, <code>unsafe-eval</code> — опасные директивы, делают CSP бесполезным.</li>
    <li id="dFii"><code>report-uri /report-csp-violation</code> — куда отправлять отчёты о нарушениях.</li>
  </ul>
  <p id="vFsn"><strong>Ошибки:</strong></p>
  <ul id="KXIB">
    <li id="5DVA">Отсутствие CSP — распространённая проблема.</li>
    <li id="wI5u">Наличие <code>unsafe-inline</code> без nonce/hash — снижает защиту.</li>
    <li id="UYUv">Слишком широкие источники (<code>*.example.com</code>, <code>https:</code>).</li>
  </ul>
  <p id="8Fo3"><strong>4.2. HSTS (HTTP Strict Transport Security)</strong></p>
  <p id="efPa">Принудительное использование HTTPS для всех подключений к домену.</p>
  <p id="KMXW"><strong>Пример:</strong> <code>Strict-Transport-Security: max-age=31536000; includeSubDomains; preload</code></p>
  <p id="9U1Y"><strong>Анализ:</strong></p>
  <ul id="TimN">
    <li id="BZOg"><code>max-age=0</code> — отключено (опасно).</li>
    <li id="g57z"><code>includeSubDomains</code> — принудительный HTTPS для всех поддоменов.</li>
    <li id="7Xi0"><code>preload</code> — домен включён в предзагружаемый список браузеров (максимальный уровень).</li>
    <li id="02ri">Отсутствие HSTS — возможность атаки «понижения до HTTP» (SSL stripping).</li>
  </ul>
  <p id="Webn"><strong>4.3. X-Frame-Options</strong></p>
  <p id="LUTv">Защита от кликджекинга (встраивания сайта в frame).</p>
  <p id="4C47"><strong>Значения:</strong></p>
  <ul id="2HrA">
    <li id="cqOE"><code>DENY</code> — запрещено встраивание в frame.</li>
    <li id="3i2f"><code>SAMEORIGIN</code> — разрешено только с того же домена.</li>
    <li id="ojt7"><code>ALLOW-FROM uri</code> — устаревшее.</li>
    <li id="LNeN">Отсутствие заголовка — сайт можно встроить в <code>&lt;iframe&gt;</code> (угроза UI redressing).</li>
  </ul>
  <p id="0CjR"><strong>4.4. X-Content-Type-Options</strong></p>
  <p id="KbdL">Защита от MIME-тип-путаницы (MIME sniffing).</p>
  <p id="QQtS"><strong>Единственное допустимое значение:</strong> <code>nosniff</code>. Если отсутствует — браузер может выполнить script, даже если тип указан как текст.</p>
  <p id="itT6"><strong>4.5. X-XSS-Protection</strong></p>
  <p id="zy81">Старый заголовок для защиты от XSS (в основном для старых браузеров).</p>
  <p id="aqKs"><strong>Значения:</strong></p>
  <ul id="x8MK">
    <li id="Ob0H"><code>1; mode=block</code> — блокировать страницу при обнаружении XSS.</li>
    <li id="Zvbk"><code>1</code> — попытаться очистить (менее безопасно).</li>
    <li id="SMaU"><code>0</code> — отключено.</li>
  </ul>
  <p id="kdmG">В современных браузерах во многом заменён CSP, но его наличие всё же полезно.</p>
  <p id="A9T7"><strong>4.6. Referrer-Policy</strong></p>
  <p id="8xz5">Контролирует передачу referer при переходе по ссылкам.</p>
  <p id="3gks"><strong>Настройки:</strong></p>
  <ul id="zFcD">
    <li id="uoMB"><code>no-referrer</code> — вообще не передавать.</li>
    <li id="wrvk"><code>strict-origin</code> — передавать только схему и домен (при HTTPS→HTTPS).</li>
    <li id="i6jx"><code>unsafe-url</code> — всегда передавать полный URL (опасно, утечка параметров).</li>
  </ul>
  <p id="NEbS"><strong>4.7. Feature-Policy / Permissions-Policy</strong></p>
  <p id="4bsS">Ограничивает доступ к функциям браузера (камера, микрофон, геолокация). Может содержать информацию о политиках организации.</p>
  <p id="03ra"><strong>Этап 5: Заголовки, указывающие на CDN, прокси, кэширование</strong></p>
  <p id="ZXZD">Эти заголовки помогают понять, скрыт ли реальный IP сервера за CDN, используется ли балансировка, кэширование.</p>
  <p id="VjmM"><strong>Cloudflare:</strong></p>
  <ul id="lXzN">
    <li id="OvbA"><code>CF-Cache-Status</code> (HIT, MISS, EXPIRED, DYNAMIC).</li>
    <li id="wYMV"><code>CF-RAY</code>.</li>
    <li id="nm2j"><code>CF-Connection-Connecting</code>.</li>
  </ul>
  <p id="YgVj"><strong>Akamai:</strong></p>
  <ul id="xNfe">
    <li id="5SDk"><code>X-Akamai-Transformed</code>, <code>X-Akamai-Cache-Status</code>.</li>
  </ul>
  <p id="gH5T"><strong>AWS CloudFront:</strong></p>
  <ul id="kovb">
    <li id="zz51"><code>X-Amz-Cf-Id</code>, <code>X-Cache</code> (Hit from cloudfront).</li>
  </ul>
  <p id="dXlF"><strong>Varnish:</strong></p>
  <ul id="Hh6q">
    <li id="ZYin"><code>X-Varnish</code>, <code>X-Cache</code> (HIT, MISS).</li>
  </ul>
  <p id="jbhK"><strong>Этап 6: Анализ заголовков на предмет утечки внутренней информации</strong></p>
  <p id="Qhkq">Некоторые заголовки могут раскрывать внутреннюю инфраструктуру:</p>
  <ul id="d1DQ">
    <li id="Ahmz"><code>X-Backend-Server</code> — имя внутреннего сервера.</li>
    <li id="Psm6"><code>X-Debug-Token</code>, <code>X-Debug-Token-Link</code> — утечка отладочной информации.</li>
    <li id="wzCw"><code>X-Server-Name</code> — имя сервера внутри сети.</li>
    <li id="2KfV"><code>X-Forwarded-For</code> (иногда) — реальный IP клиента, но может содержать IP прокси.</li>
    <li id="bfph"><code>Via</code> — прокси-серверы (например, <code>Via: 1.1 varnish</code>).</li>
  </ul>
  <p id="wJja"><strong>Если увидели <code>X-Backend-Server: web01.internal.company.com</code> — это очень ценно: можно узнать внутреннюю структуру и возможные имена хостов.</strong></p>
  <p id="0sIS"><strong>Этап 7: Инструменты для массового сбора заголовков</strong></p>
  <p id="Ns8z"><strong>Httpx (от ProjectDiscovery)</strong> — быстрый HTTP-клиент, собирает заголовки, извлекает технологии, поддерживает вывод в JSON.</p>
  <pre id="e9ZG">httpx -u https://example.com -sc -title -tech-detect -json -o headers.json</pre>
  <p id="dKHg"><strong>HTTPX (Python-библиотека) — позволяет автоматизировать запросы:</strong></p>
  <pre id="XmTb">import httpx

def get_headers(url):
    try:
        response = httpx.get(url, follow_redirects=True, timeout=10)
        headers = dict(response.headers)
        # дополнительные данные
        headers[&#x27;_status_code&#x27;] = response.status_code
        headers[&#x27;_url&#x27;] = str(response.url)
        return headers
    except Exception as e:
        return {&#x27;_error&#x27;: str(e)}

# Пример для одного сайта
print(get_headers(&#x27;https://example.com&#x27;))</pre>
  <p id="mRGD"><strong>Этап 8: Автоматический анализ с помощью <a href="https://securityheaders.com/" target="_blank">SecurityHeaders.com</a> API</strong></p>
  <p id="wPSD"><a href="https://securityheaders.com/" target="_blank">SecurityHeaders.com</a> предоставляет API для оценки заголовков.</p>
  <pre id="qad3">curl https://securityheaders.com/?q=https://example.com&amp;hide=on</pre>
  <p id="gGpU">Ответ — HTML, но можно парсить. Оценка от A+ до F (рекомендуется A).</p>
  <p id="hECp"><strong>Пример из утилиты:</strong> можно использовать <code>curl</code> и <code>grep</code> для извлечения рейтинга.</p>
  <p id="imXk"><strong>Этап 9: Оценка качества и потенциальных уязвимостей по заголовкам</strong></p>
  <p id="lDeM"><strong>Недостатки в заголовках безопасности могут указывать на слабую защиту:</strong></p>
  <ul id="7NbQ">
    <li id="tJDo">Нет HSTS → риск с stripping (особенно для сайтов с логинами).</li>
    <li id="9i7U">Нет CSP → уязвимость XSS.</li>
    <li id="nIFu">Нет X-Frame-Options → риск кликджекинга.</li>
    <li id="Q3Tu">Наличие <code>X-Powered-By</code> с точной версией → может помочь злоумышленнику (но не является уязвимостью).</li>
  </ul>
  <p id="k0ft"><strong>Практический пример: разбор заголовков сайта с логином</strong></p>
  <p id="XEN7"><em>Задача:</em> проверить защищённость сайта <code>login.target.com</code> по заголовкам.</p>
  <ol id="XlhH">
    <li id="ht1N"><strong>Запрос заголовков:</strong>textcurl -I https://login.target.comОтвет:textHTTP/2 200 server: nginx/1.22.0 x-powered-by: PHP/7.4.33 set-cookie: PHPSESSID=abc123; path=/; HttpOnly; Secure strict-transport-security: max-age=0 x-frame-options: DENY x-content-type-options: nosniff x-xss-protection: 1; mode=block content-security-policy: default-src &#x27;self&#x27;</li>
    <li id="mYpX"><strong>Анализ:</strong></li>
    <ul id="NsrE">
      <li id="RRLK"><code>server: nginx/1.22.0</code> — версия Nginx актуальна на момент проверки.</li>
      <li id="Z6zv"><code>x-powered-by: PHP/7.4.33</code> — PHP 7.4.33 EOL? Проверить (на момент написания 7.4 достиг EOL, могут быть уязвимости).</li>
      <li id="Nv5J"><code>PHPSESSID</code> с <code>HttpOnly; Secure</code> — хорошо.</li>
      <li id="xq9B"><code>strict-transport-security: max-age=0</code> — HSTS выключен! Очень плохо для страницы логина.</li>
      <li id="GpEW">CSP: <code>default-src &#x27;self&#x27;</code> — хорошо, но лучше конкретные директивы.</li>
    </ul>
    <li id="Pfek"><strong>Рекомендации:</strong></li>
    <ul id="6md8">
      <li id="c8hx">Включить HSTS с долгим max-age.</li>
      <li id="2H4x">Обновить PHP до поддерживаемой версии.</li>
      <li id="2VGT">Расширить CSP, запретив inline-скрипты.</li>
    </ul>
  </ol>
  <p id="s28z"><strong>Этап 10: Мониторинг изменений заголовков</strong></p>
  <p id="viQH">Заголовки могут меняться со временем: администраторы могут включить HSTS, изменить версии ПО. Мониторинг изменений помогает отслеживать обновления безопасности.</p>
  <p id="H6aD"><strong>Как организовать мониторинг:</strong></p>
  <ul id="izbo">
    <li id="bRGL">Периодически запрашивать заголовки (раз в день, раз в неделю).</li>
    <li id="XZcX">Сравнивать хэши ответов (или конкретных заголовков).</li>
    <li id="yj8x">При изменении — уведомление.</li>
  </ul>
  <p id="Bolf"><strong>Python-скрипт мониторинга:</strong></p>
  <pre id="TkOU">import hashlib
import httpx
import time

def fetch_headers_hash(url):
    response = httpx.get(url, follow_redirects=True)
    # конкатенируем все заголовки в строку
    headers_str = str(sorted(response.headers.items()))
    return hashlib.sha256(headers_str.encode()).hexdigest()

def monitor(url, interval_hours=24):
    last_hash = None
    while True:
        current_hash = fetch_headers_hash(url)
        if last_hash is None:
            last_hash = current_hash
            print(f&quot;Initial hash: {current_hash}&quot;)
        elif current_hash != last_hash:
            print(f&quot;Headers changed for {url}!&quot;)
            # здесь можно отправить уведомление
            last_hash = current_hash
        time.sleep(interval_hours * 3600)</pre>
  <p id="bp9o"><strong>Этические и правовые ограничения</strong></p>
  <ul id="rxU1">
    <li id="K8mr">Сбор заголовков с помощью curl/httpx — это обычный HTTP-запрос, который не нарушает закон (аналогично посещению сайта браузером).</li>
    <li id="UXPF">Использование <a href="https://securityheaders.com/" target="_blank">SecurityHeaders.com</a> и аналогичных сервисов также легально.</li>
    <li id="Hljj">Массовый сбор заголовков (тысячи доменов) может привести к блокировке IP, если сайт расценит это как сканирование. Используйте разумные интервалы.</li>
    <li id="ncjG">Анализ заголовков безопасности не даёт права атаковать сайт. Информация используется для оценки защищённости (в рамках разрешённого тестирования).</li>
  </ul>
  <p id="23Uj">HTTP-заголовки — богатый источник информации при пассивном техническом OSINT. Идентификационные заголовки (<code>Server</code>, <code>X-Powered-By</code>) раскрывают версии ПО, что позволяет искать уязвимости. Заголовки cookie указывают на используемые технологии и фреймворки. Заголовки безопасности (CSP, HSTS, X-Frame-Options) оценивают защищённость веб-приложения. Заголовки кэширования и прокси помогают определить CDN и скрыть реальный IP. Комбинация этих данных с результатами других статей цикла даёт наиболее полную картину инфраструктуры цели.</p>

]]></content:encoded></item></channel></rss>