
Ошибка Heartbleed `CVE-2014-0160` - это серьезный дефект реализации в библиотеке OpenSSL, который позволяет злоумышленникам красть данные из памяти сервера-жертвы. Содержимое украденных данных зависит от того, что находится в памяти сервера. Это могут быть закрытые ключи, ключи сеансов TLS, имена пользователей, пароли, кредитные карты и т.д. Уязвимость находится в реализации протокола Heartbeat, который используется SSL/TLS для поддержания соединения.
Ошибка Heartbleed CVE-2014-0160 — это серьезный дефект реализации в библиотеке OpenSSL, который позволяет злоумышленникам красть данные из памяти сервера-жертвы. Содержимое украденных данных зависит от того, что находится в памяти сервера. Это могут быть закрытые ключи, ключи сессий TLS, имена пользователей, пароли, данные кредитных карт и т. д. Уязвимость находится в реализации протокола Heartbeat, который используется SSL/TLS для поддержания соединения активным.
Затронутый диапазон версий OpenSSL — от 1.0.1 до 1.0.1f. Версия на виртуальной машине Ubuntu — 1.0.1.
Атака Heartbleed основана на запросе Heartbeat. Этот запрос просто отправляет некоторые данные на сервер, и сервер копирует данные в свой ответный пакет, так что все данные возвращаются обратно. В нормальном случае предположим, что запрос содержит 3 байта данных "ABC", поэтому поле длины имеет значение 3. Сервер помещает данные в память и копирует 3 байта с начала данных в свой ответный пакет. В сценарии атаки запрос может содержать 3 байта данных, но поле длины может указывать 1003. Когда сервер формирует свой ответный пакет, он копирует, начиная с начала данных (т.е. "ABC"), но копирует 1003 байта вместо 3. Эти дополнительные 1000 байт, очевидно, не берутся из запроса; они берутся из приватной памяти сервера и могут содержать информацию других пользователей, секретные ключи, пароли и т. д.
Далее измените поле длины запроса. Сначала давайте разберемся, как формируется ответный пакет Heartbeat на основе рисунка выше. Когда приходит запрос Heartbeat, сервер анализирует пакет, чтобы получить полезную нагрузку и значение Payload_length (выделено на рисунке). Здесь полезная нагрузка — это всего лишь 3-байтовая строка , а равно 3. Программа сервера слепо берет это значение длины из запроса. Затем она формирует ответный пакет, указывая на память, где хранится , и копирует байт в полезную нагрузку ответа. Таким образом, ответный пакет будет содержать 3-байтовую строку .
"ABC"Payload_length"ABC"Payload_length"ABC"Далее запустите атаку Heartbleed, как показано на рисунке ниже. Оставьте ту же полезную нагрузку (3 байта), но установите поле Payload_length в 1003. Сервер снова слепо возьмет это значение Payload_length при формировании ответного пакета. На этот раз программа сервера укажет на строку "ABC" и скопирует 1003 байта из памяти в ответный пакет в качестве полезной нагрузки. Помимо строки "ABC", дополнительные 1000 байт копируются в ответный пакет, и это могут быть любые данные из памяти, например, секретная активность, информация журналирования, пароли и т. д.
Код атаки позволяет изменять значение Payload_length. По умолчанию оно установлено довольно большим (0x4000), но его можно уменьшить.
Самый простой способ исправить уязвимость Heartbleed — обновить библиотеку OpenSSL до последней версии. Однако цель состоит в том, чтобы исправить уязвимость через исходный код.
Формат запроса/ответа Heartbeat
struct {
HeartbeatMessageType type; // 1 байт: запрос или ответ
uint16 payload_length; // 2 байта: длина полезной нагрузки
opaque payload[HeartbeatMessage.payload_length];
opaque padding[padding_length];
} HeartbeatMessage;
Первое поле (1 байт) пакета — это информация о типе, второе поле (2 байта) — длина полезной нагрузки, за которым следуют сама полезная нагрузка и дополнения. Размер полезной нагрузки должен совпадать со значением в поле длины, но в сценарии атаки длина может быть установлена на другое значение. Следующий фрагмент кода показывает, как сервер копирует данные из запроса в ответный пакет.
Обработка запроса Heartbeat и генерация ответного пакета
/* Выделить память для ответа, размер: 1 байт
* тип сообщения, плюс 2 байта длины полезной нагрузки, плюс
* полезная нагрузка, плюс дополнение
*/
unsigned int payload;
unsigned int padding = 16; /* Использовать минимальное дополнение */
// Сначала прочитать поле типа
hbtype = *p++; /* После этой инструкции указатель
* p будет указывать на поле payload_length */
// Прочитать поле payload_length из запроса
n2s(p, payload); /* Функция n2s(p, payload) читает 16 бит
* из указателя p и сохраняет значение
* в переменную INT "payload". */
pl = p; // pl указывает на начало содержимого полезной нагрузки
if (hbtype == TLS1_HB_REQUEST)
{
unsigned char *buffer, *bp;
int r;
/* Выделить память для ответа, размер: 1 байт
* тип сообщения, плюс 2 байта длины полезной нагрузки, плюс
* полезная нагрузка, плюс дополнение
*/
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;
// Ввести тип ответа, длину и скопировать полезную нагрузку *bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
// скопировать полезную нагрузку
memcpy(bp, pl, payload); /* pl — указатель, который
* указывает на начало
* содержимого полезной нагрузки */
bp += payload;
// Случайное дополнение
RAND_pseudo_bytes(bp, padding);
// Эта функция скопирует 3+payload+padding байт
// из буфера и поместит их в ответный пакет heartbeat,
// который будет отправлен обратно клиенту, отправившему запрос.
OPENSSL_free(buffer);
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
}
Уязвимость кроется здесь
// скопировать полезную нагрузку
memcpy(bp, pl, payload);
Отсутствует проверка, является ли pl допустимым. Поэтому может произойти нарушение памяти.
Исправления:
memcpy()Исправления были внесены на виртуальной машине, но не показаны в этом репозитории.
Спасибо за интерес, этот проект был увлекательным и познавательным!