Как сделать инъекцию SSH ключа в Google Cloud Compute Engine
После постоянного поиска ошибок в обычных приложениях Google, таких как Drive, мы захотели перейти в Google Cloud. Это была первая уязвимость, которую мы обнаружили на платформе и которая привела к RCE в один клик в инстансе Compute Engine пользователя-жертвы.
Это были наши первые шаги в Google Cloud и мы, естественно, наткнулись на один из самых популярных продуктов — Compute Engine. Изучая его возможности и то, как он работает, я заметил «SSH-in-browser». Это функция в GCP, которая позволяет пользователям получать доступ к своим инстансам в браузере через SSH. Визуально этот интерфейс очень похож на Cloud Shell.
Я рассматривал различные функции и опции SSH-in-browser и моё внимание привлекла возможность изменить имя пользователя.
В этом диалоговом окне вводится имя пользователя, которое затем передается на сервер через GET параметр newLinuxUsername. Даже если указанное имя пользователя не существует, он создаст нового пользователя с указанным именем и зарегистрирует вас как этого пользователя.
https://ssh.cloud.google.com/v2/ssh/projects/{PROJECT-NAME}/zones/{INSTANCE-ZONE}/instances/{INSTANCE-NAME}?newLinuxUsername={USERNAME}
Поскольку не было никакого рандомного токена или защиты от CSRF, любой мог создать ссылку и отправить ее пользователю Compute Engine, чтобы создать нового пользователя в его экземпляре. Не очень впечатляюще, но есть на что обратить внимание. Но самое интересное заключалось в том, что, по крайней мере, на первый взгляд, это выглядело так, как будто он просто принимал пользовательский ввод и передавал его в useradd или adduser.
Я попытался добавить полезные нагрузки, такие как `whoami`, $(whoami), чтобы увидеть, будут ли они выполнены. Однако ничего не произошло. Чтобы проверить инъекцию команды второго порядка, я попробовал `curl https://stazot.com` в качестве полезной нагрузки. И снова ничего не произошло. Я вернулся на страницу инстанса Compute Engine и начал искать другие вещи.
Я заметил, что в разделе «Ключи SSH» были перечислены имена пользователей, которые я использовал в качестве полезной нагрузки. Этот раздел содержит список всех пользователей и их соответствующие добавленные или сгенерированные SSH-ключи. В то время как полезные нагрузки, такие как `whoami` были отображены как есть, полезная нагрузка curl была указана странным образом.
Поле имени пользователя содержало `curl http, а поле SSH ключа содержало stazot.com`:ssh-key-value В тот же миг в моей голове зазвенели колокольчики.
Сервер использовал символ двоеточия :в качестве разделителя. Всё, что до :считалось именем пользователя, а все после : считалось SSH-ключом для этого пользователя. Теоретически, это должно позволить злоумышленнику ввести имя пользователя и свой открытый SSH-ключ. Это возможно, потому что, как отмечалось ранее, сервер принимает этот ввод в GET запросе и не имеет защиты от CSRF.
Таким образом, если заставить жертву открыть вредоносную ссылку, то добавится имя пользователя и SSH-ключ злоумышленника. Чтобы проверить уязвимость, я создал следующий URL-адрес с attacker:{URL-ENCODED-SSH-PUBLIC-KEY} в качестве полезной нагрузки:
https://ssh.cloud.google.com/v2/ssh/projects/{PROJECT-NAME}/zones/{INSTANCE-ZONE}/instances/{INSTANCE-NAME}?newLinuxUsername=attacker:{URL-ENCODED-SSH-PUBLIC-KEY}
Открыл ссылку как жертва — и попытался войти в инстанс как злоумышленник. Однако это не сработало. Причина заключалась в том, что добавленный мной SSH-ключ был добавлен к SSH-ключу, сгенерированному Google в серверной части. Чтобы исправить это, я добавил символы возврата строки к полезной нагрузке:
https://ssh.cloud.google.com/v2/ssh/projects/{PROJECT-NAME}/zones/{INSTANCE-ZONE}/instances/{INSTANCE-NAME}?newLinuxUsername=attacker:{URL-ENCODED-SSH-PUBLIC-KEY}%0d%0a
Как только жертва открыла эту ссылку, я попытался войти в этот инстанс как злоумышленник — и это сработало! Я вошел в систему на целевой машине с правами sudo.
Вот видео, демонстрирующее атаку:
Почему существовала эта ошибка?
Это поведение существовало для облегчения авторизации через функцию SSH-in-browser. Поскольку браузер действует как SSH-клиент, для входа в инстанс ему требуется авторизованный SSH-ключ.
Compute Engine обрабатывает это, создавая SSH-ключ для каждого пользователя в экземпляре (который использует SSH-in-browser) и сохраняет его в метаданных. Когда пользователь входит в систему с помощью SSH-in-browser, инстанс извлекает все авторизованные SSH-ключи с сервера метаданных и регистрирует пользователя.
Это очень сложный процесс, который подробно описан здесь.
Оригинал на английском тут.