Heartbleed — взгляд программиста


Вы слышали об уязвимости Heartbleed в OpenSSL, вам, вероятно, приходилось иметь дело с ее последствиями, но, возможно, вы не знаете, насколько это устаревшая ошибка.

OpenSSL — это всего лишь одна из реализаций протокола, но он очень широко используется, поскольку имеет открытый исходный код, а значит, не требует больших затрат и заслуживает доверия. Это причина того, что вокруг этого так много шума.

Microsoft должна быть искренне счастлива, что она еще не проглотила всю пилюлю с открытым исходным кодом, потому что ее реализация SSL не имеет такого же недостатка — вероятно, у нее есть другие недостатки, но это уже другая история.

Так что же это за ошибка, позволяющая приступить к атаке Heartbleed.

Просто — переполнение буфера.

В простейшем случае переполнение буфера включает предоставление буфера размером x байтов, а затем использование x + 1 или более байтов, то есть чтение или запись конца буфера.

Если вы работаете на языке, который заботится об управлении памятью, вы, вероятно, еще более недоверчивы, что такое может случиться. В конце концов, если вы выделите буфер размером x и попытаетесь получить доступ к элементу x + 1, система будет жаловаться — либо будет выдана ошибка времени выполнения, либо исключение. Что бы ни случилось дальше, вы точно не получите доступа к памяти за пределами последнего элемента буфера. В этом суть управляемого кода. Такие языки, как C и C ++, не управляются в этом смысле, и они позволяют, если разрешить — правильное слово, читать и писать за пределами конца буфера.

Почему спросите вы?

Простой ответ заключается в том, что код без проверок переполнения буфера выполняется быстрее, и потому, что иногда «умный», если «умный» — правильное слово, программисты могут эффективно его использовать.

Однако в этом случае управляемый код не обязательно спасет положение, потому что недостаток в коде был на логическом уровне, а не на уровне реализации. То есть ошибка Heartbleed — это не простое переполнение буфера.

В случае ошибки SSL все связано с протоколом подтверждения. Чтобы поддерживать SSL-соединение открытым, клиенты могут отправить контрольный пакет, который в принципе не несет данных и действует как сигнал «Я все еще здесь, не отключайтесь». Однако процедура контрольного сообщения принимает данные, отправленные клиентом, сохраняет их в буфере, а затем отправляет данные обратно клиенту — почему это делается, не очевидно.

Возможно, первой ошибкой была отправка данных обратно в ответ на тепловой удар.

Теперь вы можете увидеть, что здесь может пойти не так.

Процедура тактового сигнала использует заявленный клиентом размер пакета для предоставления буфера и отправки буфера обратно — без проверки того, что предоставленное значение действительно соответствует размеру пакета.

Таким образом, злоумышленник отправляет 1-байтовый пакет, но утверждает, что его размер составляет 64 КБ. Программа выделяет буфер размером 64 КБ, копирует в него один байт, а затем копирует весь буфер обратно как пакет возврата. Злоумышленник получает обратно свой один байт и все, что было в остальной части выделенного буфера до его выделения, то есть отслеживаемых данных на сумму почти 64 КБ.

Поскольку память выделяется в куче с помощью процедуры c malloc, существует даже возможность сканирования больших областей памяти путем выполнения нескольких запросов, которые получают буферы в разных областях растущей кучи.

Сможет ли управляемый язык защитить нас от ошибки?

Да, но только если он инициализировал массивы до значений по умолчанию.

В этом случае проблема заключается не в простом переполнении буфера, потому что границы буфера на самом деле не нарушены — программа запрашивает буфер размером 64 КБ и использует буфер 64 КБ. Проблема в том, что только первый байт устанавливается на новое значение, а буфер не инициализируется, чтобы стереть любые данные, которые могли быть сохранены в области памяти, которую он занимает.

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

Управляемые языки, C #, Java и т. Д., Как правило, инициализируют массивы значением по умолчанию для типа, но правила для этого иногда могут быть сложными, когда вы отказываетесь от простых массивов статического типа, поэтому даже здесь вы должны быть осторожны.

Если вам нужно больше узнать об ошибке Heartbleed — в каких именно реализациях SSL она находится и как ее исправить — посетите Heartbleed.com или один из многих других сайтов, предлагающих советы.

Что кажется весьма вероятным, так это то, что есть другие похожие ошибки, которые есть в других «хорошо продуманных» реализациях протоколов безопасности. Я не был бы слишком самодовольным, если бы я был Microsoft или отвечал за любую другую реализацию SSL.


Добавить комментарий