«Занимательный XenAPI», или «Новые приключения Citrix XenServer»

в 5:56, , рубрики: security research, xen, xenserver, Блог компании Positive Technologies, виртуализация, информационная безопасность, метки: , ,

«Занимательный XenAPI», или «Новые приключения Citrix XenServer»Привет, коллеги!
Сегодня я хотел бы продолжить свое повествование о Citrix XenServer 5.6 и о разных аспектах работы с ним. В этот раз мне пришлось решать довольно простую (казалось бы!) проблему: исполнение команд в dom0 без применения SSH. Изучение возможностей для реализации привело к обнаружению некоторых забавных нюансов HTTP API данной ОС: способов получения /etc/passwd, удаленного выполнения rsync и набросков XenSource thin CLI protocol. Сейчас я расскажу вам, что называется, историю одного ресёрча…

Прежде всего, хотелось бы объяснить, как возникла подобная задача. В предыдущей серии я показывал публичную бета-версию секьюрити-гайда для XenServer, которую я «пилю» в надежде написать внятное руководство. Одна из рекомендаций (по аналогии с security hardening guide для VMWare ESXi) — отключение SSH-демона. Мотивация заключается в том, что в корпоративной версии Xen есть возможность использовать RBAC-систему с аутентификацией через Active Directory. Согласно рекомендациям вендора, этот путь с точки зрения безопасности является предпочтительным. После некоторой модификации сценариев запуска консоли на dom0, описанных в моем руководстве, попадание через нее в систему без пароля исключено. Соответственно, чтобы попасть непосредственно в консоль dom0 требуется знание не только пароля пользователя с правами pool-admin, но и данных учетной записи root.

ОК. Теперь перед нами встает задача удаленного аудита операционной системы при помощи автоматизированных средств. Все, что есть в нашем распоряжении, это XML-RPC, ведущая к XenAPI, документация на нее и исходники Xen-org на прекрасном языке OCaml. Нам же хочется выполнять команды в bash и получать их «выхлоп» для последующей обработки. Как это сделать?

Для начала нужно понять, почему нормальным путем (через консоль, предоставляемую в рамках API) это сделать не получится. Вспомним технологию вызова консоли со стороны клиента: вы подключаетесь к консоли (https://<xen_host>/console?ref=OpaqueRef:console_id), имея валидный session_id, и попадаете на RFB-терминал vncterm. Понятно, что данный протокол позволяет отправлять действия мышки и нажатия кнопок на удаленный сервер, а в ответ получать растровые изображения экрана. Дальнейшее очевидно: современные версии протокола RFB позволяют, кроме прочего, передавать файлы. Достаточно освоить еще и исполнение команд — и нет проблем. Однако это было бы слишком просто. В своих терминалах vncterm компания Citrix использует протокол RFB версии 003.003: в ней передача файлов еще не реализована.

С учетом этой печальной новости наша команда разработчиков принялась анализировать возможные методы реализации транспорта через RFB образца 1998 года. Идей появилось две. Первая — интеграция с ABBYY FineReader (с распознаванием текста на растровых изображениях, получаемых с dom0). Вторая — эмуляция движений мыши, которые позволяют выделить текст на экране и отправить его в буфер обмена, доступный в рамках протокола. Оба варианта при ближайшем рассмотрении напоминают бред сумасшедшего :)

Невеселые перспективы заставили меня вернуться к чтению документации для XenAPI. И я обратил внимание на то, с чем мне до этого не приходилось сталкиваться, — на плагинную архитектуру, а именно на возможность обращения к собственным исполняемым файлам через RPC-вызов call_plugin. Модули расположены в директории /etc/xapi.d/plugins/. «Занимательный XenAPI», или «Новые приключения Citrix XenServer» Дальше все просто: созданный нами плагин вызывается через XML-RPC и запускает соответствующий сценарий на Python, который реализует выполнение необходимых команд силами subprocess. Отлично! Методология выполнения команд на dom0 и получения от них ответа стала ясна.

Следующая проблема появилась сама собой: каким же образом наш плагин должен попасть на сервер? При решении этой проблемы, собственно, и обнаружились некоторые «подводные грабли» XenAPI.

Конечно же, меня заинтересовала функция, доступ к которой можно получить при помощи штатной утилиты xe.exe, — patch-upload. Она позволяет удаленно загружать файлы на XenServer и устанавливать их на весь пул серверов. Формат представления патча достаточно прост: это shar, упакованный в zip и подписанный (!) компанией Citrix. О том, как заглянуть в патч и установить его вручную, можно прочесть в заметке компании «Селектел». При загрузке патча его подпись сверяется с соответствующим набором открытых ключей в gpg keyring. Таким образом, достаточно добавить собственную подпись в общую связку — и проблема разливки плагина отпадает. Собрать аналогичную конструкцию нетрудно, но чтобы залить свой ключ в связку необходимо иметь доступ к консоли. Получился замкнутый круг. Поэтому я начал искать способ залить свой плагин в обход стандартных методов.

Используя данный вызов, я отметил, что вызова https://<xen_host>/pool_patch_upload — нет в официальном описании API. Тому есть логичное объяснение: это действительно не часть API. Природное любопытство подсказывает вопрос: «А что же это тогда такое?». Способ найти ответ очень прост: Wireshark. «Занимательный XenAPI», или «Новые приключения Citrix XenServer» Возможно, вы осудите меня за столь прямолинейный подход, но HTTP-интерфейс API операционной системы XenServer описан, к сожалению, чуть менее чем никак. А к моменту работы над этой проблемой я еще не понимал OCaml на таком уровне, чтобы анализировать исходный код достаточно эффективно.

Воспользовавшись замечательной возможностью Wireshark для расшифровки TLS и заботливо оставленным на виду сертификатом в /etc/xensource/, я получил дамп общения утилиты xe.exe (из состава XenCenter) с сервером. «Занимательный XenAPI», или «Новые приключения Citrix XenServer»Я ожидал увидеть XML-RPC общение, описанное в официальной документации. Но не тут-то было! Вместо этого в логе светился «POST /cli HTTP/1.0». Утилита забирала команду, ее атрибуты — и отправляла их на https://<xen_host>/cli. «Кажется, в супе чего-то не хватает». Из расшифровки протокола следовало, что есть некий XenSource thin CLI protocol который и использует утилита. Все пути вели на Github к исходникам XenAPI.

Проведя некоторое время за чтением исходных кодов этого прекрасного компонента гипервизора, я выяснил, что существует данное «XenSource thin CLI protocol» API версии 0.2, которое реализует выполнение команд хелпера xe.exe на удаленном хосте. «Занимательный XenAPI», или «Новые приключения Citrix XenServer»Описано оно в файле xapi/cli_protocol.ml. Примечательно, что это «API будущего», призванное сделать из утилиты xe.exe всего лишь средство для пересылки команд, а обработчик встроить в XenAPI.

В целом обнаружение данного CLI API было судьбоносным: оно показало, что на порте 80443 существует не только XML-RPC приемник и переключатель /console. Какие еще модули доступны через подобный вызов — случайно получилось найти в одном из файлов исходного кода (xen-api/ocaml/idl/constants.ml). Как легко можно догадаться, там обнаружилось множество вызовов, выдававших весьма интересную информацию. Меня покорил вызов https://<xen-host>/syns_config_files: при достаточных правах (pool-admin) вы получаете /etc/passwd (как я уже рассказывал в своих прошлых статьях, XenServer хранит хэш паролей именно там).

Еще один интересный вызов реализуется через “CONNECT /remotecmd?cmd=rsync&arg=some_nice_arg &pool_secret=your_pool_secret”. Он позволяет, зная значение /etc/xensource/ptoken, удаленно выполнять на сервере команду rsync с правами root. Это дает, в сущности, полный доступ к файловой системе. Но вы, наверное, спросите: «А как же добыть ptoken?».

Здесь все еще более тривиально. Разработчики Xensource создали возможность удаленно получать содержимое базы pool в виде XML-файла. Если выполнить на сервере запрос вида "GET /pool/xmldbdump?session_id=" то вы получите полный набор пар «ключ — значение», среди которых без труда можно найти нужный pool_token.

Ну и, собственно, сама удаленная загрузка патчей осуществляется через вызов "PUT /pool_patch_upload?session_id=". В ответ на него сервер напишет: «200, OK». И будет ждать, когда вы начнете заливать в сокет информацию. Как только вы загрузите файл, начнется проверка патча на валидность. Но есть одна особенность: пока вы держите коннект, API считает что вы все еще заливаете файл, и не трогает его (хотя файл уже создан в /var/patch). Проверки на длину файла я не обнаружил. Поскольку /var/patch находится в корневом разделе сервера, то DoS неизбежен в случае отправки туда /dev/urandom.

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

В целом же композиции описанных мною методов хватило для успешной заливки патча в систему без проверки подписи. Я не буду делать детальное описание методологии, ибо она граничит с понятием «эксплуатация уязвимости», но я думаю, что вы и сами все поняли :)
Спасибо за внимание!

Автор: isox

* - обязательные к заполнению поля