«Занимательный 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/. Дальше все просто: созданный нами плагин вызывается через 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. Возможно, вы осудите меня за столь прямолинейный подход, но HTTP-интерфейс API операционной системы XenServer описан, к сожалению, чуть менее чем никак. А к моменту работы над этой проблемой я еще не понимал OCaml на таком уровне, чтобы анализировать исходный код достаточно эффективно.

    Воспользовавшись замечательной возможностью Wireshark для расшифровки TLS и заботливо оставленным на виду сертификатом в /etc/xensource/, я получил дамп общения утилиты xe.exe (из состава XenCenter) с сервером. Я ожидал увидеть 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 на удаленном хосте. Описано оно в файле xapi/cli_protocol.ml. Примечательно, что это «API будущего», призванное сделать из утилиты xe.exe всего лишь средство для пересылки команд, а обработчик встроить в XenAPI.

    В целом обнаружение данного CLI API было судьбоносным: оно показало, что на порте 80\443 существует не только 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.

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

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

    Комментарии 8

      +2
      Вы лучше сразу скажите: решето, не?
        +1
        Мне ОС нравится. Если следовать советам вендора по разделу сетей, использовании RBAC+Likewise интеграции с AD, итд. то получится хорошо.
        +2
        Файнридер покорил — молодцы, все варианты рассматривали.
          +1
          Поправка, я на хабре не писал про это. Писал я в рассылку и кому-то в почтовом общении. Суть состоит в том, что валидация пакета апдейта осуществляется с помощью расшифровки его gpg. Ключ расшифровки — открытый ключ цитрикса. Если не знать закрытый ключ (а его никто не знает), то «фальшиво зашифровать» не получится.

          Относительно жалоб на безопасность: Не выставляйте наружу management-сеть. Просто не выставляйте. Это есть во всех best practicie.

          Более того, для параноиков предусмотрен особый режим, в котором нет management интерфейса.

          ЗЫ А вот херня с местом в dom0 — известная жопа. Я даже могу рассказать про последствия: если место в dom0 заканчивается, что сервис xapi уходит в неответ и пребывает там до перезапуска (при этом xe-toolstack-restart или /etc/init.d/xapi restart не работает, нужно убивать процесс руками) и расчистки места.

          Т.к. именно xapi отвечает за перезапуск доменов, то все операции с VM на хосте останавливаются (т.е. при ребуте машина остаётся running, но не запускается снова — домен висит в -S- состоянии, а xapi его не может domain_destroy(), ибо висит.).

          Ситуация ещё лучше с, например, мастером пула. Зависание которого означает переход хостов в полу-автономный режим (только ребут и halt из самих машин).

          ЗЫ Уж если тестируете устойчивость xapi к XenAPI, поставьте в 50 потоков команду xe task-list, или там xe vm-memory-target-set для включенной машины. В какой-то момент вы доведёте xapi до засирания логами (xensource.log) со скоростью, которая превышает скорость ротации логов.

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

          ЗЗЫ На полном серьёзе предлагаю изучить опцию pv_bootloader_args — это аргументы, которые передаются программе pygrub, которая запускается от root'а в dom0 при старте любой PV-машины. Там нахулиганить, наверное, тоже можно изрядно.
            0
            Да-да, изоляция мэнеджмент-интерфейса решает 9/10 проблем.

            О! На счет pv_bootloader_args огромное!
            Поковыряю на досуге, может там какая алярма притаилась :)
              0
              На досуге посмотрите, что будет, если в PV положить ядро в 2-3Гб размером (или initrd такой). Оно в tmp кладётся при запуске машины.
                0
                Да, этот фокус мне известен, спасибо.
                Вы мне про него рассказывали в одной из прошлых серий разбирательств ксена.

                Для этого я в руководстве советую отцеплять /tmp и /var отдельно.
            0
            Можно ссылку где хоть как-то можно почитать про xen api? Интересует именно через XML

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

            Самое читаемое