Ошибка Heartbleed: Почему даже возможно обработать запрос Heartbeat перед доставкой полезной нагрузки?
Во-первых, я не программист на C, и кодовая база OpenSSL огромна, поэтому простите меня за вопрос, на который я, вероятно, смогу найти ответ, учитывая, что у меня было время и умение копаться в коде.
TLS работает через TCP из того, что я могу сказать. TCP ориентирован на поток, поэтому нет способа узнать, когда сообщение было доставлено. Вы должны знать заранее, как долго должно быть входящее сообщение или иметь разделитель для сканирования.
Имея это в виду, как OpenSSL может обрабатывать контрольные запросы до получения полной полезной нагрузки?
Если OpenSSL только начинает обрабатывать первый блок данных, которые он читает из сокета TCP после получения длины полезной нагрузки, тогда OpenSSL будет казаться не просто небезопасным, но сломанным при нормальной работе. Поскольку максимальный размер сегмента TCP составляет 536 байт, любая полезная нагрузка, превышающая его, будет охватывать несколько сегментов TCP и, следовательно, потенциально может охватывать несколько операций чтения из сокетов.
Итак, вопрос: как / почему OpenSSL может начать обработку сообщения, которое еще не доставлено?
2 ответа
Это определение пакета пульса.
struct {
HeartbeatMessageType type;
uint16 payload_length;
opaque payload[HeartbeatMessage.payload_length];
opaque padding[padding_length];
} HeartbeatMessage;
Неправильная обработка payload_length
поле это то, что вызвало ошибку кровотечения.
Однако весь этот пакет сам инкапсулирован в другую запись, которая имеет собственную длину полезной нагрузки, примерно так:
struct {
ContentType type;
ProtocolVersion version;
uint16 length;
opaque fragment[TLSPlaintext.length];
} TLSPlaintext;
Структура HeartbeatMessage находится внутри выше fragment
,
Таким образом, один целый TLS-пакет может быть обработан, когда данные в соответствии с length
поле получено, но во внутреннем сообщении Heartbeat openssl не смог проверить его payload_length
,
Вот снимок экрана с захватом пакета, на котором вы видите, что внешняя длина 3 указывает длину "пакета", а внутренняя (неправильная) длина полезной нагрузки 16384 является причиной эксплойта, так как openssl не смог проверить это в отношении фактическая полученная длина пакета.
Конечно, при обработке length
поле этой внешней записи, вы действительно хотите убедиться, что вы действительно получили length
данные перед началом обработки / анализа содержимого пакета.
Также обратите внимание, что нет особой корреляции между чтениями сокетов и сегментами TCP, при одном чтении из сокетов можно читать много сегментов или только часть сегмента. Для приложения TCP - это просто поток байтов, и одно чтение сокета может считывать до половины длины поля одного пакета TLSPlaintext или может считывать несколько целых пакетов TLSPlaintext.
Статья в Википедии Heartbleed довольно хорошо объясняет эксплойт. Перефразируя, RFC 6520 является расширением протокола TLS для сообщения "Heartbeat Request" (своего рода механизм поддержания активности). Запрос состоит из поля длиной 16 бит и сообщения для сопоставления, и предполагается, что ответ повторяет предоставленное сообщение. В реализации OpenSSL есть ошибка, которая не выполняет проверку границ. Он принимает это поле длины по номиналу, не проверяя, читает ли оно что-то, чего не должно быть (т. Е. Выходит за границы, указанные в записи SSL). Эксплойт возникает, когда "Запрос сердцебиения" искажен небольшим сообщением, но большим значением в поле длины. Это позволяет злонамеренному клиенту попытаться прочитать с сервера информацию, которая в противном случае не была бы прочитана (эта информация вернется в ответ). То, что на самом деле содержится в этой информации, зависит от того, как вещи хранятся в памяти на сервере, но потенциал для чтения конфиденциальной информации считается катастрофическим для OpenSSL, который должен обеспечить безопасную платформу.