[Перевод] Поломанные VPN, 2038 год и сертификаты с истёкшим сто лет назад сроком
7 мин Блог компании RUVDS.com *nix *Информационная безопасность *Сетевое оборудование Перевод Автор оригинала: Thomas Claburn
В конце 2010 года Зимми (псевдоним) работал в ИТ-поддержке компании, разрабатывавшей VPN-устройства и операционную систему для них. В понедельник ему позвонил клиент (розничный продавец продукции из США), рассказавший, что в выходные его VPN-оборудование перестало работать.
После изучения отчёта Зимми решил, что проблема возникла в результате сбоя валидации сертификата, о чём он и написал в недавнем посте в Mastodon.
«VPN-устройства моего работодателя управлялись через центральный сервер. На этом сервере работал собственный центр сертификации (CA), который использовался для подписывания сертификатов всех устройств. Конечные точки VPN использовали их для аутентификации на сервере управления (например, при отправке логов) и друг между другом (в основном для VPN). CA — это фундаментальная часть ПО управления».
Зимми сообщил, что предпочёл бы оставить анонимным себя, компанию и клиента.
Валидация оказывалась ошибочной, потому что система не могла проверить certificate revocation list (CRL) — список цифровых сертификатов, отозванных выпустившим их центром.
«Эти устройства аутентифицировали друг друга при помощи сертификатов, похожих на те, которые применяются для HTTPS, но подписанные частным центром сертификации. У каждого клиента был для этого свой CA. В процессе валидации сертификатов CA проверял, не отозван ли сертификат. Валидацию не удавалось выполнить, потому что VPN-устройство не могло скачать certificate revocation list (CRL), чтобы убедиться, что сертификата другого устройства нет в списке. Почему оно не могло скачать CRL?»
При изучении лога операций с сертификатами VPN-устройства выяснилось, что CRL оказался слишком велик для скачивания.
«ПО, которое занимается валидацией, распределяет для хранения CRL 512 КиБ пространства, но этого всё равно недостаточно? Этот CA применяется крайне неактивно, так что мы вряд ли можем упереться в ограничение в течение своей жизни. Что такого произошло с CRL? Из-за чего он так быстро упёрся в предельный размер?»
В статье [PDF] 2015 года об отзыве сертификатов, опубликованной исследователями Мэрилендского университета, говорится, что размер CRL для медианного сертификата равен 51 КБ, и что размер половины всех CRL меньше 900 Б.
Изучаемый Зимми CRL был размером почти 1 МБ; это было вызвано тем, что CA раз в секунду постоянно отзывал и выпускал каждый подписанный им сертификат. CRL разрастался со скоростью больше 250 КиБ в день, что, по оценкам Зимми, означало, что за день размер увеличивался как за целый век.
«К счастью, у центра сертификации был собственный лог операций с сертификатами», — вспоминает Зимми. «В этом логе фиксируется каждый момент отзыва сертификата центром, подписывания нового и выполнения некоторых других операций. Изучив самые последние записи, я увидел, что процесс CA просыпается, решает, что срок собственного сертификата CA истёк, затем отзывает и заново выпускает каждый подписанный им сертификат. Я посмотрел предыдущие записи и увидел, что тот же процесс выполнялся одной секундой ранее. И ещё за секунду до этого».
По словам Зимми, у этих цифровых сертификатов было две даты: notBefore — дата, до которой они не валидны, и notAfter, после которой их срок истекает.
«Посмотрев на сертификат CA, я увидел, что он недействителен после 1910 года. Это странно, но, по крайней мере, объясняет, почему CA говорит, что срок его сертификата истёк. Но потом он выпускает себе сертификат на замену, срок которого тоже истекает в 1910 году. Это чрезвычайно странно. Он должен заново выпускать сертификат с новыми датами».
Зимми обратил внимание на дату notBefore. «Обычно она никого не волнует, потому что крайне редко можно получить сертификаты, которые пока не валидны», — объясняет он.
«Но этот не был валиден до какой-то даты в 2037 году. Это как? Он не валиден до 2037 года и не валиден после 1910 года? Это значит, что он никогда не будет действительным. Как такое могло произойти?»
▍ Ошибочные расчёты
Работающие с сетями читатели уже могли догадаться, что какая-то ошибка закралась в расчёты времени.
«Когда-то давно разработчики UNIX решили отсчитывать время в виде знакового 32-битного счётчика количества секунд после полуночи 1 января 1970 года. В Linux это решение сохранилось ради совместимости. „Знаковый“ означает, что число может быть отрицательным, чтобы можно было задавать значения времени до 1970 года. 32 битов достаточно для задания чуть менее чем 4,3 миллиарда секунд. Использование знака означает, что можно получить примерно 2,1 миллиарда секунд до 1970 года и примерно 2,1 миллиарда после. 2,1 миллиарда секунд — это чуть меньше 25 тысяч дней, или чуть больше 68 лет».
Через 68 лет после 1970 года будет 2038 год. Все, кто имеют дело со временем UNIX, знают о 2038 годе, у которого даже есть собственный веб-сайт, предупреждающий о надвигающихся проблемах.
«Последняя секунда, которую можно задать таким образом — это 03:14:07 UTC 19 января 2038 года. После этой секунды инкремент таймера „переполняет“ значение и превращается из большого положительного числа в большое отрицательное. Следующей секундой, которую может описать этот счётчик, будет 20:45:52 UTC 13 декабря 1901 года. Это называется проблемой 2038 года».
Зная, что центр сертификации подписывает свой срок, чтобы тот был валидным в течение десяти лет, Зимми, пришёл к выводу, что при вычислении даты истечения срока он столкнулся с проблемой 2038 года. Это предполагало наличие бага в библиотеке вычислений с датами или в коде, реализующем эту библиотеку. Но всё равно оставался вопрос, почему центр обновлял сертификаты с теми же самыми датами.
Зимми изучил код автоматического обновления CA и убедился, что тот после истечения срока сертификата не выпускает его заново с более ранней датой начала.
«Это логично при обычной работе, но не позволяет исправить странные ситуации наподобие той, с которой столкнулся один из моих клиентов. Ещё один баг. Почему он вообще подписал сертификат, не валидный до 2037 года?»
Изучив длинный лог операций CA, он обнаружил метку системной даты в 2037 году с записью о том, что CA обновляет себя.
«Теперь сертификат, срок которого начинается в 2037 году, выглядел логично, если не учитывать тот немаловажный факт, что на дворе был 2010-й, а не 2037 год. Почему же в логе операций CA появились записи из 2037 года?»
Клиент сообщил Зимми, что не устанавливал время на 2037 год. «Однако он использовал NTP, который синхронизируется с сервером времени каждые 120 секунд». Network Time Protocol (NTP) применяется для синхронизации часов компьютера с сетевым сигналом времени.
«Я начал изучать логи NTP клиента. В большинстве записей клиент подстраивал часы, меняя значение на небольшую долю секунды вперёд или назад. Я отфильтровал все записи, изменения в которых начинались с -0. или с 0., и обнаружил гораздо более крупные изменения. На десятки тысяч секунд. На сотни тысяч. На миллионы. Я обнаружил одно изменение примерно на 4 миллиарда секунд, затем снова на миллионы или сотни тысяч секунд за раз».
▍ Машина времени
По словам Зимми, система считала, что NTP-сервер устанавливал время на 2010, 2019, 2028, 2037, 1907, 1918 годы и так далее, пока не возвращался обратно в настоящее.
«NTP-клиент в ОС очень плох, он позволяет вносить изменения времени гораздо больше разумного. В CA работала предоставленная моим работодателем ОС, так что у нас был собственный NTP-клиент. Третий баг за день. Ну, по крайней мере, мы знаем, почему центр сертификации думал, что на дворе 2037 год!»
Но всё равно оставался вопрос, почему NTP-клиент скакал по всему доступному пространству меток времени.
«Спустя несколько дней это повторилось, и мы перехватили пакет, доказывающий, что он выдаёт фальшивые метки времени. К сожалению NTP-сервер был выпущен другой компанией, так что мы могли лишь строить гипотезы. Думаю, это была ошибка с типом значения в C».
Зимми рассказал следующее: «В языке C есть концепция знаковых и беззнаковых целых чисел, но разделение между ними крайне слабое. Можно попросить систему взять знаковое целое значение и сохранить его куда-нибудь, после чего случайно интерпретировать его как беззнаковое значение. Для положительных чисел это ничего не меняет, однако становится проблемой для отрицательных чисел. По множеству причин они хранятся в памяти в виде дополнительного кода. Когда вы сохраняете значение -1 в виде 32-битного целого числа со знаком, то оно представляется в памяти как 0xFFFFFFFF. Если интерпретировать его как беззнаковое целое, то мы получим значение 4294967295».
По словам Зимми, NTP-сервер использовал для прослушивания источника сигнала времени радиоприёмник.
«Такими приёмниками обычно бывают GPS-приёмники, но в то время сотовые сети CDMA тоже передавали достаточно точную информацию о времени. Подозреваю, что у NTP-сервера были поломанные внутренние часы, работавшие очень быстро. Ещё я думаю, что он терял радиосигнал на время, достаточное для того, чтобы внутренние часы как минимум на секунду опережали время, полученное от приёмника. Наверно, когда сервер получал радиосигнал, он вычитал время внутренних часов из полученного по радио времени, чтобы понять, насколько велика разница. Если я прав, то получалось небольшое отрицательное число. Думаю, при вычислении того, насколько внутренние часы отстают, оно обрабатывалось как беззнаковое целое и это маленькое отрицательное число превращалось в ОГРОМНОЕ положительное. Из-за этого сервер максимально быстро скакал вперёд на 4,29 миллиарда секунд».
«По моему опыту, самое необычное поведение всегда вызывается очень незначительными ошибками. И эта небольшая ошибка могла объяснить такое поведение».
В своём посте Зимми написал, что восстановить работу было сложно. «Из-за того, что VPN-соединения не работали, клиенту пришлось отправить кого-то физически обойти сотню мест и сделать так, чтобы каждое из устройств доверяло новому CA. И прежде, чем мы устранили все проблемы, такое произошло несколько раз».
Мы спросили у Зимми, почему, по его мнению, эта история вызвала большой интерес у пользователей Mastodon.
«Люди обожают загадки. Мы хотим понимать. Я люблю рассказывать эту историю, потому что она показывает, что маленькая проблема на самом деле может быть больше, чем вы думаете, и что большие, непонятные проблемы тоже можно проанализировать и понять. Мне кажется, это близко многим людям».
А ещё когда загадка наконец решена, мы получаем большое удовольствие.
«Обычно я начинаю устранение неполадок с того, что спрашиваю систему о её действиях. Перехватываю пакеты, заглядываю в логи и так далее. Проделав так несколько раз, я выдвигаю гипотезу о причине и начинаю тестировать свою гипотезу. Основа научного метода. Большинство гипотез легко проверить. Когда они оказываются ошибочными, я двигаюсь дальше. Так я начинаю сужать диапазон возможного, и когда гипотезы оказываются истинными, это очень волнительный момент. Делать предположения и доказывать их правоту невероятно приятно».
Зимми рассказал, что подобные проблемы с реализациями NTP встречаются довольно часто.
«Думаю, самое близкое к этому, что я видел за последние десять лет — это баг gpsd, о котором писали в конце 2021 года. Разумеется, самые неприятные баги и проблемы с реализацией выжидают, выбирая время, когда мы их меньше всего ожидаем. Когда мы приблизимся к 2038 году, я точно буду внимательно выискивать проблемы с NTP».
Скидки, итоги розыгрышей и новости о спутнике RUVDS — в нашем Telegram-канале 🚀
Теги:
- ruvds_переводы
- unix time
- ntp
- vpn
- операционные системы
- синхронизация времени
- проблема 2038 года
Хабы:
- Блог компании RUVDS.com
- *nix
- Информационная безопасность
- Сетевое оборудование