Автор: Мария Недяк


Вы наверняка видели в медицинском сериале, как интерны бьются над рентгеновским снимком пациента, а потом приходит их наставник и ставит диагноз по едва заметному пятнышку. В реальности такими остроглазыми диагностами становятся модели машинного обучения, применяемые в технологии medical imaging. Благодаря таким штукам можно гораздо быстрее выявить болезнь, например, определить, являются ли клетки на снимках опухолевыми или неопухолевыми.


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



Протокол DICOM и его слабые места


Коротко о самом протоколе


DICOM (Digital Imaging and Communication in Medicine) — протокол представления медицинских обследований и передачи их между различными компонентами. Этими компонентами могут быть:


  • медицинское оборудование, которое непосредственно делает сканирование;
  • DICOM-сервер — база данных для DICOM-файлов;
  • DICOM-клиент — обычно это приложение для просмотра результатов медицинских обследований.

Протокол DICOM имеет две части:


  • описание формата файла;
  • описание сетевого взаимодействия.

Теперь к делу


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


Мы также изучили безопасность PACS (Picture Archiving and Communication System). Это такие системы, в которых результаты обследований хранятся в электронном виде. Они позволяют передавать снимки между врачами по сети. Да-да, благодаря PACS больше не нужно таскать с собой рентгеновские снимки в конверте, как мы привыкли.


DICOM-файл


DICOM-файл — изображение медицинского характера, сохраненное в формате DICOM. Этот формат — отраслевой стандарт для хранения и распространения медицинских снимков.


Помимо графических данных, DICOM-файлы могут содержать персональную информацию в виде атрибутов, позволяющих сопоставить изображение с конкретным человеком и идентифицировать пациента. К ним относятся пол, имя пациента, дата рождения и пр. Список возможных атрибутов и их описание можно найти в документации.


На рисунке показана структура DICOM-файла:



DICOM Network


Стандарт протокола довольно сложно и нудно описывает сетевое взаимодействие — мне до сих пор не удалось изучить его целиком. Но если говорить в общем, вот какие команды сетевого взаимодействия есть у DICOM-протокола:


Действие Описание
C-ECHO Тест соединения между двумя устройствами
C-FIND Поиск исследований на удаленном сервере
C-GET, C-MOVE Скачивание исследований с удаленного сервера
C-STORE Сохранение исследования на удаленном сервере


Вот как выглядит процесс передачи результатов медицинских обследований по DICOM-протоколу


Немного статистики по DICOM-серверам в интернете.



Такие картинки-отчеты о сканировании генерирует Grinder Framework


Интересный факт: когда мои коллеги из AISec проводили сканирование в 2019 году, то доступных серверов было меньше тысячи. В 2020 году же их оказалось около 2700.


Большинство хостов в сети настроены таким образом, что любой может установить с ними соединение. Давайте разберем пример, как просто получить исследование с удаленного сервера из сети. Для этого воспользуемся утилитами findscu и getscu из DCMTK — набора библиотек и приложений, реализующих большую часть стандарта DICOM.


Первой командой получаем список всех доступных исследований на сервере.


$ findscu -aet <AE Title> -P -k PatientName="*" <host> <port>

С ключом -aet в запросе передаем название Application Entity Title. Обычно этот тайтл требуется, когда нужно разграничить доступ к изображениям при использовании одного DICOM-сервера разными PACS. Это, своего рода, идентификация клиента. Но проблема в том, что тайтлы на многих серверах настроены по умолчанию. Это значит, подобрать его можно, перебирая дефолтные значения от разных разработчиков PACS и DICOM-серверов.


С ключом -k передаем фильтр "PatientName=*", который позволит показать любое доступное исследование на сервере.



Пример вывода команды findscu


Следующей командой скачиваем нужное нам исследование или все исследования сразу.


$ getscu -aet <AE Title> -P -k PatientName="John Doe" <host> <port>

Значения ключей в команде аналогичны предыдущей.



Пример исследования, скачанного с удаленного сервера DICOM


Вот так с помощью двух команд можно скачать данные с удаленного DICOM-сервера при наличии одного из условий:


  • Application Entity Title не задан,
  • Application Entity Title установлен по умолчанию, поэтому его удалось подобрать.

Далее рассмотрим популярные инструменты и реализации протокола и найденные в них недостатки.


Реализации протокола DICOM


SimpleITK


SimpleITK — реализация протокола, которая используется в одном из крупных проектов в области medical imaging NVIDIA CLARA.


Для поиска уязвимостей в ней использовался фаззинг AFL со словарем. В результате мы наткнулись на переполнение кучи. Попытки эксплуатации привели к обнаружению более простой уязвимости — переполнению буфера через поле PatientName. Причем для переполнения буфера было достаточно создать файл с длиной имени пациента свыше 512 байт.



В ответ на репорт о переполнении кучи разработчик просто поправил файл, демонстрирующий уязвимость, чтобы он корректно обрабатывался библиотекой :)



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


DCMTK


DCMTK (DICOM Toolkit) — самая старая реализация DICOM-протокола. Она включает набор инструментов для работы с протоколом: парсеры DICOM-файлов в разных форматах и из разных форматов, а также утилиты для взаимодействия с DICOM-сервером по сети.


XXE в xml2dcm


Парсер xml2dcm конвертирует результаты медицинского обследования из формата XML в DICOM.
В xml2dcm мы нашли уязвимость к самой простой XXE-атаке: создаем XML-файл с внешней сущностью в имени пациента и на выходе получаем DICOM-файл с содержимым /etc/passwd.


<?xml version="1.0" encoding="ISO-8859-1"?>
   <!DOCTYPE foo [ 
   <!ELEMENT foo ANY >
 <!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
…
<element tag="0010,0010" vr="PN" vm="1" len="32" name="PatientName">&xxe;</element>
…


Чем кончилось: данную уязвимость вендор устранил.


Небезопасные функции xml2dcm


По аналогии с external entities в XML, cама по себе утилита xml2dcm позволяет создавать DICOM-файлы c содержимым других файлов внутри. Это удобно, потому что не нужно писать данные в XML-файл — достаточно указать в теге PixelData путь к файлу, из которого нужно подгрузить данные при конвертировании.


<element tag="7fe0,0010" vr="OW" vm="1"  name="PixelData" loaded="no" binary="file">/etc/passwd</element>

Если в теге PixelDatа указать путь к любому файлу в системе, то после обработки утилитой xml2dcm мы получим DICOM-файл с его содержимым.


Чем кончилось: эту функцию невозможно отключить никаким флагом, а на доработку парсера xml2dcm вендор не согласился :(


DoS в парсерах DICOM-файлов


Мы тестировали парсеры из DCMTK при помощи фаззинга AFL и libFuzzer. Результат — DoS-утилиты xml2dcm и dcm2xml.


Чем кончилось: вендор исправил найденные ошибки.


DoS в dcmqrscp-сервере


DCMTK также предоставляет реализацию DICOM-сервера dcmqrscp. Тестирование безопасности DICOM-сервера dcmqrscp было проведено с помощью фаззинга, в результате которого был обнаружен DoS.
Фаззинг проводился при помощи AFLNet. Поддержку протокола DICOM я добавила в официальный репозиторий AFLNet, если кому-то захочется пофаззить другие DICOM-серверы.


Чем кончилось: вендор исправил ошибку реализации.


Приложения medical imaging


ORTHANC


В ходе исследования мы затронули приложение ORTHANC. Этот продукт очень прост в настройке и использовании: он предоставляет веб-обертку для просмотра DICOM-файлов, и для работы с DICOM-протоколом требуется только браузер.
ORTHANC используют в здравоохранении, в различных университетах и госпиталях, с его помощью проводятся исследования в области машинного обучения medical imaging (раз и два).



Открытые серверы ORTHANC в сети


Небезопасное API


Сервер ORTHANC открыт к сообществу разработчиков и предоставляет REST API для написания различных плагинов. Если взглянуть на список доступных методов, можно заметить интересные методы для перезагрузки и выключения сервера, а также метод с названием execute-script.



Он принимает на вход скрипты на lua и без какой-либо валидации выполняет их на сервере. Чтобы убедиться в этом самостоятельно, посмотрите исходный код ORTHANC.



Чем кончилось: раньше этот метод был включен по умолчанию в конфиге Orthanc, но, к счастью, недавно вендор догадался его отключить. Однако в сети до сих пор можно наткнуться на Orthanc-серверы с работающим методом execute-script.


Уязвимость аутентификации к CSRF-атаке


Метод execute-script является небезопасным. Разработчик ORTHANC решил эту проблему при помощи аутентификации, чтобы выполнять такие запросы могли только зарегистрированные пользователи. Но по умолчанию аутентификация отключена при работе с ORTHANC-сервером с официального сайта.


В докере от разработчика уже включена аутентификация по умолчанию. Это здорово, если бы не следующая проблема: данная система аутентификации уязвима к CSRF-атаке. Достаточно создать страничку со следующим содержанием и отправить ее пользователю:


<html>
  <body>
    <form action="http://<host>:8042/tools/execute-script" method="POST" enctype="text/plain">
      <input type="hidden" name="cmd" value="'mkdir /tmp/testCSRF';os.execute(cmd)"/>
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

Когда пользователь открывает страницу, на сервер отправляется команда, запускающая выполнение произвольного кода.


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



Ответ вендора


BTW: кеш гугла показал, что приписку в документации о CSRF вендор сделал после моего репорта об уязвимости. И, конечно же, вряд ли он известил тех клиентов, которые уже пользуются их «микросервисом» и верят в его надежность.



Страница документации после ответа вендора на репорт об уязвимости



Страница документации из кеша гугл за пару дней до репорта не содержит каких-либо упоминаний CSRF


Чем кончилось: вендор так ничего и не предпринял для устранения данной уязвимости, только дополнил документацию.


Заключение


То, как просто найти уязвимости в medical imaging и как забавно вендоры реагируют на баг-репорты, показывает слабый уровень защищенности таких технологий. Чтобы они стали безопасными, над ними еще работать и работать.


Напоследок оставлю табличку со всеми описанными в статье багами.


Vendor Product Weakness
SimpleITK ImageSeriesReader Heap-buffer-overflow
SimpleITK ImageSeriesReader Buffer-overflow
Orthanc Orthanc CSRF with remote code execution
DCMTK xml2dcm XXE
DCMTK xml2dcm DoS
DCMTK xml2dcm File read functionality
DCMTK dcm2xml DoS
DCMTK dcmqrscp DoS

Также подробная информация лежит на github.