Универсальный перехват. Как обойти SSLPinning раз и навсегда и читать трафик любого приложения

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

Немного об HTTP и о том, при чем здесь OpenSSL

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

За время своего существования HTTP оброс кучей разных фич и наворотов, устраняющих недостатки его изначальной «вебовой» версии: Cookies для устранения Stateless, Keep-Alive и Long polling для имитации непрерывности соединения, концепция REST, бинарный HTTP/2 с повсеместным сжатием и многое другое. Забегая вперед, отмечу, что HTTP/2 вообще разрабатывался с учетом использования вместе с TLS 1.2+.

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

Параллельно с HTTP шло развитие SSL, а их синтез — HTTPS — постепенно захватывал долю трафика в мировом интернете, пока в один прекрасный момент в конце января 2017 года не превысил половину. Сейчас его доля стремится к 80%. Многие увидели смысл в шифровании данных, а тех, кто не успел, стали поторапливать в том числе и разработчики браузеров. В итоге все снова приняли это (что хорошо!) и начали повсеместно применять в своих приложениях, многие из которых и так уже работали по привычному HTTP.

Развитие SSL, а впоследствии и TLS (SSL 3.0+) во многом определялось развитием опенсорсного проекта OpenSSL. Он медленно, но верно впитывал в себя все новые и новые спецификации RFC. Как известно, велосипеды изобретать никто не любит, поэтому библиотека OpenSSL стала де-факто стандартом при имплементации защищенного транспорта для HTTP, и теперь ее следы можно обнаружить в бессчетном количестве софтверных проектов.

 

Принцип работы классической схемы HTTPS

Использование HTTPS помогло защититься от MITM-атак на пользователя, однако не от его собственных ошибок. Достаточно взглянуть на классическую схему протокола SSL, и становится понятно, что конфиденциальность здесь держится исключительно на сертификате сервера.

TLS Handshake

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

Для проверки используются центры сертификации, которые бывают корневыми и промежуточными. Публичных корневых центров мало, и обо всех них знает каждый клиент SSL/TLS. А промежуточных центров может быть очень много — единственное их отличие от корневых в том, что выпускаемые ими сертификаты подписываются приватным ключом вышестоящего CA. Например, если посмотреть на путь сертификации google.com на рисунке ниже, то корневым будет сертификат, выданный GlobalSign, а промежуточным — Google Internet Authority.

Цепочка сертификатов Google

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

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

 

Исследуем принципы работы OpenSSL на подопытном приложении

Библиотека OpenSSL «в вакууме» по умолчанию не доверяет никому, а чтобы инициировать процесс появления этого доверия, библиотеке необходимо сообщить о том, кому, собственно, мы намерены доверять. Если рассмотреть пример классического клиента TLS, приведенный в wiki.openssl, то можно там заметить загрузку доверенных сертификатов непосредственно перед осуществлением запроса HTTP:

...
ctx = SSL_CTX_new(method);
if(!(ctx != NULL)) handleFailure();

SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
SSL_CTX_set_verify_depth(ctx, 4);

const long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
SSL_CTX_set_options(ctx, flags);

// загрузка доверенных сертификатов
res = SSL_CTX_load_verify_locations(ctx, "random-org-chain.pem", NULL);
if(!(1 == res)) handleFailure();
...

Кроме функции SSL_CTX_load_verify_locations существует еще несколько похожих способов загрузки проверенных сертификатов внутрь OpenSSL. Наверняка разработчики, которые писали свои клиенты TLS, не стеснялись подсматривать подобные общедоступные примеры.

Попробуем отследить поведение представленного классического клиента TLS на каком-нибудь реальном приложении. Для начала возьмем что-нибудь простое, что могло бы быть связано с TLS, — например, библиотеку OkHttp. Она обеспечивает коммуникации по HTTP/S в бесчисленном количестве современных приложений для Android.

Заранее замечу, что OkHttp написана на Java и работает на JVM, поэтому сама по себе она не интересна, так как является своеобразной оберткой более интересной составляющей — низкоуровневой имплементации OpenSSL для Android. Вот ей-то мы и займемся.

 

Готовим тестовое приложение

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

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

// Кнопка "Do just HTTPS!"
val okHttpClient = OkHttpClient.Builder().build()
val request = Request.Builder().url("https://google.com").get().build();
okHttpClient.newCall(request).execute()
...
// Кнопка "Do Https with pin!"
val certificatePinner = CertificatePinner.Builder()
  .add("google.com","sha256/f8NnEFZxQ4ExFOhSN7EiFWtiudZQVD2oY60uauV/n78=")
  .build()
val okHttpClient = OkHttpClient.Builder()
  .certificatePinner(certificatePinner)
  .build()
val request = Request.Builder().url("https://google.com").get().build();
okHttpClient.newCall(request).execute()

UI подопытного приложения для Android

Совершенно стандартный код, кочующий по Ctrl-C и Ctrl-V из одного проекта в другой, подвергаясь при этом несущественным изменениям. Итак, запускаем новоиспеченное приложение и сразу же заглядываем в разметку его памяти.

Ищем следы OpenSSL в приложениях для Android

Источник: xakep.ru

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *