Все привыкли считать телеграм надежной и безопасной средой для передачи сообщений любого сорта. Однако, под капотом у него крутится совершенно обычная комбинация а- и симметричного шифрований, а это ведь совсем не интересно. Да и в конце концов, зачем вообще явно доверять свои сообщения третьей стороне?
КДПВ за авторством Antonio Prohias
TL;DR — изобретаем приватный скрытый канал через блокирования пользователями друг друга.


Скрытый канал


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


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


Цветок в окне


Цветочно-оконные каналы телеграма


Для организации своего скрытого канала по такому примеру концептуально нужны лишь две вещи: окно и цветок. Окно описывает доступный для изменения и определения состояния объект, а цветок — возможные значения и метод их изменения.


Что же такого может изменять некто Алиса в телеграме, что доступно для просмотра некоему Бобу? Да много чего: аватарки, имена пользователя, время последнего посещения и многое другое. Однако, обычно они доступны всем сразу, что ограничивает приватность возможного диалога — любой, обладающий знанием метода передачи, сможет прослушивать посылаемые сообщения. Как ни странно, это ограничение можно обойти без использования криптографии.


В черном-черном списке


У каждого пользователя есть свой уникальный черный список, и, если читатель бывал в нем хоть раз, то должен был заметить, изменение даты последнего посещения обидчика на "last seen a long time ago" наравне с пустым аватаром. На самом деле, пользователь мог быть в сети хоть секунду назад и сменить сотню котиков в своем профиле, но Telegram API просто не возвращает приложению эти данные. Таким образом он защищает вашу личную жизнь от нежеланных лиц, взамен давая им понять, что они заблокированы.


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


Организовываем биты


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


Сам алгоритм обмена на каждом такте выглядит при этом так:


  • A проверяет отправляемый бит и, если требуется послать сигнал, то, в зависимости от его значения:
    • A -> T: заблокировать Б;
    • A -> T: разблокировать Б.
  • Б получает бит:
    • Б -> T: запрос пользователя A;
    • T -> Б: доступная Б информация об А;
    • Б: проверяет, есть ли в полученной информации статус:
      • Б: если есть -> он не заблокирован и получен 0
      • Б: если нет -> он заблокирован и получен 1

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


Кодируем сообщения


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


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


Поэтому мы, находясь в 21 веке, будем кодировать тексты одним из самых эффективных доступных телеграфистам столетие назад методов — кодом Бодо. Точнее, его последней вариацией ITA-2 за авторством Мюррея, чтобы совершать меньше вызовов API на часто встречаемых символах. Можно пожаловаться на отсутствие русского языка, но в моем представлении транслит является более простым выходом, чем жертвоприношение дополнительных символов всяким Ю и Щ.


Осталось лишь определить начало и конец каждого сеанса передачи данных, чтобы принимающая сторона среди большого потока могла выделить передаваемые ей сообщения. До начала передачи Боб находится либо в черном списке, либо за его пределами. Тогда Алиса сигнализирует о начале передачи, сменив это состояние на один такт на противоположное. В конце же передачи она просто разблокирует Боба. Он продолжает получать нулевые символы пока не накопит достаточное количество, чтобы надежно констатировать конец передачи.


На временной шкале


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


Высокие технологии


После нескольких часов попыток использовать официальную библиотеку для взаимодействия с API, я плюнул и написал все необходимое на питоне с использованием более человечного Telethon'а. Автор даже представляет API в синхронном стиле, что почему-то стало жуткой редкостью. Кодирование сообщений с помощью ITA-2 пришлось писать самому на коленке, так как ничего готового толком не нашел.


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


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


Пример использования


Для использования надо получить свой api_id и api_hash у телеграма по этой инструкции и выставить полученные значения в файле 'covertele.py'. В аргументах командной строки указываются желаемое действие, свой идентификатор, идентификатор получателя, а также передаваемое сообщение, если он является отправителем. Выглядит все это как-то так:


У Алисы:                                    У Боба:

Enter your phone number: XXX        |       Enter your phone number: XXX
Enter auth code: YYY                |       Enter auth code: YYY
Started message transmission...     |       Listening for the message...
---++ ('O', '9')                    |       ---++ ('O', '9')
--+-+ ('H', '#')                    |       --+-+ ('H', '#')
+++++ (1, 1)                        |       +++++ (1, 1)
--++- ('N', ',')                    |       --++- ('N', ',')
--+-- (' ', ' ')                    |       --+-- (' ', ' ')
++-++ (0, 0)                        |       ++-++ (0, 0)
--+-+ ('H', '#')                    |       --+-+ ('H', '#')
-++-- ('I', '8')                    |       -++-- ('I', '8')
--+-- (' ', ' ')                    |       --+-- (' ', ' ')
--+++ ('M', '.')                    |       --+++ ('M', '.')
++--- ('A', '-')                    |       ++--- ('A', '-')
-+-+- ('R', "'")                    |       -+-+- ('R', "'")
++++- ('K', '(')                    |       ++++- ('K', '(')
+++++ (1, 1)                        |       +++++ (1, 1)
+-++- ('F', '!')                    |       +-++- ('F', '!')
--+++ ('M', '.')                    |       --+++ ('M', '.')
--+++ ('M', '.')                    |       --+++ ('M', '.')
Done, exiting...                    |       ----- ('', '')
                                    |       ----- ('', '')
                                    |       Automatically decoded: OH, HI MARK!..

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


Если кому-то захочется, можно также использовать простой предоставляемый API:


from covertele import TelegramBlockingAPI
from cochannel import CovertChannel

friend = input("Введи имя друга для передачи данных:")
# Для работы канала с телеграмом нужно создать сначала интерфейс взаимодействия
# По умолчанию, для авторизации используется стандартный ввод-вывод, за обходом читай исходный код
id = input("Введите свой идентификатор:")
api = TelegramBlockingAPI(id)

# С помощью созданного интерфейса создается непосредственно канал
friend = input("Enter your friend's id:")
channel = CovertChannel(api, friend)

# Дальше можно использовать channel.receive() и channel.send() для непосредственного обмена данными
channel.send("Bork, bork!")
print(channel.receive)

# Если нужно обрабатывать код на ошибки автоматически, можно использовать их аналоги с суффиксом _raw
codes = channel.receive_raw()
for code in codes:
check(code)
channel.send_raw([19, 24, 24, 13])

За пределами телеграма


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


P.S. Отдельная благодарность моей пассии за любовь кидать меня в ЧС.


-> GitHub

Комментарии (35)


  1. Ryav
    15.05.2019 16:17
    +2

    Так вот почему меня блокируют… :)
    Хорошая статья, которая в очередной раз подтверждает, что те, кому нужно, всегда организуют свой скрытый канал, а блокировки эти сами знаете зачем.


  1. Tangeman
    15.05.2019 17:34
    +1

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

    Прям таки «все»? Если это не секретный чат, то ничего особо безопасного там нет. Удобно — это да, в основном надежно, но явно не безопасно.


    1. Labunsky Автор
      15.05.2019 17:40

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


      1. Tangeman
        15.05.2019 18:23

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

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

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


        1. Labunsky Автор
          15.05.2019 18:33
          -1

          Алгоритмы открыты, а как они реализуются программно и крутятся на телефонах особо никто не проверяет. Да и даже если там все хорошо, сохрани кто чужую важную переписку, зашифрованную 30 лет назад надежным DES…


  1. worldmind
    15.05.2019 17:51

    Благо, медленный питон вряд ли станет помехой при побитовой передаче на таких скоростях.

    не такой уж он и медленный, тут думаю всё время на запросы к внешнему API уходит


    1. Labunsky Автор
      15.05.2019 17:57

      Ну так да, 99% времени уходит на сетевые запросы и сон при синхронизации. Потому и написал, что помехой не должна скорость быть. Просто подобные вещи обычно пишутся на чем-нибудь типа Си и плюсов, а там издержки в разы меньше


  1. namikiri
    15.05.2019 18:03
    +1

    Когда я думал, что IP over Telegram это извращение, я сильно заблуждался…


    1. Labunsky Автор
      15.05.2019 18:15

      Если есть желание — можно совместить, я же тут фактически описываю абстрактный физический канал, просто очень низкочастотный


  1. mafia8
    15.05.2019 19:59

    Дуров приехал в Москву, передал ФСБ ключи шифрования и уехал.
    ФСБ: Сейчас мы почитаем Навального. Включай. Что это? Какой-то винегрет символов, большое сообщение со строками одинаковой длины и потом разные сообщения, и все нечитаемые.
    Дуров: Это ровно то, что ввел пользователь, можете посмотреть исходный код. Например, он мог использовать бумажные шифроблокноты или шифровать на другом устройстве и вводить уже зашифрованный текст.


    1. Simplevolk
      16.05.2019 13:11

      Немедленно расшифровать! А если откажешься — заблокируем!


  1. lxsmkv
    16.05.2019 01:23

    Вспомнил Spy vs Spy — эх, игра была, часами напролет в нее резались.


  1. IGR2014
    16.05.2019 11:02

    «OH, HI MARK»

    Томми Вайсо дорвался и до программирования?!)


  1. BigBeaver
    16.05.2019 15:24

    Можно радикально повысить пропускную способность за счет увеличения числа подконтрольных аккаунтов.


    1. Labunsky Автор
      16.05.2019 15:33
      +1

      Верно, масштабируется по такому принципу не хуже, чем PCI Express. Разве что симки надо где-то для регистрации аккаунтов раздобыть


      1. BigBeaver
        16.05.2019 15:46

        Ну так там 2^N в худшем случае (если у вас 1 приемник и N передатчиков) и больше, если приемники также скоординировать. То есть, по 4 симки на человека (что не то чтобы проблема) даст уже неплохой канал. При чем, это можно реализовать поверх почти любого социального сервиса — хоть того же инстаграмма. То есть смотрите, вместо наращивания числа симок можно наращивать число сервисов на каждую симку. Для видео стриминга, конечно, не годится, но можно довольно бодрый канал поднять, в целом.

        Это еще и диверсификация рисков — если допустить, что злоумышленник каким-то образом контролирует (прослушивает) один из аккаунтов, это не дает особого профита.


        1. Labunsky Автор
          16.05.2019 15:53

          можно наращивать число сервисов на каждую симку
          Несомненно, я для телеграма сделал только потому, что он популярнее среди целевой аудитории, изначально предполагал использование всяких фейсбуков и вконтактов
          если у вас 1 приемник и N передатчиков
          Не советую пытаться использовать более одного передатчика на один применик — за флуд пробиванием пользователей обычно банят надолго (в случае телеги — на 23 часа)


          1. BigBeaver
            16.05.2019 16:16

            флуд пробиванием пользователей
            Что это такое? (я не особо пользуюсь телегой).


            1. Labunsky Автор
              16.05.2019 16:51

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


              1. BigBeaver
                16.05.2019 17:24

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


                1. Labunsky Автор
                  16.05.2019 17:32

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


                  1. BigBeaver
                    16.05.2019 18:41

                    Вопрос приоритетов.


                    1. Labunsky Автор
                      16.05.2019 19:02

                      Понятное дело, каждый делает как хочет, но исторически мы не просто так ушли от параллельных интерфейсов к последовательным


                      1. BigBeaver
                        16.05.2019 19:53

                        Если вы не можете повышать скорость канала, то неизбежно придется повышать разрядность передаваемых символов.


                        1. Labunsky Автор
                          16.05.2019 19:58

                          Чего нельзя, того нельзя. Всегда можно просто оставить все как есть и подождать, все-таки жили с телеграфом как-то, а тут даже выше скорость


                          1. BigBeaver
                            16.05.2019 20:08

                            Если с задачами справляется, то можно)
                            В принципе, можно ограничиться координацией толстого канала и передачей ключей. Допустим, выкладываем шифрованные толстые данные где-то публично. ссылку и ключ передаем сабжевым методом. для автоматизации пишем обертку. хмм…


                            1. Labunsky Автор
                              16.05.2019 20:12

                              Конечно, это канал исключительно для критических сообщений, передавать хоть что-то объемное будут только мазохисты)


  1. balamutang
    16.05.2019 17:06

    с таким же успехом можно бледные (+- пару уровней) QR коды накладывать на серии картинок и выкладывать в инсту/вк/фб/имиджборда, а на другом конце при имея те же картинки из серии (скачивая из той же галереи) — выделять QR коды и расшифровывать. причем в одной картинке сразу все сообщение поместится (сжатое и зашифрованное конечно же).


    1. Labunsky Автор
      16.05.2019 17:25

      Это будет стеганография (притом довольно топорная), немного другая степь. Скрытый канал полезен тем, что он скрыт сам по себе, его контейнером является среда передачи (в данном случае, месседжер/соцсеть), а не внешние объекты (картинки/аудио/и прочие файлы), загружаемые и передаваемые пользователем через нее


      1. balamutang
        16.05.2019 18:37

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


        1. BigBeaver
          16.05.2019 18:43

          Кстати, тоже неплохо. Можно кодировать 1/0 как цветы/еда, дома/машины и тд.


        1. Labunsky Автор
          16.05.2019 18:55

          Ну так под стеганографию можно и шифрование подогнать, определив как скрытие сообщений в «случайных числовых последовательностях», но оно нам надо?)

          наверняка сам факт взаимодействия двух аккаунтов фиксируется
          Я упомянул возможность этого. Тем не менее, в этом нет никакого смысла за пределами точек восстановления бд — с ней банально нечего делать, с тем же успехом можно забивать логи случайным шумом
          для скрытности надо делать сообщение широковещательным
          Широковещательный канал же доступен вообще всем. Это значит, что даже если телеграм не хранит изменение состояния, то его может фиксировать кто угодно другой, очень сильно ограничивая практическую пропускную способность до нескольких бит в день
          то это вообще как на ладони
          Несомненно, если злые админы телеги уже знают, что человек будет таким образом передавать очень важную и опасную информацию, и хотят ее перехватить, то они это сделают. Но скрытые каналы как раз применяются в тех случаях, когда о твоей передаче указанным методом заранее никто не знает И одновременно требуется, чтобы о ней никто не узнал пост-фактум. Это принципиально разные юзкейсы


  1. mikechips
    16.05.2019 18:50

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

    Нет))


  1. Skydev
    17.05.2019 02:35

    Еще информацию почти таким же образом можно передавать при помощи настройки белых-черных списков видимости последнего времени посещения (точное или приблизительное).
    К сожалению ещё один бит не добавит т.к. при блоке время вообще не видно, но можно для чего то использовать (например, последовательность блок-точное: 0, блок-приблизительное: 1).


    1. Mogwaika
      17.05.2019 15:33

      А можно просто по времени посещения.