ЭЦП в Казахстане — общее благо или находка для хакера?
Привет Хабр! Я занимаюсь тестированием на проникновение и сетевой безопасностью в Казахстане. В свободное время занимаюсь поиском уязвимостей, а так же публикую небольшие статьи тут и на Medium.
В данной статье хочу рассказать о небольшом исследовании безопасности касающегося использования электронной цифровой подписи (ЭЦП) на казахстанских ресурсах. В ней вы узнаете о последствиях некорректной реализации проверки ЭЦП в информационных системах в масштабах страны. Техническое описание и инструменты для выявления такой уязвимости прилагаются. Будет актуально всем, кто работает с похожими системами, а казахстанским компаниям и разработчикам — обязательно к прочтению.
Предыстория
Впервые взглянуть на электронную цифровую подпись (ЭЦП) с технической точки зрения мне довелось осенью 2023 года во время проекта по тестированию на проникновение крупной государственной организации. В одной из ее информационных систем (ИС) была возможность использовать ЭЦП для входа в личный кабинет, с NCALayer в качестве посредника.
Что такое ЭЦП и как ее использовать в общих чертах и в контексте Республики Казахстан кратко описано тут и тут. Но самое важное, что ЭЦП позволяет не только аутентифицировать ее владельца в различных ИС, она так же равнозначна собственноручной подписи и имеет такую же юридическую силу (статья 10, закона об ЭЦП РК).
В этой ИС использовалась подпись в формате XML-Dsig, как указано в примере на скриншоте ниже:
Здесь можно выделить 3 элемента XML:
DigestValue - хэш подписываемых данных;
SignatureValue – сама подпись;
X509Certificate – сертификат пользователя с открытым ключом.
Используя перехватчик в Burp Suite, я обнаружил, что запрос на аутентификацию был успешным даже с пустыми значениями первых двух элементов в подписи, а значит можно было войти в систему, имея лишь открытый ключ и сертификат, что противоречило принципу использования ЭЦП и уже не являлось подписью.
Однако, я пошел дальше и понял, что подпись самого сертификата так же не проверялась, а значит я мог изменять любые значения в нем, в том числе и ИИН пользователя.
Сам сертификат X.509 — это бинарный файл, кодированный с помощью Distinguished Encoding Rules (DER) и сериализованный в ASN.1. Править его руками в HEX-редакторе занятие непростое: при изменении длины строки вся структура смещается и нужно менять кучу значений во всей структуре. Для этих целей существует специальное ПО, например ASN.1 Editor.
У сертифката есть набор свойств: серийный номер, алгоритм подписи, информация об издателе и владельце, срок действия, открытый ключ, цифровая подпись самого сертифката для его проверки и прочее. Для идентифкации свойств используятся Object identifier (OID).
Например, для хранения ИИН владельша используется стандарнтый OID 2.5.4.5 (SerialNumber), 2.5.4.3 (CN) для имени и фамилии. Другие идентификаторы описаны в правилах выдачи регистрационных свидетельств национальным удостоверяющим центром РК.
Вот пример структуры сертификата физического лица, выданного НУЦ РК (значения изменены):
После изменения сертификата и упаковки его обратно в контейнер PKCS#12 (файл *.p12) NCALayer отказывался принимать его, что предсказуемо, ведь подпись сертификата теперь не может пройти проверку.
Хорошо, что количество цифр в ИИН фиксировано и эти 12 байт в бинарном виде сертификата можно поменять “налету”, не меняя структуру данных ASN.1. Что я и сделал в первом PoC для этой ИС: подменял ИИН в сертификате, кодировал его в BASE64, отправлял запрос на сервер с модифицированной подписью XML-Dsig, а в ответ получал идентификатор сессии.
Так я впервые обошел аутентификацию на основе ЭЦП. Забегая вперед, хочется сказать, что это была не худшая реализация работы с ЭЦП.
Больше уязвимостей богу уязвимостей
Исходя из опыта, я был уверен, что другие разработчики при работе с ЭЦП будут допускать аналогичные ошибки. А учитывая растущую популярность использования ЭЦП в различных организациях, таких систем должно быть немало.
Используя Google-дорки, список авторизованных ИС для работы с ЭЦП и прочие порталы со списком популярных в РК сервисов для граждан, удалось найти больше 90 разных ИС. Однако проверить удалось только 80 из них. Остальные либо не работали, либо требовали закрытой регистрации.
В итоге 52 из 80 проверенных ИС оказались уязвимы и позволяли обойти механизм аутентификации ввиду отсутствия проверки сертификата пользователя. Уточню, что некоторые ресурсы использовали ЭЦП как один из факторов, и в этом случае риск был не так критичен.
В этом списке не учитывались ИС собщим бэкендом/провайдером аутентификации, если учитывать каждую, то можно смело увеличить этот список в двое.
Масштаб проблемы
Большинство уязвимых ИС принадлежат или созданы для государственных или квазигосударственных организаций. Часть из них принадлежала коммерческим организациям.
Например, были найдены 4 системы документооборота, с которых работают крупнейшие организации страны, в том числе государственные органы (от 5 до 100 тысяч организаций (по данным официальных сайтов). Данные системы предоставляли доступ к платформе SaaS/PaaS, что позволяло (в теории) получить доступ к личным кабинетам клиентов, а в некоторых случаях и к административной части системы. В том числе была найдена система документооборота on-premises с множеством инстансов в РК.
Одна из проверенных ИС принадлежала банку, хотя и не была связана с ДБО. А также были ИС и порталы, вязанные со здравоохранением, образованием и социальными услугами для населения.
На одном из таких порталов мне удалось войти от имени пользователя с “очень большими“ привилегиями и доступом к персональным данными родителей и их детей общим количеством более миллиона записей. Не лучшая идея — делать административный интерфейс с такими привилегиями с доступом из публичной сети.
Ну и конечно же портал с электронными петициями, где также используется ЭЦП и из-за отсутствия нормальной проверки можно накрутить голоса. Вот ссылка на новость на inbusiness.kz.
Все детали уязвимостей были отправлены на Bug Bounty платформу Tumar. И тут надо сказать спасибо команде ЦАРКА за их труд, ибо общение с владельцами ИС — это совершенно неблагодарное занятие.
Ситуацию усугубляет тот факт, что по некоторым ИС отчеты были отправлены еще в феврале, а устранены были единицы и даже МЦРИАП (Министерство цифрового развития и аэрокосмической промышленности) ничего не может с этим поделать.
Рутина
Иногда найти уязвимость бывает проще, чем эксплуатировать. Перехватить запрос из браузера, поменять пару символов в сертификате или подписи не трудно. Но почти всегда нужно подменять другие значения в сертификате: ИИН/БИН, ФИО, email и т.д. Модифицированный сертификат в NCALayer использовать нельзя, да и писать огромную инструкцию для PoC с использованием кучи утилит не вариант. Такой отчет будут проверять очень долго и не факт, что примут, поэтому для каждой ИС нужно было писать свой скрипт для подтверждения концепции.
На большей части ИС проверялась подпись данных, но не сертификат, пришлось разбираться как подписать данные на Python в формате XML-Dsig, CMS и CAdES. Пришлось также разбираться как модифицировать сертификат, разбирать и собирать заново структуру ASN.1, что с моим уровнем владения Python было непросто.
Ситуацию осложняло и то, что разработчики в некоторых ИС радовали изобретательностью. В некоторых ИС от одного разработчика был общий бэкенд аутентификации, с несколькими запросами и редиректами на разные хосты, да и формат данных был везде разный. На одной из ИС вообще была обфускация отправляемых данных и самого JS-скрипта для их отправки!
В общем требовалось много времени на PoC для каждой ИС, а учитывая список из 40 штук (на тот момент) руки начали опускаться. Пока в голову не пришла отличная идея.
Fake Certification Authority Layer
Практически на всех найденных ИС (за некоторым исключением) вся работа с криптографией реализована на стороне NCALayer, именно для этого он и создан. А то, как данные передаются от фронтенда к бэкенду особого значения не имеет. Осталось сымитировать работу NCALayer, отправив браузеру модифицированный сертификат или подпись. Звучит просто.
Для получения официального SDK, надо подписывать NDA и другие бумаги с НУЦ РК, что не выглядело разумной идеей. На официальном форуме (старом и новом), как и в репозитории на GitHub, информации по работе с NCALayer было мало. Буквально крохи информации.
Так что в ход пошли DevTools и Burp для анализ общения браузера и NCALayer. Еще очень помог ресурс с интерактивной документацией по KAZTOKEN mobile. Это не официальная документация, но очень близко кто тому-что было по факту.
Все общение сводилось к обмену сообщениями в формате JSON внутри WebSocket’a, и в обобщенном виде оно выглядело так:
Если не вдаваться в детали, то браузер сначала посылает запрос о статусе и получает в ответ готовность и версию, затем отправляет запрос на подписание данных.
В этом запросе указывается модуль для подписи, метод, сами данные для подписи и другие параметры. А в ответ получает цифровую подпись в запрошенном формате, либо сообщение об ошибке. Есть еще много нюансов, но они сейчас не важны.
Самих модулей и их методов много, но реализованы были лишь те, что попадались в проверяемых ИС.
Скрипт FCALayer.py получился довольно компактным, 1000+ строк, половина из которых комментарии. На данный момент в скрипте реализовано 17 методов в 4-х модулях и доступен следующий функционал:
Имитация работы NCALayer через WebSocket;
Подпись данных в различных форматах — XML‑Dsig/CMS/CAdES;
Создание не валидного сертификата для физ и юр. лиц с нужными свойствами при запуске по шаблону:
Сертификат физ. лица для аутентификации (AUTH_RSA256_*.p12);
Сертификат физ. лица для подписи (RSA256_*.p12);
Сертификат юр. лица для аутентификации (AUTH_RSA256_*.p12);
Использование валидного сертификата и ключа из файла PKCS#12 (.p12);
Установка задержки отправки подписи для проверки истечения токена аутентификации в ИС;
Намеренная поломка цифровой подписи данных для проверки ее валидации на ИС.
В некоторых ИС для аутентификации требовался контейнер с ключами, а криптографические функции были реализованы на JavaScript. Некоторые ИС для аутентификации просто отправляли сертификат, без ЭЦП. В одной так и вовсе контейнер с ключами отправлялся на сервер, разумеется, перед этим спросив пользователя пароль от него (исправлено владельцем после отправки отчета).
А разработчики одной страховой компании совсем уж не стали заморачиваться со всей этой криптографией и решили отправлять только ИИН, открытым текстом без пароля и каких-либо проверок.
По этим причинам был создан еще один скрипт — make‑p12.py. Он просто генерирует сертификат с нужными свойствами и закрытый ключ, сохраняя все в контейнер PKCS#12.
Более подробную информацию можно найти в репозитории FCALayer (доступ быдет открыт позже).
UPD: Доступ открыт.
Почему так случилось и что с этим делать
Я не разработчик и не эксперт в криптографии и могу лишь предполагать о причинах таких проблем с реализацией работы ЭЦП. Также позволю себе несколько общих рекомендаций.
Почему так случилось:
В ТЗ не указали проверку ЭЦП и сертификата, хотя это прямо прописано в правилах проверки подлинности ЭЦП и в руководстве для ИС НУЦ РК;
Официальный SDK есть не для всех языков. Я видел много сообщений на форуме на эту тему. И вероятно в некоторых ИС работа с ЭЦП была реализована «на коленках»;
Возможно, SDK позволяет получить поля сертификата без его валидации;
Непонимание принципов инфраструктуры открытых ключей и ЭЦП у отдельных разработчиков.
Что с этим делать:
Использовать официальный SDK или проверенные и надежные библиотеки для криптографии;
Делать все проверки согласно правилам проверки подлинности ЭЦП, так как это ответственность владельцев ИС;
Повышать осведомленность разработчиков и людей принимающих решения об угрозах ИБ;
Провести аудит ИС, работающих с ЭЦП используя FCALayer;
Ну и сигнатурная защита, о ней чуть ниже.
Сигнатурная защита подойдет как временное решение. Вы легко можете написать регулярные выражение для текущей реализации скрипта. Однако, специалист обойдет такую защиту, немного погрузившись в проблему. А поскольку сертификат не проверяется, в нем можно изменить любую часть и вашему WAF/IPS не за что будет зацепиться, если только там нельзя использовать скрипты.
Собственно, такая попытка была со стороны одного SOC, но небольшой фикс и отчет был успешно отправлен на платформу. В текущей реализации я убрал этот фикс, позже верну.
Кому и зачем все это нужно
Кому нужна эта статья? Всем нам. Все так боятся утечки персональных данных, но никого не волнуют проблемы безопасности, из-за которых эти утечки происходят.
Кому нужен FCALayer? Для аудиторов и разработчиков. Некоторые владельцы ИС заявляли модераторам платформы, что исправили уязвимость и просили перепроверить несколько раз. Но ничего не менялось. То есть отдельным разработчикам не только сложно реализовать проверку подлинности, но и проверить ее работу.
Еще я уверен, что такой инструмент должны разрабатывать те же люди что и NCALayer. При проверке некоторых ИС мне приходилось добиваться одинакового ответа как в NCALayer, буквально посимвольно: соблюдать длину строки для BASE64, правильно выбирать управляющие символы для конца строки (\r\n), соблюдать регистр тегов XML и многое другое.
На этом все, спасибо, что дочитали!
Ссылки
XCA — Управление сертификатами и ключами
ASN.1 Editor — Графический редактор Abstract Syntax Notation One (ASN.1) на основе.NET
Правила проверки подлинности электронной цифровой подписи МИР РК
ASN.1 простыми словами (часть 1, часть 2, часть 3) — статья на Хабр
Интеграция ЭЦП НУЦ РК в информационные системы на базе веб технологий — статья на Хабр
pkigovkz — аккаунт НУЦ РК на GitHub.com
Интерактивная документация по KAZTOKEN mobile
P.S.
Небольшой дисклеймер:
Содержание статьи личное мнение автора;
egov.kz и pki.gov.kz не подвержены уязвимости;
Уязвимости найдены в реализации проверки ЭЦП в конкретных ИС, а не в NCALayer;
Статья имеет образовательный характер, использовать приведенное ПО необходимо только с согласия владельца информационной системы.