Добрый день, дорогие читатели Хабра!
Мы команда специалистов из компании ПМ. Довольно часто к нам приходят заказы на анализ защищенности или тестирование на проникновение веб-ресурсов. Первоначальным этапом при проведении работ является разведка, которая включает в себя анализ принципов работы исследуемого веб-ресурса, обнаружение используемых технологий, окружения и т.д. Одним из методов для осуществления данной задачи является исследование пакетов, отправляемых между веб-клиентом и веб-сервером. Иногда исследование пакетов не составляет особого труда, но бывают случаи, когда это становится нетривиальной задачей. В ситуации, когда речь идет об открытом (незашифрованном) трафике, можно элементарно воспользоваться любым пакетным анализатором, типа Wireshark. Однако в тех случаях, когда применяется шифрование, приходится использовать различные методы для расшифровки. Именно они и будут рассмотрены в данной статье.
1. Настройка расшифровки трафика при помощи перехватывающего прокси
1.1. Обычный прокси
В качестве перехватывающего прокси рассмотрим Burp Suite. При его запуске автоматически запускается листенер на 127.0.0.1:8080. Чтобы пропустить трафик через него, нужно указать утилите этот адрес. К примеру, так это будет для утилиты curl:
curl --proxy 127.0.0.1:8080 https://google.com
Стоит отметить, что так как сертификат Burp Suite по умолчанию не является доверенным, то потребовалось отключить проверку сертификата при помощи опции -k. Альтернативой этому может служить добавление CA сертификата в список доверенных. Скачать его можно, если перейти по адресу http://127.0.0.1:8080/. В случае с curl, чтобы добавить сертификат в список доверенных нужно перевести скаченный cacert.der в cacert.crt:
openssl x509 -in cacert.der -inform DER -out cacert.crt
После этого можно добавить сертификат:
sudo cp cacert.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
1.2. Прозрачный прокси
В случае, если клиент не может использовать прокси (например, если у используемого ПО нет поддержки прокси в настройках), в Burp Suite есть возможность настроить так называемый прозрачный прокси.
При этом варианте трафик направляется листенеру Burp Suite, который после этого делает запрос на нужный ресурс. Есть несколько методов как можно настроить невидимый прокси.
1.1.1. Изменение разрешения DNS
Изменяя трансляцию доменных имен через файл /etc/hosts можно перенаправить трафик на листенер невидимого прокси:
После того как пакет поступил на листенер, Burp Suite перенаправляет его, основываясь на заголовке Host. Для того, чтобы Burp Suite доставил пакеты туда, куда мы бы хотели их доставить, можно воспользоваться следующими методами:
1. Изменить разрешение доменных имен в самом Burp Suite
2. Перенаправить трафик в опциях листенера. При таком варианте нужно для каждого ip создавать виртуальный интерфейс командой:
sudo ifconfig lo:0 127.0.0.2 netmask 255.255.255.0 up
Такой вариант подойдет, если в запросе отсутствует заголовок Host, например, если используется протокол ниже HTTP/1.1
1.1.1. Перенаправление трафика через iptables
При помощи iptables можно перенаправить исходящий трафик на листенер Burp Suite. Однако, следует учитывать, что нужно каким-то образом пропускать трафик, который отправляет сам Burp, в противном случае произойдет зацикливание. Это можно решить, если создать пользователя (например, burp) и потом запустить от него Burp Suite:
adduser burp
После этого можно создать правило iptables:
sudo iptables -t nat -A OUTPUT -m owner ! --uid-owner burp -p tcp --dport 443 -j DNAT --to-destination 127.0.0.1:443
Стоит отметить, что для возможности запуска Burp Suite, нужно добавить пользователю burp доступ к X-серверу командой:
xhost -si:localuser:burp
1.1.1. Вывод
В первом разделе мы рассмотрели основные способы проксирования трафика через Burp Suite, а именно:
Если никаких проблем, то нам достаточно настройки обычного прокси.
В случае если у используемого ПО нет поддержки прокси в настройках, то мы можем настроить так называемый прозрачный прокси, при этом необходимо изменить разрешения DNS или перенаправить трафик через iptables.
Далее будут рассмотрены методы расшифровки трафика при помощи ключей шифрования
2. Настройка расшифровки трафика при помощи ключей шифрования
При данном методе расшифровки трафика требуется выполнить 3 задачи:
Извлечь ключи шифрования из исследуемой утилиты.
Захватить сетевой трафик.
Расшифровать захваченный трафик, используя извлеченные ключи шифрования.
Задачи захвата и расшифровки трафика выполняет анализатор трафика Wireshark. Согласно странице wiki, существует два метода для расшифровки: используя приватный ключ RSA или сеансовые секреты (per-sess ion secrets).
Метод с сеансовым ключом является универсальным и работает, даже когда генерация общего секрета происходит при помощи протокола Диффи-Хеллмана. Однако, для его применения необходимо постоянно записывать сеансовые секреты в файл. Некоторые программы, например, браузеры Firefox и Chrome, записывают сеансовые секреты по пути, который указан в переменной окружения SSLKEYLOGFILE. Формат логов:
<Label><space><ClientRandom><space><Secret>
, где
<Label>
название секрета.
<ClientRandom>
32 байтное случайное значение, получаемое из сообщения Client Hello, представляется в виде 64 шестнадцатеричных символов.
<Secret>
само значение секрета, формат зависит от названия <Label>
Возможные значения <Label>:
RSA
: 48 bytes for the premaster secret, encoded as 96 hexadecimal characters (removed in NSS 3.34).CLIENT_RANDOM
: 48 bytes for the master secret, encoded as 96 hexadecimal characters (for SSL 3.0, TLS 1.0, 1.1 and 1.2).CLIENT_EARLY_TRAFFIC_SECRET
: the hex-encoded early traffic secret for the client side (for TLS 1.3).CLIENT_HANDSHAKE_TRAFFIC_SECRET
: the hex-encoded handshake traffic secret for the client side (for TLS 1.3).SERVER_HANDSHAKE_TRAFFIC_SECRET
: the hex-encoded handshake traffic secret for the server side (for TLS 1.3).CLIENT_TRAFFIC_SECRET_0
: the first hex-encoded application traffic secret for the client side (for TLS 1.3).SERVER_TRAFFIC_SECRET_0
: the first hex-encoded application traffic secret for the server side (for TLS 1.3).EARLY_EXPORTER_SECRET
: the hex-encoded early exporter secret (for TLS 1.3).EXPORTER_SECRET
: the hex-encoded exporter secret (for TLS 1.3).
Программы, которые не поддерживают логирование сеансовых ключей, следует настраивать индивидуально. К примеру, для java программ можно использовать jSSLKeyLog, extract-tls-secrets.
Если программа использует библиотеку openssl, то извлечь сеансовые ключи можно используя отладчик (например gdb), через LD_PRELOAD или путем добавления в исходный код openssl логирования сенсовых ключей и пересборки пакета. Далее рассмотрим каждый из этих методов в изолированной среде Docker.
2.1. Извлечение ключей через gdb
Для сборки пакета libssl с отладочными символами через apt, нужно добавить в sources.list архивы с исходными кодами и загрузить информацию о пакетах через apt update.
sources.list
deb http://archive.ubuntu.com/ubuntu/ focal main restricted
deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted
deb http://archive.ubuntu.com/ubuntu/ focal universe
deb http://archive.ubuntu.com/ubuntu/ focal-updates universe
deb http://archive.ubuntu.com/ubuntu/ focal multiverse
deb http://archive.ubuntu.com/ubuntu/ focal-updates multiverse
deb http://archive.ubuntu.com/ubuntu/ focal-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu/ focal-security main restricted
deb http://security.ubuntu.com/ubuntu/ focal-security universe
deb http://security.ubuntu.com/ubuntu/ focal-security multiverse
deb-src http://archive.ubuntu.com/ubuntu focal main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu focal-updates main restricted universe multiverse
deb-src http://security.ubuntu.com/ubuntu focal-security main restricted universe multiverse
После этого нужно установить необходимые для сборки зависимости через apt-get build-dep libssl1.1
и начать сборку через команду apt-get --build source libssl1.1
, предварительно установив переменную окружения DEB_BUILD_OPTIONS
в значение debug notest nostrip noopt nocheck
. Теперь нужно установить собранный пакет через команду (название пакета может отличаться) apt-get install --reinstall ./libssl1.1_1.1.1f-1ubuntu2.12_amd64.deb
и перейти в директорию openssl-1.1.1f
, которая была создана при сборке и запустить отладку программы, использующей openssl. В данном случае рассмотрим отладку утилиты curl.
Для автоматизации данных действий создадим в отдельной директории Dockerfile:
Dockerfile
FROM ubuntu:18.04
SHELL ["/bin/bash", "-c"]
COPY ./sources.list /etc/apt/sources.list
ENV TZ=Europe/Moscow
ENV DEB_BUILD_OPTIONS="debug notest nostrip noopt nocheck"
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone\
&& apt-get update\
&& apt-get install vim -y\
&& mkdir -p /sources_build/libssl1.1\
&& cd /sources_build/libssl1.1\
&& apt-get -y build-dep libssl1.1\
&& apt-get --build source libssl1.1\
&& find -maxdepth 1 -name "*openssl*" -type d -execdir mv {} openssl1.1 \;\
&& cd ./openssl1.1\
&& dpkg-buildpackage -b \
&& find ../ -name "*libssl1.1*.deb" -execdir mv {} libssl1.1.deb \;\
&& apt-get install --reinstall --allow-downgrades ../libssl1.1.deb -y\
&& apt-get install curl -y\
&& apt-get install gdb -y\
&& rm -rf /var/lib/apt/lists/*\
&& apt-get clean
В этой же директории поместим указанный ранее sources.list и создадим Docker образ:
docker build -t tls_debug_gdb .
Для того, чтобы в контейнере было возможно полноценно использовать gdb, его нужно запускать со следующими опциями:
docker run -it --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --name ssl_debug_gdb_cont -v /tmp:/tmp tls_debug_gdb
Перед отладкой установим в контейнере переменную окружения SSLKEYLOGFILE
в /tmp/tls.log и запустим отладку.
export SSLKEYLOGFILE=/tmp/tls.log
gdb curl
Если поставить точку остановки на функцию ssl_log_secret
, то можно увидеть названия сеансовых секретов, их значения, совпадающие с теми, которые записаны в /tmp/tls.log (если перевести в единый формат).
Чтобы автоматически записывать ключи, можно написать Python скрипт, к примеру такой:
import gdb
def start():
gdb.Breakpoint("ssl_log_secret")
gdb.execute("run")
while True:
try:
gdb.execute("n")
cl_rd_addr=gdb.parse_and_eval("ssl->s3->client_random").address
cl_rd_data=gdb.selected_inferior().read_memory(cl_rd_addr, 32)
cl_rd_data=''.join([f'{ord(i):02x}' for i in cl_rd_data])
secret_addr=gdb.parse_and_eval("secret").format_string().split()[0]
secret_data=gdb.selected_inferior().read_memory(int(secret_addr,16), 48)
secret_data=''.join([f'{ord(i):02x}' for i in secret_data])
with open("/tmp/key.log",'a') as file_:
file_.write(' '.join([gdb.parse_and_eval("label").string(),cl_rd_data,secret_data]))
file_.write('\n')
gdb.execute("c")
except:
break
Данный скрипт записывает ключи в файл /tmp/key.log
. Для его запуска необходимо сохранить скрипт в файл, например в /root/.gdb/keylog.py
, прописать в /etc/gdbinit
:
python
import sys
sys.path.insert(0, '/root/.gdb')
import keylog
end
И запустить логирование:gdb -batch -ex "py keylog.start()" --args curl https://google.com
2.2. Извлечение ключей через пересборку пакета
Аналогично предыдущему пункту скачиваем исходные коды libssl и добавляем код в тело функции ssl_log_secret
в файле ssl/ssl_lib.c, который осуществляет запись сеансовых секретов в файл, например такой:
FILE *f1 = fopen("/tmp/key.log", "a");
if (f1) {
fprintf(f1, "%s ",label);
for (int n=0;n<sizeof ssl->s3->client_random;n++){
fprintf(f1, "%02x", ssl->s3->client_random[n]);
}
fprintf(f1, " ");
for (int n=0;n<secret_len;n++){
fprintf(f1, "%02x", secret[n]);
}
fprintf(f1, "\n");
fclose(f1);
}
После этого нужно собрать и установить пакет :
dpkg-buildpackage -b
apt-get install --reinstall ../libssl1.1_1.1.1f-1ubuntu2.22_amd64.deb
2.3. Извлечение ключей через LD_PRELOAD
Для извлечения через LD_PRELOAD
можно воспользоваться кодом. Перед компиляцией необходимо либо установить заголовки разработки для libbsl
:
apt-get install libssl-dev
Либо раскомментировать строчки:
//#define NO_OPENSSL_102_SUPPORT
//#define NO_OPENSSL_110_SUPPORT
После компиляции:
cc sslkeylog.c -shared -o libsslkeylog.so -fPIC -ldl
Нужно установить переменные окружения LD_PRELOAD
и SSLKEYLOGFILE
, где в LD_PRELOAD
нужно указать путь до скомпилированной библиотеки, а в SSLKEYLOGFILE
путь до файла, где будут храниться ключи.
2.4. Проверка работы на различных версиях протокола TLS
Для демонстрации работы при использовании различных версий протокола TLS, давайте развернем другой Docker контейнер с сервером Apache, поддерживающим соединение по любой версии TLS. Для включения поддержки различных версий TLS пропишем следующий конфигурационный файл:
Конфигурационный файл 000-default.conf для Apache
<VirtualHost *:443>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
SSLProtocol +TLSv1.3 +TLSv1.2 +TLSv1.1 +TLSv1
SSLCompression off
SSLHonorCipherOrder on
SSLCipherSuite "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA"
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
</VirtualHost>
Для автоматизации развертывания контейнера создадим в отдельной директории Dockerfile:
Dockerfile
FROM ubuntu:18.04
COPY ./000-default.conf /tmp/000-default.conf
RUN apt-get update\
&& apt-get install apache2 -y\
&& cp /tmp/000-default.conf /etc/apache2/sites-enabled/000-default.conf\
&& a2enmod ssl
В этой же директории поместим указанный ранее 000-default.conf и создадим Docker образ:
docker build -t apache_tls .
После этого запустим контейнер:
docker run --rm -t apache_tls bash -c '/etc/init.d/apache2 start;bash'
Теперь, когда настроили сервер apache, перейдем обратно в контейнер ssl_debug_gdb_cont
. Используя утилиту curl будем отправлять запросы на адрес apache сервера, используя разные версии протоколы tls
. Но перед этим нужно изменить конфигурацию openssl
на стороне клиента, чтобы было доступно использование всех версий протокола, используя команду:
sed -i '1iopenssl_conf = default_conf' /usr/lib/ssl/openssl.cnf\
&& sed -i -e '$a[ default_conf ]\
ssl_conf = ssl_sect\
[ssl_sect]\
system_default = ssl_default_sect\
[ssl_default_sect]\
MinProtocol = TLSv1\
CipherString = DEFAULT:@SECLEVEL=1' /usr/lib/ssl/openssl.cnf
Для отправки запросов с разными версиями протокола, воспользуемся следующими командами:
curl --tlsv1.0 --tls-max 1.0 -k https://172.17.0.3 > /dev/null
curl --tlsv1.1 --tls-max 1.1 -k https://172.17.0.3 > /dev/null
curl --tlsv1.2 --tls-max 1.2 -k https://172.17.0.3 > /dev/null
curl --tlsv1.3 --tls-max 1.3 -k https://172.17.0.3 > /dev/null
Чтобы wireshark мог расшифровывать трафик нужно после начала захвата трафика перейти в Edit->Preferences
, после чего выбрать Protocols->TLS
, где в (Pre)-Master-Secret log filename
указать путь до файла с секретами.
Стоит отметить, что при использовании данного метода, можно расшифровывать не только https трафик, но и любой другой, если утилита использует openssl. В качестве примера рассмотрим msmtp, которая хоть и использует по умолчанию gnutls, но позволяет произвести сборку с openssl.
Для этого, аналогично openssl скачаем зависимости и произведем сборку:
apt update
apt-get -y build-dep msmtp
apt-get -y --build source msmtp
Дополнительно установим libssl-dev
:
apt install libssl-dev
После изменим в файле msmtp-1.8.6/debian/rules строку
CONFIGURE_COMMON_OPTS := --with-libgsasl --with-tls=gnutls --with-msptpd
на
CONFIGURE_COMMON_OPTS := --with-libgsasl --with-tls=openssl --with-msptpd
И произведем сборку и установку пакета:
cd msmtp-1.8.6
dpkg-buildpackage -b
apt install ../msmtp-gnome_1.8.6-1_amd64.deb
Чтобы убедиться, что пакет собрался с openssl, можно воспользоваться командой:
ldd `which msmtp` | grep libssl
Теперь осталось настроить msmtp. В качестве примера рассмотрим отправку сообщений через Яндекс. Для этого изменим файл ~/.msmtprc
:
.msmtprc
account default
logfile /var/log/msmtp.log
host smtp.yandex.ru
port 587
from <username>@yandex.ru
keepbcc on
auth on
user <username>@yandex.ru
password <password>
tls on
tls_certcheck off
Стоит отметить, что пароль для приложения нужно предварительно создать
Напишем текст письма в файл email.txt:
email.txt
From: <username>@yandex.ru
To: <another_username>@yandex.ru
Subject: Hello World
Hello world
Теперь письмо можно отправить командой:
cat email.txt | msmtp -a default <another_username>@yandex.ru
Чтобы Wireshark расшифровал сообщения, необходимо добавить в настройках протокола SMTP порт 587.
2.4. Вывод
Тут мы уже рассмотрели чтение трафика через Wireshark и расшифровку при помощи ключей шифрования, то есть, через что, и как именно можно извлекать ключи: gdb, пересборка пакетов, LD_PRELOAD.
Заключение
В результате разбора нескольких способов расшифровки трафика, можем сказать, что при анализе HTTPS трафика, наиболее удобным вариантом является использование перехватывающего прокси, такого как Burp Suite, так как там более удобный интерфейс для анализа содержимого HTTP пакета. В случае, если необходимо исследовать трафик других протоколов, использующих TLS, то расшифровку нужно проводить через Wireshark. Из рассмотренных способов извлечения ключей шифрования, наиболее удобным, на наш взгляд является LD_PRELOAD, так как требует меньшей настройки.
Ссылки
Статьи про WireShark:
https://habr.com/ru/post/204274/ — Wireshark — приручение акулы @sinist3rr)
Статьи, связанные с расшифровкой TLS-трафика:
https://habr.com/ru/company/billing/blog/261301/ — Анализ SSL/TLS трафика в Wireshark @trustedd)
https://habr.com/ru/post/253521/ — Как легко расшифровать TLS-трафик от браузера в Wireshark @ValdikSSS)
Статьи, про работу TLS:
https://habr.com/ru/post/258285/ — Что такое TLS @babayota_kunn)
https://habr.com/ru/post/191954/ — Первые несколько миллисекунд HTTPS соединения @thevar1ablee)
https://tls.dxdt.ru/tls.html — Ключи, шифры, сообщения: как работает TLS
Комментарии (6)
mikaelkg Автор
14.05.2024 09:09+1Если Вы имеете ввиду, что для каждой программы можно перехватить данные перед шифрованием и отправке этих данных по сети, то это действительно так. Однако, это не универсальный способ.
Если имеете ввиду, что можно поставить агента, который будет в роли прозрачного прокси перехватывать весь трафик, при этом логируя все запросы и ответы, то это тоже верно. Такой способ был указан в статье через Burp Suite в случае HTTPS.
hpf
14.05.2024 09:09+1Оставлю это здесь: https://ecapture.cc
mikaelkg Автор
14.05.2024 09:09+1Спасибо. Используемый инструментом метод мы в статье не рассмотрели. Постараемся в будущем дополнить статью
Voevo
14.05.2024 09:09Спасибо за статью, а как быть с двухсторонним TLS? Burp Suite его поддерживает?
mikaelkg Автор
14.05.2024 09:09+1Да, Burp Suite поддерживает использование клиентских сертификатов. Если хотите протестировать можно сделать следующим образом (если нет своего клиентского сертификата и сайта):
Создать сертификат для сервера и клиента, данный процесс хорошо описывается здесь.
Настроить сервер, который будет давать доступ по клиентским сертификатам. Конфигурацию для Apache можно взять здесь.
Перевести клиентский сертификат и ключ в формат pkcs12 командой:
openssl pkcs12 -export -in client.cert.pem -inkey client.key.pem -out client.p12
-
В Burp Suite зайти в Proxy->Proxy Settings. Слева найти Network->TLS. Пролистать до Client TLS certificates и добавить клиентский сертификат.
После этого можно перенаправлять трафик через Burp Suite
Vadziku
Все методы "расшифровки" предназначены для случая локальных клиентов.
А в этом случае проще установить агенты на станции локальных пользователей, которые перехватят все что угодно без всякой расшифровки и не компостировать себе мозги.