История о слабой внутренней безопасности в разгар скандалов и новых правил.
Несмотря на то, что они продвигают умные свидания с помощью науки и машинного обучения, их сайт было так легко взломать за 15 минут.
Заявление об ограничении ответственности:
Я не фанат онлайн-знакомств, и на моих устройствах не установлено никаких приложений для онлайн-знакомств. Я пробовал несколько самых известных онлайн-приложений для знакомств, и они мне не понравились. Я люблю подходить к людям где угодно и здороваться.
Так почему я подписался на это?
Они продвигали его в подполье как сайт знакомств, основанный на науке. Это действительно заинтриговало меня увидеть, как это работает.
Вы регистрируетесь, отвечаете на десятки вопросов о себе, а затем они показывают вам несколько совпадений с размытыми фотографиями, говоря, что они совместимы с вами примерно на 95%. Не платя за полное членство, вы сможете только смотреть, насколько вы совместимы, улыбаться людям и отправлять заранее определенные сообщения, такие как «Если бы вы были знамениты, кем бы вы были?» или «Если бы у вас был последний день в жизни, что бы вы сделали?». Если бы они ответили, вы бы не узнали, что они ответили, и не смогли бы отправить личное сообщение, если только вы не заплатите.
Этот сайт знакомств стоит более 50 фунтов стерлингов в месяц за возможность просматривать фотографии и отправлять сообщения людям. Это, безусловно, связано с тем, что они предоставляют такие умные услуги.
Сегодня вечером, когда я работал над своим стартапом DeveloperHub.io - сервисом для создания вашей собственной красивой документации по продукту, справочника по API, руководств пользователя в размещенных центрах разработчиков (порталах) - я получил сообщение от кого-то со стопроцентной совместимостью, как утверждает сайт знакомств. , поэтому я был очень заинтригован, узнав, кто она такая.
Сайт знакомств даже не позволяет прочитать сообщение. Поэтому я подумал: хм, давайте посмотрим, насколько умны эти «умные» люди.
Если вы не технический специалист, перейдите к разделу Мораль истории ниже.
Пусть начнется обратный инжиниринг
Я подумал, что первое, что я могу сделать, это увидеть входящий и исходящий сетевой трафик из приложения. Я использую приложение на своем iPhone. Итак, я установил на свой Mac прокси, Charles, и запустил Wi-Fi iPhone через этот прокси.
Я могу видеть профиль и каждую деталь, которую она ввела о себе. Довольно жутковато, но ладно, в любом случае это вид отображается в приложении. Но подождите, они просто отправили полный профиль девушки по незащищенному протоколу HTTP? Хм…
Есть список размытых фотографий, но я не мог легко получить доступ к не размытым фотографиям. Нет проблем, оставлю это на потом.
Кажется, что все важные запросы выполняются по SSL. Я активировал Charles SSL Proxy и установил сертификат Charles SSL на свой iPhone, но он просто не работал, и приложение больше не могло подключиться. Похоже, они проделали хорошую работу, зная, что я не использую надлежащие сертификаты SSL и что я выполняю атаку человека в середине.
Веб приложение
Я сказал, что если приложение iOS немного сложно взломать, давайте попробуем веб-приложение. Я захожу на их сайт и захожу в систему. Я почти видел тот же интерфейс, те же размытые лица, тот же почтовый ящик, который я не мог прочитать.
В Chrome довольно легко читать запросы HTTPS, и я так и сделал. Отфильтровал вкладку «Сеть» до XHR, посмотрел на запросы GET и вуаля… Вот только что полученное мной сообщение чата в почтовом ящике!
[ { "messageId": "b123738-5123-4123-9123-1232333b1234", "type": "CHAT", "value": "Hi Zed! I feel like I should send an interesting message but I'm all Mondayed out. How are you?", "createdTimeStamp": 1523914585468, "readTimeStamp": 1523914778123, "sender": false }, { "messageId": "ABC1235C-AABC-4ABC-8ABC-1ABC4EBC7ABC", "type": "SMILE", "createdTimeStamp": 1523883156123, "readTimeStamp": 1523886591123, "sender": true } ]
Ха! Это было просто.
Ладно, круто, но я все равно не могу ни определить, кто этот человек, ни ответить. Поскольку мы зашли так далеко, возможно, мы сможем пойти еще дальше.
На этом этапе - я начал писать этот пост на Medium, потому что понял, что их безопасность не кажется изумительной.
Отправка сообщения - это сработает?
Если мне нужно отправить сообщение, первое, что мне нужно сделать, это посмотреть, как выглядит отправка сообщения. Итак, я переключился на любого другого человека, который есть в моем списке совпадений, нажал кнопку, чтобы отправить заранее определенное сообщение, выбрал одного из них «Если бы вы знаменит, кем бы вы были?» И отправил его.
Тем временем я сохранял журнал сетевых запросов Chrome.
Хорошо, просматривая только что созданные запросы PUT и POST, я нигде не могу найти слово «знаменитый». Это то, что сообщение не отправляется, или что-то еще происходит?
В одном из запросов POST, которые произошли после того, как я отправил сообщение, полезная нагрузка была:
{ "logs": [ { "logMessage": "Message Sent (Soft ACK) - on server sender", "method": "WEBSOCKET", "logLevel": "INFO", "additionalInfo": "{\"messageId\":\"12351f23-fABC-4ABC-9ABC-ABCc123a0ABC\",\"matchId\":12309078132}" } ] }
Websocket. Черт побери, чат происходит через веб-сокеты (я этого и ожидал). Посмотрим, что делает веб-сокет.
Проверка веб-сокетов
Переходя к фильтрации веб-сокетов на вкладке «Сеть Chrome», мы обрадовались, что нужно было отслеживать только один веб-сокет.
Хорошо, давайте сделаем самое простое и отфильтруем по слову «известные».
Блин, «знаменитых» в вебсокете тоже не бывает. Перебирая сообщения, пытаясь понять отправляемый XML (кто, черт возьми, использует XML в наши дни для связи через веб-сокеты?), Похоже, что это:
- Открытие соединения
- Аутентификация в сервисе websocket
- Подключение к клиенту Jabber и настройка конфигурации здесь и там
- Тогда отправьте сообщение!
<message xmlns=”jabber:client” to=”[email protected]” id=”84123ff6-f123-4123-9123-c123458a0abc" type=”chat”><body>{“message”:{“messageId”:”84123ff6-f123-4123-9123-c123458a0abc",”type”:”CEQ”,”value”:”62"}}</body><request xmlns=”urn:xmpp:receipts”/><data><accesstoken>84123ff6-f123-4123-9123-c123458a0abc</accesstoken><header name=”User-Agent” value=”Mozilla/5.0 (Macintosh; Intel Mac OS X 10
THUMB
5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"/><header name=”X-xyz-gdid”/><resourceid>12309078132</resourceid></data></message>
Замечательно, теперь, когда я понимаю, как работает рассылка, давайте попробуем повторить это.
Я установил расширение Simple Websocket Client Chrome, скопировал URL-адрес веб-сокета, открыл соединение с веб-сокетом, и это было успехом. Следующие шаги:
- Хорошо, давайте сделаем первый запрос на открытие соединения. Хороший.
- Аутентифицируйте. Хороший.
- Подключитесь к клиенту Jabber и установите эти параметры. Хороший.
Ха, это просто. Хорошо, как нам сообщить об этом матче?
Глядя на полезную нагрузку JSON, кажется, что существует объект сообщения, а затем предварительно определенное сообщение имеет идентификатор, и мы его отправляем. Я решил попробовать установить для ключа сообщения значение «Привет!», Просто чтобы попробовать.
{“message”:"Hey There!"}
Аааа, ошибка.
<error code=’400' type=’modify’><bad-request xmlns=’urn:ietf:params:xml:ns:xmpp-stanzas’/><text xmlns=’urn:ietf:params:xml:ns:xmpp-stanzas’>Can not instantiate value of type [simple type, class com.xyz.services.comm.api.message.Message] from String value ('Hey there!'); no single-String constructor/factory method
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@b5a4c95; line: 1, column: 2] (through reference chain: com.xyz.services.comm.api.message.ClientMessageWrapper["message"])</text></error></message>
Хм, интересно. Не ожидал такой схемы. Давай еще раз посмотрим. Я отправляю заранее определенный идентификатор сообщения, поэтому он должен где-то существовать. Давайте посмотрим на список заранее определенных сообщений.
Я открыл список, чтобы отправить больше сообщений, проверил HTML и выяснил, что это сообщение имеет идентификатор 62.
Хорошо, я вижу, где я ошибся, messageId - это какой-то другой идентификатор, а значение 62 для предварительно определенного сообщения. Что насчет типа «CEQ», что мне установить?
Я вспомнил, что просматривая запросы GET, видел такое. Вот:
Теперь я вижу, что делать, просто установил тип на ЧАТ и значение для моего сообщения чата. Давай попробуем.
Ответ:
<message xmlns=’jabber:client’ from=’[email protected]/android.phone.emulator’ to=’1231232yr2_3–[email protected]/android.phone.emulator’ xml:lang=’en’ id=’123f7–32' type=’chat’><data/><received xmlns=’urn:xmpp:receipts’ id=’81231236-f5ce-abcd-9abc-c6e12312312c0'/></message>
О ДА, Я ПОЛУЧИЛ ЧЕК. Обновите страницу почтового ящика, и вуаля, мы написали сообщение.
Поговори с моей спичкой
Последний кусочек головоломки - знать, как разговаривать с кем-нибудь на этом веб-сайте, а не только с этим человеком. Кажется, что у человека, с которым я болтаю, нет никакого идентификатора, кроме как во фрейме веб-сокета сообщения. Кажется, что адрес чата, который выглядит как адрес электронной почты, является идентификатором человека, которому я отправляю сообщения.
to=”[email protected]”
Откуда мне взять этот идентификатор.
Скопируйте расширенную информацию профиля в Sublime Text. Найдите адрес чата в тексте. Ах, это зашифрованный идентификатор пользователя. Хорошо, давай попробуем.
{“message”:{“messageId”:”84123ff6-f123-4123-123b-c6123e8a1230",”type”:”CHAT”,”value”:”Good evening Sophie! Haha already tired? Interesting messages are over-rated anyway 😜”}}
Что ж, это был провал, я отправил его той же девушке, на которой я тестировал. Ха. Не надо было добавлять имя, теперь оно будет выглядеть супер-странно… Виииллл. Давай еще раз попробуем.
После долгого просмотра всех этих идентификаторов и адресов чата выясняется, что это идентификатор ресурса:
<resourceid>12309078132</resourceid>
Пробный номер 2:
Найдите идентификатор этого ресурса. Хорошо, это незашифрованный идентификатор пользователя. Очень просто. Отредактируйте идентификатор ресурса и вуаля. Мы получили сообщение милашке!
Зачем останавливаться здесь
Я начал думать, ну развлекается. Как насчет того, чтобы сейчас попытаться увидеть эти размытые фотографии. В массиве JSON профиля есть список фотографий, и URL-адреса выглядят так:
https://images.xyz.com/photos/v2/photo/NORMAL/I1/d5abcttnp5yxjytb227v6fp56p.jpg?blur=60&crop=faces&fit=crop&g=2&h=160&ixlib=java-1.1.1&w=160&s=cda2e652b4182b123a1f5f6781daa36a
Я пытался изменить параметры запроса, но всегда получал пустое изображение.
Я подумал, может быть, если у меня будет платная учетная запись, тогда я смогу увидеть, как сопоставить размытые изображения с исходными изображениями. Что ж, я не собираюсь этого делать.
Так что мы можем сделать?
💡💡💡💡💡💡💡💡💡
Просто проверьте мою фотографию в профиле, из чего состоит URL?
На самом деле, я сделал:
https://www.xyz.com/photos/v1/photo/THUMB/I3/1236VKj18jtm5Ih8Cr2pSAabc.jpg
Что это за параметры? Давайте распечатаем мой профиль на jsonprettyprint.com, скопируем в Sublime Text и произведем поиск. Оказывается, на веб-сайте есть способ нумерации изображений I1
, I2
, I3
и так далее, этот длинный идентификатор - мой encryptedUserId
, фотографии имеют версии (1 или 2), а размеры определяются THUMB
, ICON
, NORMAL
, и так далее. Так:
https://www.xyz.com/photos/v<version>/photo/<SIZE>/<IMAGE-NUMBER>/<ENCRYPTED-USER-ID>.jpg
Это просто, давайте применим это к изображению другого совпадения. И вуаля, у нас есть изображение.
Является ли этот сервис таким небезопасным, как я думаю?
Хорошо, давайте проверим, насколько это небезопасно. Можем ли мы читать профили других людей, даже не сравнивая их с ними? Давайте посмотрим.
Чтобы получить чей-то профиль, HTTP-запрос GET выглядит так:
https://www.xyz.com/publicapi/v2/matchprofile/<match-id>/profile
Если мы скопируем CURL из сети Chrome, мы получим такую чушь:
curl 'https://www.xyz.com/publicapi/v2/matchprofile/12303942525/profile?' -H 'authorization: Bearer 12339f23-2302-4e6f-b9ae-1f9c99a6e123' -H 'Accept-Encoding: gzip, deflate, br' -H 'Accept-Language: en-US,en;q=0.9,ar;q=0.8' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10THUMB
5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36' -H 'x-xyz-gdid: ' -H 'accept: application/json' -H 'Connection: keep-alive' -H 'content-type: application/json' -H 'x-xyz-platform: desktop' --compressed
Хорошо, давайте изменим одно число в идентификаторе матча и посмотрим, сможем ли мы получить данные.
404 Не Найдено.
Повторите попытку с другими номерами: 404 Not Found.
Это хорошо. Можем ли мы получить эти профили, используя идентификатор пользователя? Я не понимаю, как мы можем это сделать сейчас. Без проблем. Я не буду тратить на это больше времени, моя точка зрения доказана.
Мораль истории
Я не хакер и не хочу причинять вред. Я просто понимаю, как работают веб-сервисы. Реверс-инжиниринг, который я только что сделал, на 99% выполнен в Chrome без каких-либо других инструментов. Получить функции полноправного членства в сервисе, который требует такой высокой оплаты, было так просто, поскольку большая часть безопасности выполнялась во внешнем интерфейсе, а не в серверной части. Это крепость с высокими стенами и открытыми воротами, внутри которой нет стражи.
Рекомендации своим инженерам (если им было интересно):
- Ради бога, используйте только безопасные соединения. Отправка чьего-либо профиля через HTTP недопустима.
- Настройте разрешения в вашей системе обмена сообщениями Jabber. Проверьте, отправляется ли сообщение с
CHAT
действием, что отправляющий пользователь действительно является премиум-участником. - Отфильтруйте вашу
messages
конечную точку (которая показывает разговоры), чтобы показывать сообщения только в том случае, если пользователь является премиум-участником. - Не показывайте изображения без проверки членства. Вы можете перенаправить свой веб-сервер (Jetty) при запросе изображения, проверить членство и подавать его размытым, если пользователь не является пользователем премиум-класса, или обычным пользователем, если он им является.
- И, что наиболее важно, сделайте свою безопасность изнутри!
Ваше членство можно легко заменить расширением Chrome, которое заменяет URL-адреса для фотографий, заменяет HTML-код почтового ящика в соответствии с тем, что вы получаете в запросах, и отправляет сообщения с помощью вашего веб-сокета.
Почему все это имеет значение?
После скандала с Facebook я бы рекомендовал каждой компании нанять несколько этичных хакеров, чтобы понять, где ваш сервис небезопасен. В вашей политике конфиденциальности указано, что у вас есть обширные меры безопасности, включая использование SSL, что вы будете проявлять разумную осторожность в обеспечении безопасной передачи информации, но вы также заявляете, что не несете ответственности за любое непреднамеренное раскрытие информации.
Мы живем в эпоху, когда сбор данных технически прост для компаний, а пользователи готовы безрассудно и без колебаний раскрывать свои данные, не зная о расплывчатой политике конфиденциальности, стоящей за ними. Объем данных, которые вы собираете о пользователях, огромен, и вы несете за это большую ответственность. Если вы не можете защитить эти данные, не собирайте их.
Общий регламент защиты данных (GDPR) выйдет 25 мая 2018 года. Когда постановление вступит в силу, вам лучше не отправлять профиль любого европейца на небезопасный уровень, поскольку вас могут оштрафовать на 20 миллионов евро или 4% от вашего глобальный оборот, в зависимости от того, что больше.
Обновление (25 мая 18): Здравствуйте, GDPR!
Мои надежды
Я надеюсь, что благодаря GDPR ваша осведомленность об объеме данных, которые собираются о вас, повысится. С большей осведомленностью люди начнут нерешительно предоставлять информацию о себе, которая может быть ненужной для работы служб, и компании будут вынуждены быть более прозрачными в отношении того, как они используют данные.
Помните, что с GDPR вы можете запросить копию своих данных в удобочитаемом формате у любого поставщика услуг, и этот запрос должен быть выполнен в течение 72 часов.
Как только начнут появляться новости о штрафах компаний, компании начнут применять методы защиты своих систем. В конце концов, кажется, что только 27% предприятий считают, что GDPR применим к их бизнесу, а половина предприятий Великобритании знают о GDPR.
Примечание. Я размыл и заменил все вхождения названия веб-сайта и всю идентифицирующую информацию об участниках, относящихся к ним. Этот пост не предназначен для веб-сайта и не предназначен для причинения им вреда.
Если вам понравилась эта история, 👏 👏 👏 за нее ниже!