Pull to refresh
0

OSINT в Telegram

Reading time15 min
Views41K


Протокол Telegram известен своей доступностью и открытостью. У него есть множество публичных реализаций: tdlib/td, rubenlagus/TelegramApi, vysheng/tg, LonamiWebs/Telethon и другие. Однако, даже имея в распоряжении столь богатый инструментарий и объемную документацию (https://core.telegram.org/api), решить прикладную задачу, собрав из многообразия методов API нужную цепочку – не так-то просто. Сможет, например, “неподготовленный ум“ сходу догадаться, как решить прикладную задачу а-ля “поиск по номеру в Telegram“? — Скорее всего, придется потратить какое-то время на изучение API.


Официальный клиент Telegram содержит в себе массу API-цепочек, реализующих определенные пользовательские сценарии. Если подумать, взаимодействие на основе сценариев — наиболее удобный и предпочтительный способ, поэтому мы решили пойти по пути упрощения взаимодействия с Telegram на основе реализации библиотеки сценариев. Так как наша деятельность тесно связана с направлением OSINT, то в первую очередь мы решили реализовать ряд OSINT-сценариев, применимых в сети Telegram, о которых и хотим рассказать в этой статье.


Для решения задач OSINT мы еще давно начали работу над собственным клиентом для сети Telegram, который в последствии трансформировался в расширяемую библиотеку сценариев — telegram-osint-lib.


Почему пришлось делать собственный клиент?


Мы разрабатываем программные системы для сбора данных из открытых источников. Об одном из наших сервисов — Nuga — мы уже как-то рассказывали ранее.


Здесь и далее под “клиентом” подразумевается не графическое приложение, а “бот” (не путать с внутренним функционалом Telegram), управляемый из консоли, который решает определенную прикладную задачу в рамках сети Telegram с помощью предоставляемого API.


На момент первичной реализации клиента (~конец 2018 года) сторонние библиотеки-клиенты Telegram, которые мы рассматривали, обновлялись нерегулярно и не соответствовали требованиям из документации по протоколу, которая обновлялась с большой задержкой (полгода и более), как и официальные клиенты.


Часто в новоиспеченных версиях официальных клиентов можно было наблюдать кардинальные отличия от официальной документации, что сеяло зерна конспирологических сомнений. Например, API Layer 105 (выпущенный с огромным скачком от последней версии 23) наконец-то рассказал нам об опциональных полях и всех недостающих типах данных, устранив тем самым все подозрения на гипотетические закладки. Сейчас с этим стало лучше, документация обновлена, хотя некоторые детали все равно приходится определять экспериментально.


Сценарийная направленность


Занимаясь достаточно долгое время развитием собственного клиента, клиент оброс множеством оберток над группами API-вызовов. Стало понятно, что как простой набор API вызовов к Telegram он нас не особо интересует — в пределах сети Telegram гораздо интереснее выполнять комплексные операции, задействующие сразу множество API-вызовов. Таким образом был осуществлен переход от API-направленности к сценарийной направленности клиента.


Сценарий в telegram-osint-lib – это “черный ящик”, реализованный в виде последовательности API-вызовов, позволяющей достичь определенной конечной цели (output) на основе входных параметров (input). Аргументы представляют из себя понятные сущности окружающего мира (например, ключевое слово для поиска). В результате использования аргументов внутри черного ящика на выходе получается запрошенная информация (например, сообщения с указанным ключевым словом). Вся рутина по взаимодействию с API Telegram при этом инкапсулирована в реализации сценария. Сценарии могут сочетаться друг с другом, формируя более комплексные сценарии.


Концепция сценария была разработана в процессе решения задач внутри компании, однако, схожие понятия встречаются много где, например, в литературе по анализу требований — Scenario-based modeling and its applications — т.е идея “scenario-based“ сама по себе, конечно же, не нова.


Детали реализации


Библиотека telegram-osint-lib реализована по асинхронной модели и рассчитана на поддержку нескольких одновременных соединений (пример). Изначально при реализации мы следовали следующим принципам:


  • fail fast: при работе с проприетарным протоколом (пусть и имеющим более-менее открытую документацию) необходимо реагировать на изменения оперативно

  • conformity: библиотечная реализация максимально соответствует существующим клиентам и учитывает ограничения, заложенные в протокол

  • testability: код должен быть доступен для тестирования, а именно: быть декомпозирован, иметь низкую связность


На этапе дизайна архитектуры библиотеки, были выделены следующие уровни абстракции (от низкого к высокому):


  1. Уровень Telegram API (TL nodes);

  2. Уровень клиента, содержащего набор нужных методов API для выполнения класса операций

  3. Уровень сценария, комбинируемого с соседями по уровню

  4. Уровень интерфейса пользователя (скрипт, вызывающий сценарии)


Так как клиент реализован по асинхронной модели, каждый вызов возвращает результат не напрямую, а через callback. Такой подход к реализации позволяет обрабатывать асинхронные ответы (что предусмотрено протоколом Telegram) и держать в одном потоке множество соединений.


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


Как известно, Telegram протокол описан в виде схемы на языке TL. Однако, на той же странице, мы можем получить схему и в формате JSON (которая на практике оказывается более применимой). В схеме два основных блока: constructors и methods. Первый описывает структуры данных, которые принимает на вход или возвращает Telegram сервер, а второй — описание методов: спецификацию типов входных параметров и тип ответа.


Несмотря на то, что сейчас протокол на сайте регулярно обновляется (в процессе написания статьи вышла новая версия TL-Schema 108, а за ней и 109), наблюдать за дельтой изменений между версиями по прежнему необходимо, для чего используется небольшой сниппет, принимающий на вход два json файла и выдающий на выходе конструкторы/методы, которые есть во втором файле, но нет в первом. На текущий момент в официальном API описано более 1100 дескрипторов (конструкторов/методов). Пользуясь такой json схемой нетрудно составить или декодировать любое сообщение.


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


OSINT на примере некоторых сценариев


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


Пойдем от простого к сложному и попробуем разобрать некоторые из существующих сценариев на примере реальных кейсов. Для более удобного взаимодействия со сценариями библиотека telegram-osint-lib была завернута в Docker:


docker build -t telegram-osint-lib . 
docker run -d -t --name tg-osint-lib telegram-osint-lib

Преамбула: Генерируем бота-клиента


Перед тем как запустить содержимое Docker-контейнера и получить о пользователе первые данные, нам понадобится “зайти” в сеть Telegram. Для входа в сеть библиотека использует ботов (о которых уже было упомянуто выше), от имени которых производятся все действия.


Для генерации бота нам потребуется сценарий регистрации, реализующий связку auth.sendCodeauth.signInauth.signUp:


docker exec -i tg-osint-lib php examples/registration.php
Number: 790612***31
SMS code: 123123

На выходе получаем бота, готового к задачам OpenSource Intelligence:


AuthKey: 790612***31:aabbccdd...

Этот ключ(AuthKey) будет использоваться во всех дальнейших примерах следующим образом:


docker exec --env BOT=... -i tg-osint-lib php ...

Ниже в примерах, для краткости, бот будет указываться так: --env BOT=...


Собираем сведения о владельце номера


Начать мы решили с простого сценария — поиска пользователя Telegram по номеру телефона. Данный сценарий довольно очевидный и доступный для рядовых пользователей, однако его массовое применение несколько затруднительно “из коробки“.


Предположим, у вас есть ряд номеров, о владельцах которых вы хотели бы собрать информацию минимальными усилиями. Сеть Telegram в этом случае позволит собрать следующую информацию о телефонном номере:


  1. Ник (который многие пользователей могут использовать и в других местах)

  2. Фото

  3. Общие чаты

  4. Имя/Фамилия

  5. “О себе”

  6. Последнее время пребывания в Telegram

  7. Предпочтительный язык


Этой информации уже достаточно для составления начального профиля владельца номера. Мы будем использовать сценарий из telegram-osint-lib, который будет получать на входе список телефонных номеров, пробегать по ним и запрашивать для каждого номера соответствующий аккаунт на сервере Telegram. Если аккаунт есть, мы получаем его данные. Если же аккаунт не найден, сохраняем номер телефона в отдельный список, которые будет обрабатываться в дальнейшем вручную.


Запускаем сценарий на группе номеров:


docker exec --env BOT=... -i tg-osint-lib php examples/parseNumbers.php 7985****294,7985****977,7986****777,7986****252,7988****417,7999****169,7999****869,7999****053,7999****364,7999****916,7999****475,7999****959,7985****025,7985****343,7989****207,7916****668,7926****802 > numbersInfo.txt

Всю основную работу в недрах библиотеки выполняет метод InfoClient::getInfoByPhone(), использующий связку API вызовов import_contacts->get_user_full->delete_contacts->get_user_full. А обработка результатов происходит в функции обратного вызова, которой в параметрах передаётся модель с данными пользователя. Если у пользователя установлено фото профиля, изображение будет загружено в папку со скриптом примера, а в поле Photo будет указано имя файла.




Следим за присутствием пользователя


Развивая предыдущий сценарий, нетрудно догадаться, что запрашивать информацию об интересующем пользователе можно периодически и каждый раз сохранять его статус присутствия в сети Telegram. Это позволит спустя некоторое время накопить достаточно данных, чтобы составить карту активности.


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


Запускаем уже другой сценарий на группе номеров и наблюдаем за ними некоторое время:


docker exec --env BOT=... -i tg-osint-lib php examples/monitorNumbers.php 97155******9,...,798*****777 presence_map.txt

На выходе сценария мы получим ASCII-карту (где “+“ — состояние online в секунду времени) взаимоприсутствия интересующих пользователей в сети. Из карты видно, что вероятность активного общения между пользователями 2,4,5,9 более вероятна, чем между всеми остальными:




Откуда HackerNews черпает свои новости?


В Telegram популярны каналы новостного типа — рассылающие анонсы новостей, статей и прочих тематических постов. Но многие ли обращают внимание, откуда популярные каналы черпают информацию?


Возьмём относительно популярный канал HackerNews. Нас интересует список ресурсов, новости из которых чаще всего постились в группе за последний месяц.


В библиотеке реализовано два похожих метода для работы с сообщениями групп: InfoClient::getChannelLinks() и InfoClient::getChannelMessages(). Работают они практически одинаково, за исключением того, что первый метод фильтрует сообщения и отбирает только те, в которых были размещены ссылки.


Попробуем собрать все ссылки за последние несколько месяцев с целью определить основные источники информации канала:


docker exec --env BOT=... -i tg-osint-lib php examples/parseChannelLinks.php https://t.me/HNews "2019-12-01 00:00:00"

Спустя некоторое время сценарий соберет статистику по доменам и на выходе мы увидим примерно следующие результаты:




Итого, получили источники по убыванию частотности:


  1. habr.com (45%)

  2. xakep.ru (44%)

  3. threatpost.com (11%)

  4. остальное (<1%)


Теперь тот, кто подписан на HackerNews, Xakep.ru и Habrahabr может задуматься, а не подписан ли он на что-то лишнее?


Самый большой болтун


Кроме анализа информационных источников группы, немалый интерес может представлять выборка самых активных участников тематической группы. Не секрет, что 20% участников группы производят 80% активности этой группы. Получив такую информацию, можно определить наиболее приоритетных для группы людей и наладить контакт именно с ними.


Определять топ самых активных участников проще всего на промежутке времени или по числу последних сообщений (например, по последним 1000 сообщений). Собрать информацию о частоте сообщений участников группы нам поможет метод API messages.getHistory, используемый сценарием с другим целевым предназначением (сбор сообщений в группе), но кастомизируемый внешними инструментами. Запускаем очередной сценарий на одной из популярных групп с пост-фильтрацией средствами командной строки:


docker exec --env BOT=... -i tg-osint-lib php parseGroupMessages.php https://t.me/vityapelevin -- 1570207168 1580207168 --info \
  head -n 2000 | \
  ggrep -oP 'from [a-zA-Z0-9_]+ at' | \
  sort | uniq -c | sort -r -n -k1 | awk '{print $1 " " $3 }' | \
  head -n10

Что на выходе позволяет получить самых больших болтунов группы:


355 289336351
237 710806664
226 Yuliya04
216 735896305
187 Retrovertigodor
187 971662085
175 Mahmud_Abas
141 VwVwVoid
94 nikol_pelevina
85 kotenok_gaff

Общие интересы


Как можно расширить информацию о человеке, имея на руках его сетевой профиль, связанный с номером? Одной из примечательных функций Telegram является групповое тематическое общение. Все существующие чаты можно условно классифицировать по интересам (которых суммарно будет несколько сотен) и из каждого класса интересов выбрать несколько самых популярных чатов для поиска в них интересующего пользователя.


Ядром сценария, определяющего “карту интересов” пользователя, будет API-метод get_common_chats, возвращающий список чатов, которые являются общими для бота и пользователя из наших контактов.
Итоговый алгоритм определения интересов пользователя будет следующим:


  1. Добавить пользователя к себе в контакты

  2. Подписать бота на множество популярных чатов, используя join_channel

  3. Получить информацию об общих с пользователем чатах, с помощью get_common_chats

  4. Сопоставить общие чаты с классами интересов


Для исполнения этого алгоритма на конкретном номере будет использоваться очередной сценарий, использующий внутри себя другой, уже задействованный ранее, сценарий поиска информации по номеру телефона:


docker exec --env BOT=... -i tg-osint-lib php examples/commonChats.php 7926****802

Изучая исходник примененного сценария можно обратить внимание на то, как происходит комбинирование простых сценариев в более сложные:


public function getCommonChats(?callable $callback = null)
{
    $client = new UserContactsScenario([$this->phone], function (UserInfoModel $user) use ($callback) {
        $this->infoClient->getCommonChats($user->id, $user->accessHash, 100, 0, function (AnonymousMessage $message) use ($callback) {
            if (!Chats::isIt($message)) return;
            $updates = new Chats($message);

            foreach ($updates->getChats() as $chat) {
                $this->commonChats[] = strtolower($chat->username);
            }
            ...
        });
    });
    $client->startActions(false);
}

По полученным общим чатам “вычисляются” классы интересов, к которым они относятся, подсчитывается количество общих чатов в каждой из категорий, и на выходе сценария получаем список интересов пользователя, упорядоченный по количеству чатов, а значит можем сделать вывод, какие из интересов наиболее предпочтительны пользователем:




Извлечение сообщений пользователя из публичных чатов и каналов


Продолжаем дальше искать новые источники открытой информации в Telegram. Какую информацию о пользователе мы смогли извлечь на данный момент:


  1. Номер → Профиль пользователя

  2. Профиль пользователя → Общие каналы с пользователем


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


   3.  Общие каналы с пользователем → Сообщения пользователя в канале


Для этого нам поможет сценарий извлечения сообщений группы. В его основе лежит API-метод messages.getHistory. Для примера, выгрузим последние сообщения уже известного нам пользователя a_averyanova_m:


docker exec --env BOT=... -i tg-osint-lib php parseGroupMessages.php https://t.me/phuketrusa a_averyanova_m --info | head -n10 
30.01.2020 13:26:17 | parseGroupMessages.php: starting group resolver for username: phuketrusa
30.01.2020 13:26:18 | TelegramOSINT\Scenario\GroupMessagesScenario: resolved user a_averyanova_m to 272425703
30.01.2020 13:26:19 | TelegramOSINT\Scenario\GroupMessagesScenario: got message 'Учиться мопед водить ?))))  \\ Ну приеду , проверю информацию )' from a_averyanova_m at 2020-01-30 12:25:48
30.01.2020 13:26:19 | TelegramOSINT\Scenario\GroupMessagesScenario: loading more messages, starting with 26451
30.01.2020 13:26:20 | TelegramOSINT\Scenario\GroupMessagesScenario: loading more messages, starting with 26332
30.01.2020 13:26:21 | TelegramOSINT\Scenario\GroupMessagesScenario: loading more messages, starting with 26219
30.01.2020 13:26:22 | TelegramOSINT\Scenario\GroupMessagesScenario: got message 'Я так поняла , тут вариантов не много, или ты катаешься без прав (наши тут с кат.авто не прокатят) и лишаешься страховки (если вдруг что)+платить штраф 500-1000бат, или идёшь и получаешь права в Тае, по времени это пару дней (когда как) и по деньгам явно дешевле, чем на лапу давать) мы по прилету , будем делать их на месте' from a_averyanova_m at 2020-01-29 14:38:40
30.01.2020 13:26:22 | TelegramOSINT\Scenario\GroupMessagesScenario: loading more messages, starting with 26099
30.01.2020 13:26:22 | TelegramOSINT\Scenario\GroupMessagesScenario: got message 'Спасибо' from a_averyanova_m at 2020-01-29 10:55:06
30.01.2020 13:26:22 | TelegramOSINT\Scenario\GroupMessagesScenario: got message 'Добрый день , еду на 2 месяца в Тай, кто какую страховку делал , при условии того, что буду ездить на байке без открытой категории. \\ И ещё , слышала, что можно открыть категорию ( получить их права) на месте , кто этим занимался, платить каждый раз по 1000бат тоже не самый классный вариант' from a_averyanova_m at 2020-01-29 10:09:10

Помимо возможностей пост-фильтрации, есть возможность выборки по диапазону дат. Пример фильтра (~с 04.10.2019 до 27.01.2020):


docker exec --env BOT=... -i tg-osint-lib php parseGroupMessages.php https://t.me/vityapelevin -- 1570207168 1580207168 --info | grep сновидения

28.01.2020 10:45:22 | TelegramOSINT\Scenario\GroupMessagesScenario: got message 'Да, там про всякие сверхвозможности, в искусстве сновидения вроде' from 735896305 at 2020-01-27 21:02:01

На этом этапе проведем небольшую ретроспективу собранных данных по одному из исходных номеров:


  1. Первым шагом в разделе “Собираем сведения о владельце номера“ мы сопоставили профиль пользователя a_averyanova_m с реальным человеком, владеющим номером 7926****802

  2. Далее в разделе “Общие интересы“ определили возможные интересы человека на основе анализа общих групп (путешествия)

  3. И, наконец, в этом разделе извлекли публичную переписку в одной из обнаруженных общих групп с ботом


Вместе вся эта информация позволяет сформировать достаточно полное представление об исходном владельце номера и даже о его планах на будущее.


Гео-разведка


Использование гео-позиции открывает интересные возможности для сбора информации, однако сама по себе гео-разведка является одним из тех направлений OSINT, разрушительный эффект от которых очевиден разработчикам по умолчанию и который они всяческими методами пытаются снижать. Разработчики Telegram здесь не стали исключением и так же постарались минимизировать возможные утечки, связанные с гео-локацией. В итоге весь потенциал направления сузился до нескольких API-методов: geochats.getLocated и contacts.getLocated, но и их в некоторых случаях может хватить для извлечения дополнительной информации о пользователе — например, где он чаще всего появляется в городе?


Завершающий сценарий, который мы рассмотрим в этой статье, позволяет определять потенциальные “места обитания” пользователя на определенной локации на основе поля гео-точек. В основе сценария будет лежать API-метод contacts.getLocated, который возвращает гео-чаты и контакты, находящиеся в определенном радиусе (эмпирическая оценка ~1 километр) от заданной гео-точки. Метод возвращает структуру Updates, благодаря которой мы можем реализовать мониторинг изменений в отслеживаемой группе пользователей.


Запускаем сценарий на поле из двух точек для конкретного пользователя:





docker exec --env BOT=... -i tg-osint-lib php geoSearch.php 55.753930,37.615714,55.756390,37.661931 b00k1ng 30 --info
...
29.01.2020 16:00:06 | TelegramOSINT\Scenario\GeoSearchScenario: found group 'Эдвард юил' near (55.753930, 37.615714)
29.01.2020 16:00:06 | TelegramOSINT\Scenario\GroupMembersScenario: searching chat 1404414249 participants for b00k1ng
29.01.2020 16:00:06 | TelegramOSINT\Scenario\GeoSearchScenario: found group 'Френдовская' near (55.753930, 37.615714)
29.01.2020 16:00:06 | TelegramOSINT\Scenario\GroupMembersScenario: searching chat 1404180655 participants for b00k1ng
29.01.2020 16:00:06 | TelegramOSINT\Scenario\GroupMembersScenario: chat 1211826903 contains user 883904218 with username b00k1ng

На основе результата можно делать предположения о том, в каких локациях пользователь гипотетически может появляться, что позволит сделать определенные выводы о его интересах и деятельности в реальной жизни.


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


$groupHandler = function (GeoChannelModel $model) use (&$generator, &$finders, $username) {
    $membersFinder = new GroupMembersScenario(
        $model->getGroupId(),
        null,
        $generator,
        100,
        $username
    );

    $membersFinder->startActions(false);
    $finders[] = $membersFinder;
};

$search = new GeoSearchScenario($points, $groupHandler, $generator, $limit);
$search->startActions();

Заключение


В этой статье на примере библиотеки сценариев telegram-osint-lib мы разобрали ряд сценариев OSINT-направленности в сети Telegram. Как можно было заметить, разобранные “утечки“ OSINT являются издержками исходных бизнес-требований к функционалу, а потому не могут быть устранены легко. Наверное, это одна из тех причин, по которой направление разведки по открытым источникам будет существовать постоянно в том или ином виде — это что-то вроде шума в электронных цепях, к которому все уже давно привыкли: ослабить эффект возможно, но устранить полностью экономически невыгодно.


Мы шли от более простых сценариев к более сложным, встраивая и комбинируя сценарии друг с другом. Структура библиотеки позволяет легко комбинировать как методы, так и сценарии между собой в нужную цепочку вызовов, получая на выходе необходимый сценарий. А асинхронный механизм работы уменьшает зависимость различных методов друг от друга и позволяет реализовать несколько одновременных соединений, позволяя масштабировать сценарии.


Мы рассмотрели ряд примеров, работающих автономно, без какого-либо “состояния” (State), однако добавление к сценариям состояния (до чего у нас пока не дошли руки), позволит вытворять еще более интересные штуки в сфере OSINT. Например обнаруженная в августе 2018 года “уязвимость“, позволяющая определять номер телефона по никнейму пользователя была не уязвимостью, а ничем иным, как OSINT-сценарием с наличием состояния: кто-то начал массово искать пользователей по номеру телефона, собирая базу данных (State) вида “пользователь->номер“, исходя из структуры которой определение телефона по никнейму являлось тривиальной операцией.


В завершение хотелось бы отметить, что Telegram API непрерывно развивается, открывая с каждым API Layer все новые возможности не только для OSINT-исследователя, поэтому любой желающий может присоединиться к разработке библиотеки сценариев и пополнять её новыми сценариями, скрывающими хитросплетения API-вызовов и решающими любые прикладные задачи в сети Telegram.

Tags:
Hubs:
+9
Comments4

Articles

Change theme settings

Information

Website
postuf.com
Registered
Founded
Employees
31–50 employees
Location
О.А.Э.