Начинающий
- Статус
- Оффлайн
- Регистрация
- 17 Июн 2024
- Сообщения
- 36
- Реакции
- 29
Что такое MITM-атака?
Man-in-the-Middle (MITM) — атака, при которой злоумышленник встаёт между клиентом и сервером, перехватывая и потенциально подменяя трафик. Даже HTTPS не является абсолютной защитой: если атакующий может подсунуть клиенту свой сертификат (например, через поддельный корневой CA), TLS-соединение будет установлено с ним, а не с настоящим сервером. А использование своего кустарного шифрования как по мне глупо и не безопасно так как https уже предоставляет отличное AES шифрование и RSA шифрование для передачи ключа.Стандартная проверка сертификата в TLS выглядит так:
- Клиент получает сертификат сервера
- Проверяет цепочку доверия до одного из корневых CA в системном хранилище
- Если цепочка валидна — соединение считается безопасным
Что такое SSL Pinning?
SSL Pinning (или Certificate / Public Key Pinning) — техника, при которой приложение не просто доверяет системному хранилищу CA, а проверяет конкретный сертификат или публичный ключ сервера, жёстко прописанный в коде.Существует два варианта привязки:
- Certificate Pinning — привязка к конкретному сертификату (ломается при его обновлении)
- Public Key Pinning — привязка к хэшу публичного ключа (сертификат можно перевыпускать, сохраняя ключ)
Получение PIN'a сертификата домена:
openssl s_client -connect libomath.org:443 -servername libomath.org 2>/dev/null \
| openssl x509 -pubkey -noout \
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256 -binary \
| openssl enc -base64
Реализация на C++ с CPR и libcurl
Рассмотрим конкретную реализацию с использованием библиотеки CPR (C++ Requests), которая является обёрткой над libcurl:
C++:
#include <cpr/cpr.h>
#include <print>
#include <string>
#include <format>
#include <VMProtectSDK.h>
int main() {
auto r = cpr::Get(
cpr::Url{"https://libomath.org"},
cpr::Ssl(cpr::ssl::VerifyPeer(true),
cpr::ssl::VerifyHost(true),
cpr::ssl::PinnedPublicKey(
VMProtectDecryptStringA("sha256//LRaA1V5K6VB1abkO1XdpYSaGuStJG1vYiJAsdevSPjA=")))
);
if (r.error) {
std::println("Error: {}", r.error.message);
} else {
std::println("Status: {}", r.status_code);
}
}
Разберём каждый компонент:
VerifyPeer(true) — стандартная проверка сертификата через цепочку CA. Отключать нельзя.
VerifyHost(true) — проверка соответствия имени хоста в сертификате и запрошенного домена. Защищает от подстановки сертификата другого домена.
PinnedPublicKey("sha256//...") — ключевой параметр. libcurl вычисляет SHA-256 хэш публичного ключа из полученного сертификата и сравнивает с указанным значением. Если хэши не совпадают — соединение немедленно разрывается.
VMProtectDecryptStringA(...) — строка с pin-значением обфусцирована и зашифрована в бинаре с помощью VMProtect. Это предотвращает тривиальное извлечение pin'а из исполняемого файла и его подмену в памяти.
Результат:
Как мы видим приложение яро отказывается устанавливать соединение даже с сертификатом которому доверяет ОС. Если http debugger закрыть то всё будет работать нормально и вернётся код 200.
Как это защищает от MITM?
При установке TLS-соединения происходит следующая цепочка проверок:- Сервер присылает свой сертификат
- libcurl проверяет цепочку доверия (VerifyPeer)
- libcurl проверяет имя хоста (VerifyHost)
- libcurl извлекает публичный ключ из сертификата и вычисляет его SHA-256 хэш
- Хэш сравнивается с pin-значением, расшифрованным VMProtect
- Только при совпадении всех трёх проверок соединение считается безопасным
ИТОГ
Скомпрометированный центр сертификации: Заблокировано — ключ будет другойКомпрометация самого сервера (утечка приватного ключа): Не защищает — нужна ротация pin
Атака на уровне ОС (патч libcurl в памяти, ring0) : Не защищает — обход на системном уровне (однако в целях усложнения жизни реверсеру можно накинуть виртуализацию на всё что связанно с сетью: cpr, OpenSSL, curl и тд)
Отсутствие резервного pin при плановой ротации ключа: Приложение перестанет работать
