Учебное пособие по обратному проектированию частного API вашего программного обеспечения: взлом вашего дивана

Опубликовано: 2022-03-11

Путешествия — моя страсть, и я большой поклонник каучсерфинга. Couchsurfing — это глобальное сообщество путешественников, где вы можете найти жилье или поделиться своим домом с другими путешественниками. Кроме того, Couchsurfing поможет вам насладиться настоящим путешествием, общаясь с местными жителями. Я участвую в сообществе Couchsurfing более 3 лет. Сначала я посещал встречи, а потом, наконец, смог принимать людей. Какое это было удивительное путешествие! Я встретил так много замечательных людей со всего мира и завел много друзей. Весь этот опыт действительно изменил мою жизнь.

Я сам принимал много путешественников, гораздо больше, чем я на самом деле занимался серфингом. Живя в одном из основных туристических направлений на Французской Ривьере, я получил огромное количество запросов на диван (до 10 в день в высокий сезон). Как внештатный бэкенд-разработчик, я сразу заметил, что проблема веб-сайта coachsurfing.com заключается в том, что он не справляется с такими «высоконагруженными» случаями должным образом. Информации о наличии вашего дивана нет — когда вы получаете новый запрос на диван, вы не можете быть уверены, что уже принимаете кого-то в это время. Должно быть визуальное представление ваших принятых и ожидающих запросов, чтобы вы могли лучше управлять ими. Кроме того, если бы вы могли объявить доступность своей кушетки общедоступной, вы могли бы избежать ненужных запросов на кушетки. Чтобы лучше понять, что я имею в виду, взгляните на календарь Airbnb.

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

  • http://www.nithincoca.com/2013/03/27/the-rise-and-fall-of-couchsurfing/
  • http://mechanicalbrain.wordpress.com/2013/03/04/couchsurfing-a-sad-end-to-a-great-idea/

Я знал, что многие члены сообщества были бы рады этой функции. Итак, я решил сделать приложение для решения этой проблемы. Оказывается, общедоступного Couchsurfing API нет. Вот ответ, который я получил от их службы поддержки:

«К сожалению, мы вынуждены сообщить вам, что наш API на самом деле не является общедоступным, и на данный момент нет планов сделать его общедоступным».

вломиться в мой диван

Пришло время использовать некоторые из моих любимых методов обратного проектирования программного обеспечения, чтобы взломать Couchsurfing.com. Я предположил, что их мобильные приложения должны использовать какой-то API для запросов к серверной части. Итак, мне пришлось перехватывать HTTP-запросы, идущие от мобильного приложения к серверной части. Для этого я установил прокси в локальной сети и подключил к нему свой iPhone для перехвата HTTP-запросов. Таким образом, я смог найти точки доступа к их частному API и выяснить их формат полезной нагрузки JSON.

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

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

  • https://github.com/nderkach/couchsurfing-python

Пример календаря

Я создал отличную функцию для Couchsurfing и, естественно, предположил, что они оценят мою работу — возможно, даже предложат мне место в их команде разработчиков. Я отправил электронное письмо на адрес jobs(at)couchsurfing.com со ссылкой на веб-сайт, моим резюме и рекомендацией. Благодарственное письмо, оставленное одним из моих гостей на каучсерфинге:

Благодарственная записка.

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

Я предложил дать им мое приложение бесплатно. Они могут разместить его в своей среде и подключить через аутентификацию Facebook. В конце концов, это отличная функция, и сообщество нуждалось в ней. Вот окончательное решение, которое я получил:

«После праздников мы возвращаемся в колею здесь и хотели продолжить.

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

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

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

Доступный в настоящее время API скоро будет заменен версией, которая потребует аутентификации/авторизации от приложений, которые получают к нему доступ».

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

Return To Innocence - снова взломать мой диван

Несколько недель назад меня побудило написать статью о методах обратного проектирования частных API. Естественно, я решил обобщить предыдущие статьи, написанные на эту тему, и добавить еще несколько деталей. Когда я начал писать новую статью, я хотел продемонстрировать процесс реверс-инжиниринга с современным API и сделать еще одну заглушку по взлому API. Основываясь на моем предыдущем опыте и на том факте, что Couchsurfing недавно объявила о совершенно новом веб-сайте и мобильном приложении http://blog.couchsurfing.com/the-future-of-couchsurfing-is-on-the-way/, я решили снова взломать их API.

Почему я занимаюсь этим процессом обратного проектирования? Ну, во-первых, заниматься реверс-инжинирингом программного обеспечения в целом очень весело. Что мне особенно нравится в этом, так это то, что здесь задействованы не только ваши технические навыки, но и ваша интуиция. Иногда лучший способ выяснить что-то — это сделать обоснованное предположение — это сэкономит вам много времени по сравнению с грубой силой. Недавно я услышал историю от компании, которой пришлось работать с проприетарными API и практически без документации. Они несколько дней пытались расшифровать полезную нагрузку ответа API в неизвестном формате, затем кто-то решил попробовать ?decode=true в конце URL-адреса, и у них был правильный JSON. Иногда, если вам повезет, все, что вам нужно сделать, это украсить ответ JSON.

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

Таким образом, с новым API веб-сайта веб-сайта веб-сайта веб-сайта Couchsurfing.com я начал с аналогичного подхода и установил их последнее приложение для iOS.

Во-первых, вам нужно настроить прокси-сервер в вашей локальной сети для подделки HTTP-запросов, поступающих от приложения к API, путем выполнения атаки «человек посередине» (MITM).

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

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

Для корректной работы SSL должны быть соблюдены следующие условия:

  • Сертификат сервера должен быть подписан доверенным центром сертификации (ЦС).
  • Общее имя сервера в сертификате должно совпадать с доменным именем сервера.

Чтобы преодолеть шифрование при MITM-атаке, наш прокси-сервер должен действовать как ЦС (центр сертификации) и генерировать сертификаты на лету. Например, если клиент пытается подключиться к www.google.com, прокси-сервер динамически создает сертификат для www.google.com и подписывает его. Теперь клиент думает, что прокси на самом деле www.google.com

На этой диаграмме показаны шаги по обратному проектированию частного API.

Чтобы реализовать сниффинг-прокси, используемый для обратного проектирования частного API, я буду использовать инструмент под названием mitmproxy. Вы можете использовать любой другой прозрачный HTTPS-прокси. Чарльз — еще один пример с приятным графическим интерфейсом. Чтобы все заработало, нам нужно настроить следующие вещи:

Настройте шлюз по умолчанию для WiFi-подключения вашего телефона в качестве прокси-сервера (чтобы прокси-сервер находился посередине и все пакеты проходили через него) Установите сертификат прокси-сервера на телефон (чтобы клиент имел открытый ключ прокси-сервера в своем хранилище доверенных сертификатов)

Проверьте документацию вашего прокси об установке сертификата. Вот инструкции для mitmproxy. А вот PEM-файл сертификата для iOS.

Чтобы отслеживать перехваченные HTTP-запросы, вы просто запускаете mitmproxy и подключаетесь к нему со своего мобильного телефона (порт по умолчанию — 8080).

Настройки мобильного телефона.

Откройте веб-сайт в мобильном браузере. На этом этапе вы должны увидеть трафик в mitmproxy.

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

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

Не существует строгого алгоритма обратного проектирования программного API — большую часть времени вы полагаетесь на свою интуицию и делаете предположения.

Мой подход состоит в том, чтобы реплицировать вызовы API и играть с различными вариантами. Для начала неплохо воспроизвести запрос, который вы перехватили в mitmproxy, и посмотреть, работает ли он (нажмите 'r', чтобы воспроизвести запрос). Первый шаг — выяснить, какие заголовки являются обязательными. Довольно удобно играть с заголовками с помощью mitmproxy: нажмите «e», чтобы войти в режим редактирования, затем «h», чтобы изменить заголовки. С ярлыками, которые они используют, любители vim будут чувствовать себя как дома. Вы также можете использовать расширения браузера, такие как Postman, для тестирования API, но они, как правило, добавляют ненужные заголовки, поэтому я предлагаю придерживаться mitmproxy или curl.

Я сделал скрипт, который читает файл дампа mitmproxy и генерирует строку curl - https://gist.github.com/nderkach/bdb31b04fb1e69fa5346

Начнем с запроса, отправленного при входе в систему.

 POST https://hapi.couchsurfing.com/api/v2/sessions ← 200 application/json 

Первый шаг в этом руководстве по обратному инжинирингу — повторить вызовы API и поэкспериментировать с полученными параметрами.

Первое, что я заметил, это то, что каждый запрос содержит обязательный заголовок X-CS-Url-Signature , который каждый раз разный. Я также пытался переиграть запрос через некоторое время, чтобы проверить, есть ли проверка временной метки на сервере, и ее нет. Следующее, что нужно сделать, это выяснить, как вычисляется эта подпись.

В этот момент я решил перепроектировать двоичный файл и выяснить алгоритм. Естественно, имея опыт разработки под iPhone и имея в распоряжении iPhone, я решил начать с iPhone ipa (приложение для iPhone). Оказывается, чтобы расшифровать один, мне нужен телефон с джейлбрейком. Останавливаться! Время молотка.

Потом я вспомнил, что у них есть и приложение для Android. Я немного не решался попробовать этот подход, так как ничего не знаю об Android или Java. Тогда я подумал, что это хороший шанс узнать что-то новое. Оказалось, что легче получить удобочитаемый квази-исходный код путем декомпиляции байт-кода Java, чем сильно оптимизированный машинный код iphone.

Apk (приложение для Android) в основном представляет собой zip-файл. Вы можете использовать любой zip-архиватор, чтобы распаковать его содержимое. Вы найдете файл с именем class.dex, который представляет собой байт-код Dalvik. Dalvik — это виртуальная машина, используемая для запуска переведенного байт-кода Java на Android.

Чтобы декомпилировать файл .dex в исходный код .java, я использовал инструмент под названием dex2jar. Результатом этого инструмента является файл jar, который можно декомпилировать с помощью различных инструментов. Вы даже можете открыть банку в Eclipse или IntelliJ IDEA, и она сделает всю работу за вас. Большинство этих инструментов дают аналогичный результат. Нам все равно, сможем ли мы скомпилировать его для запуска, мы просто используем его для анализа исходного кода.

Вот список инструментов, которые я пробовал:

  • FernFlower (теперь часть IntelliJ IDEA)
  • CFR
  • JD-GUI
  • Кракатау
  • Процион

CFR и FernFlower работали для меня лучше всего. JD-GUI не смог декомпилировать некоторые критические части кода и был бесполезен, в то время как остальные были примерно такого же качества. К счастью, кажется, что код кода Java не был запутан, но есть такие инструменты, как ProGuard http://developer.android.com/tools/help/proguard.html, которые помогут вам расшифровать код.

Декомпиляция Java на самом деле не является предметом этого руководства по обратному инжинирингу — на эту тему написано много, поэтому давайте предположим, что вы успешно декомпилировали и деобфусцировали свой код Java.

Я объединил весь соответствующий код, используемый для вычисления X-CS-Url-Signature, в следующем виде: https://gist.github.com/nderkach/d11540e9af322f1c1c74.

Во-первых, я искал упоминания X-CS-Url-Signature , которые нашел в RetrofitHttpClient . Один конкретный вызов показался интересным - к модулю EncUtils . Покопавшись в нем, я понял, что они используют HMAC SHA1. HMAC — это код аутентификации сообщения, который использует криптографическую функцию (в данном случае SHA1) для вычисления хэша сообщения. Он используется для обеспечения целостности (т. е. для предотвращения изменения запроса человеком посередине) и аутентификации.

Для вычисления X-CS-Url-Signature нам нужны две вещи: закрытый ключ и закодированное сообщение (вероятно, некоторая вариация полезной нагрузки HTTP-запроса и URL-адреса).

 final String a2 = EncUtils.a(EncUtils.a(a, s)); final ArrayList<Header> list = new ArrayList<Header>(request.getHeaders()); list.add(new Header("X-CS-Url-Signature", a2));

В коде a — это сообщение, а s — это ключ, который используется для вычисления заголовка a2 (двойной вызов EncUtils просто вычисляет шестнадцатеричный дайджест HMAC SHA1).

Найти ключ не составило труда — он хранился в виде простого текста в ApiModule и использовался для инициализации второго параметра RetrofitHttpClient.

 RetrofitHttpClient a(OkHttpClient okHttpClient) { return new RetrofitHttpClient(okHttpClient, "v3#!R3v44y3ZsJykkb$E@CG#XreXeGCh"); }

Если мы посмотрим на вызов EncUtils , то увидим, что приведенный выше строковый литерал дословно используется в качестве ключа для вычисления HMAC, за исключением случая, когда this.b определен. В последнем случае this.b добавляется точка.

 String s; if (this.b == null) { s = this.a; } else { s = this.a + "." + this.b; }

Теперь, просто взглянув на код, я не понял, где и как инициализируется this.b (единственное, что я смог обнаружить, это то, что он вызывается в методе с сигнатурой this.a(String b) , но нигде в коде не нашел вызова).

 public void a(final String b) { this.b = b; }

Я призываю вас декомпилировать его и выяснить это самостоятельно :)

Выяснить сообщение было довольно просто — в коде вы можете видеть, что это конкатенация пути URL-адреса, то есть /api/v2/sessions и строки с полезной нагрузкой JSON (если есть).

 final byte[] b = this.b(request.getUrl()); byte[] a; if (request.getBody() != null && request.getBody() instanceof JsonTypedOutput) { System.out.println("body"); // this.a(x, y) concatenates byte arrays a = this.a(b, ((JsonTypedOutput)request.getBody()).a); } else { a = b; }

Просто взглянув на код, было сложно понять точный алгоритм расчета HMAC. Итак, я решил перестроить приложение с символами отладки, чтобы выяснить, как именно оно работает. Я использовал инструмент под названием apktool https://code.google.com/p/android-apktool/ для дизассемблирования байт-кода Dalvik с помощью smali https://code.google.com/p/smali/. Я следовал руководству по адресу https://code.google.com/p/android-apktool/wiki/SmaliDebugging.

После сборки apk вам необходимо подписать и установить его на свое устройство. Поскольку у меня не было устройства Android, я использовал эмулятор, который поставляется с Android SDK. При кормлении с ложки вот как это сделать:

 jarsigner -verbose -keystore ~/.android/debug.keystore -storepass android -keypass android <path_to_your_built_apk> androiddebugkey jarsigner -verify -verbose -certs <path_to_your_built_apk> zipalign -v 4 <path_to_your_built_apk> <path_to_your_output_signed_apk>

Я использовал встроенный эмулятор Android, который поставляется с SDK, и виртуальный образ Atom x86 с включенным HAXM, чтобы обеспечить его бесперебойную работу.

 tools/emulator -avd mydroid -no-boot-anim -cpu-delay 0

Вот хорошее руководство по настройке виртуального образа: http://jolicode.com/blog/speed-up-your-android-emulator

Убедитесь, что строка HAX работает, а эмулятор работает в быстром режиме virt при запуске эмулятора, чтобы убедиться, что у вас включен HAXM.

Затем я установил apk в эмулятор и запустил приложение. Следуя руководству по apktool, я использовал удаленный отладчик IntelliJ IDEA, чтобы подключиться к эмулятору и установить несколько точек останова:

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

Немного поиграв с приложением, я смог выяснить, что закрытый ключ, используемый для инициализации RetrofitHttpClient , используется для вычисления HMAC подписи запроса на вход. В ответ на логин POST вы получаете идентификатор пользователя и accessToken ( X-Access-Token ). Маркер доступа используется для авторизации всех следующих запросов. HMAC для всех запросов после входа в систему создается так же, как запрос на вход, за исключением того, что ключ составляется путем добавления .<user_id> к исходному закрытому ключу.

Здесь показан процесс авторизации, необходимый для обратного проектирования этого частного API.

После авторизации приложение отправляет следующий запрос:

 POST https://hapi.couchsurfing.com/api/v2/users/1003669205/registerDevice ← 200 application/json

Как я смог эмпирически сделать вывод, этот запрос необязателен для аутентификации. Бонусные баллы, если вы выясните, для чего он используется!

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

 GET https://hapi.couchsurfing.com/api/v2/users/1003669205 ← 200 application/json 

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

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

Я написал простой скрипт Python для входа в систему с использованием ваших учетных данных веб-сайта веб-сайта веб-сайта веб-сайта веб-сайта веб-сайта веб-сайта веб-сайта веб-сайта веб-сайта веб-сайта Couchsurfing.com и получения вашего профиля пользователя: https://gist.github.com/nderkach/899281d7e6dd0d497533. Вот оболочка Python для API: https://github.com/nderkach/couchsurfing-python с пакетом, доступным в репозитории pypi (pip install Couchsurfing).

Следующие шаги

Я не уверен, что именно я собираюсь делать с API на этот раз. Код HTML в профилях пользователей больше не разрешен, поэтому мне придется придумать другой подход к старой проблеме. Я буду продолжать разрабатывать и улучшать оболочку python API, если на нее будет спрос, и предполагаю, что веб-сайт Couchsurfing.com не вызовет слишком много проблем. Я не слишком много изучал API, а просто проверил его на наличие некоторых основных уязвимостей. Это кажется достаточно безопасным, но было бы интересно узнать, можете ли вы получить доступ к данным, недоступным через веб-сайт. В любом случае, теперь вы можете использовать мой обратный инжиниринг для создания альтернативного клиента для Windows Phone, Pebble или вашего умного дивана.

Подведение итогов с вопросом

Я хотел бы открыть дискуссию — почему бы не опубликовать свой API и не сделать его общедоступным? Даже если бы мне не удалось взломать API, все равно можно было бы парсить сайт. Это было бы медленнее и сложнее в обслуживании, но, конечно же, они предпочли бы, чтобы потребители использовали API, а не парсер. Доступность API позволит сторонним разработчикам улучшать продукт компании и создавать на его основе дополнительные услуги. Можно возразить, что поддерживать общедоступный API будет дороже, чем частный; но опять же, преимущества ваших услуг по созданию сообщества поверх вашего продукта перевешивают затраты на обслуживание API.

Можно ли полностью предотвратить использование частного API сторонними клиентами? Я так не думаю. Использование закрепления SSL предотвратит прослушивание запросов API с помощью простого метода прозрачного прокси, как описано ранее. В конце концов, даже если вы запутаете двоичный файл, мотивированный хакер с некоторыми ресурсами и временем всегда сможет перепроектировать двоичный файл приложения и получить закрытый ключ/сертификат. Я думаю, что предположение о том, что конечная точка клиента защищена, по своей сути неверно. API-клиент — слабое место.

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

Couchsurfing, пожалуйста, с сахаром сверху, откройте API.