Уязвимость CVE-2019-5736 в runc, позволяющая получить права root на хосте +32



Прим. перев.: Минувшей ночью Aleksa Sarai, старший инженер по контейнерам из SUSE Linux, сообщил в почтовой рассылке oss-sec о критической уязвимости в безопасности runc/LXC, которая позволяет злоумышленнику, имеющему доступ в изолированный контейнер, получить привилегии root на хостовой системе. Поскольку проблема выявлена в эталонной реализации исполняемой среды для контейнеров — runc, — она затрагивает многочисленные контейнерные системы включая Docker/Moby, Podman, cri-o (и собственно Kubernetes). Ниже представлены подробности в виде перевода сообщения инженера в почтовую рассылку.


Я являюсь одним из мейнтейнеров runc (нижележащей среды исполнения контейнеров, используемой в Docker, cri-o, containerd, Kubernetes и т.п.). Недавно стало известно об уязвимости, которую мы проверили и пропатчили.

Исследователи, обнаружившие уязвимость:

  • Adam Iwaniuk;
  • Borys Poplawski.

Кроме того, Aleksa Sarai (т.е. я) обнаружил, что более изощрённой версии этой уязвимости подвержен и LXC.

Краткий обзор


Уязвимость позволяет вредоносному контейнеру (с минимальным взаимодействием с пользователем) переписать хостовый бинарник runc и таким образом получить возможность исполнения кода с правами root на хосте. Степень взаимодействия с пользователем такова, что позволяет запускать любые команды (не важно, контролирует ли команду злоумышленник) с правами root в рамках контейнера в любом из этих контекстов:

  • Создание нового контейнера из контролируемого злоумышленником образа;
  • Подключение (docker exec) к существующему контейнеру, к которому ранее у злоумышленника был доступ на запись.

Эта уязвимость не блокируется ни политикой AppArmor по умолчанию, ни политикой SELinux по умолчанию в Fedora* (потому что процессы контейнера выглядят как запущенные с container_runtime_t). Однако она блокируется корректным использованием пользовательских пространств имён (где root хоста не map'ится в пользовательское пространство имён контейнера).

Вектор CVSSv3 (с рейтингом в 7.2) таков:

AV:L/AC:H/PR:L/UI:R/S:C/C:N/I:H/A:H

Проблеме назначен CVE-2019-5736.

* Проблема относится только к пакету moby-engine в Fedora. Пакет docker, как и podman, защищён от её эксплуатации, поскольку запускает процессы контейнера как container_t.

Патчи


Прикладываю соответствующий патч, исправляющий проблему (0001-nsenter-clone-proc-self-exe-to-avoid-exposing-host-b.patch). Он основан на ветке HEAD, хотя код в libcontainer/nsenter/ меняется настолько редко, что патч скорее всего применим практически к любой старой версии кодовой базы runc, с какой вы имеете дело.

Обратите внимание, что патч, который я push'нул в master-ветку runc, является модифицированной версии этого патча, хоть они и идентичны функционально (если вы ещё не пропатчили свои файлы приложенным патчем, мы рекомендуем использовать upstream-версию).

Второстепенность кода эксплоита


Некоторые вендоры запросили код эксплоита, чтобы убедиться, что патчи решили проблему. Поскольку проблема очень серьёзна (особенно для вендоров публичных облаков), мы решили предоставить такой код. Эксплоит написан мною, является более общим, чем оригинальный код, предоставленный исследователями, и работает для LXC (не требует сильных модификаций для возможности применить для других уязвимых исполняемых сред). Подробности о том, как использовать код эксплоита, предоставлены в README.

В соответствии с правилами OpenWall, код эксплоита будет публично обнародован через 7 дней после CRD (т.е. 18 февраля 2019 года). Если у вас есть исполняемая среда для контейнеров, пожалуйста, предварительно убедитесь, что она не подвержена этой уязвимости.

Влияние на другие проекты


Следует отметить, что в процессе дальнейшего расследования проблемы я обнаружил, что у LXC есть схожая уязвимость, и они уже push'нули схожий патч нашей совместной разработки. LXC немного сложнее эксплуатировать, однако сама проблема та же.

Обсуждение с представителями systemd-nspawn привело к выводу, что они уязвимости не подвержены (поскольку у них иной метод подключения к контейнеру для LXC и runc).

Со мной также связались представители Apache Mesos, сообщившие, что и они подвержены уязвимости (думаю, что они попросту использовали код эксплоита, который будет опубликован). Очень похоже, что большинство исполняемых сред для контейнеров подвержены уязвимости, если они предварительно не предпринимали весьма необычных действий по смягчению её возможного воздействия.

Другие новости


Мы настроили рассылку анонсов для будущих уязвимостей в безопасности: процесс присоединения к ней описан здесь (он основан на почтовой рассылке Kubernetes security-announce). Пожалуйста, присоединяйтесь, если распространяете любые исполняемые среды для контейнеров, которые зависят от runc (или других проектов OCI).

P.S. От переводчика


Проблема CVE-2019-5736 в трекерах популярных Linux-дистрибутивов:


… и запись в блоге Kubernetes (обратите внимание на раздел «What Should I Do?»).

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (8):

  1. ElegantBoomerang
    /#19746748

    Запись в /proc/self/exec? На это хватило прав? Какие права проброшены внутрь контейнера? Столько интересных вопросов, аж жаль, что перевод.

    • mk2
      /#19746924

      Через 6 дней можно будет взглянуть на эксплоит и понять точно. Или перевести статью, где это сделал кто-то еще)

      • cyrillpetroff
        /#19747982

        Эксплоит скорее всего уже написан, только в привате и за $

        • mk2
          /#19748002

          Закрытый — возможно. Но и сам автор говорит, что они написали демонстрационный, и откроют его 18 февраля. А на него уже посмотреть всем можно будет.

    • shurup
      /#19747824

      Из NVD (ссылка есть в статье):

      This occurs because of file-descriptor mishandling, related to /proc/self/exe.

      В этом патче (к LXC; тоже есть ссылка в статье) — больше подробностей:

      The attack can be made when attaching to a running container or when starting a
      container running a specially crafted image. For example, when runC attaches
      to a container the attacker can trick it into executing itself. This could be
      done by replacing the target binary inside the container with a custom binary
      pointing back at the runC binary itself. As an example, if the target binary
      was /bin/bash, this could be replaced with an executable script specifying the
      interpreter path #!/proc/self/exe (/proc/self/exec is a symbolic link created
      by the kernel for every process which points to the binary that was executed
      for that process). As such when /bin/bash is executed inside the container,
      instead the target of /proc/self/exe will be executed — which will point to the
      runc binary on the host. The attacker can then proceed to write to the target
      of /proc/self/exe to try and overwrite the runC binary on the host. However in
      general, this will not succeed as the kernel will not permit it to be
      overwritten whilst runC is executing. To overcome this, the attacker can
      instead open a file descriptor to /proc/self/exe using the O_PATH flag and then
      proceed to reopen the binary as O_WRONLY through /proc/self/fd/ and try to
      write to it in a busy loop from a separate process. Ultimately it will succeed
      when the runC binary exits. After this the runC binary is compromised and can
      be used to attack other containers or the host itself.

      • ElegantBoomerang
        /#19747892 / +3

        Спасибо! То есть:


        1. Мы заставляем exec-нуться в себя runc, теперь он запущен внутри контейнера.
        2. Процесс не потерял fd-шку на себя потому что лапки.
        3. Изнутри контейнера мы через этот fd начинает писать в бинарник.
        4. У нас получается?!

        • ElegantBoomerang
          /#19748246 / +1

          Для желающих, я почитал доки: да, получится, ещё как! :D Одновременно красивый хак, но и безолаберный: запуск привилегированного контейнера это практически root, вот ещё один пример в коллекцию.

  2. MMik
    /#19748284

    Выдохнул. Очередной повод порадоваться включенному SELinux, и поблагодарить разработчиков.

    Пакет docker, как и podman, защищён от её эксплуатации, поскольку запускает процессы контейнера как container_t.
    OpenShift Online and Dedicated are not vulnerable to exploit due to the use of SELinux in enforcing mode.
    Customers using SELinux in enforcing mode can observe exploitation attempts by looking at AVC events in the audit logs. E.g.
    $ aureport -a
    AVC Report
    ===============================================================
    # date time comm subj syscall class permission obj result event
    ===============================================================
    1. 11/02/19 00:00:00 script system_u:system_r:container_t:s0:c530,c886 2 file write system_u:object_r:container_runtime_exec_t:s0 denied 81359