• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Анализ RCE импорта проекта Gitlab (CVE-2022-2185)

вавилонец

CPU register
Пользователь
Регистрация
17.06.2021
Сообщения
1 116
Реакции
1 265
ОРИГИНАЛЬНАЯ СТАТЬЯ
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro
$600 на SSD для Solidity hacking by Jolah Milovsky---> 0x5B1f2Ac9cF5616D9d7F1819d1519912e85eb5C09

В начале этого месяца GitLab выпустил патч безопасности для версий 14->15. Интересно, что в бюллетене упоминалась ошибка RCE после аутентификации в CVSS 9.9.
Ошибка существует в GitLab Project Importsфункция, которую нашел @vakzz . Кстати, когда я рылся в профиле автора h1. Я обнаружил, что четыре месяца назад он также нашел ошибку в функции импорта проекта:
Сначала я подумал, что это заманчиво, увидев награду, поэтому начал изучать Rails и отладил эту ошибку! (кто бы мог подумать, что 30к не будет так просто (° ͜ʖ ͡°))
Обратите внимание, что статья может быть длиннее, чем обычно. Вы можете прочитать его на досуге, но если вам просто нужен PoC, вы можете перейти к концу, чтобы посмотреть видео!

НАСТРОЙКА СРЕДЫ И ОТЛАДКА

Эта часть также относительно громоздка и сложна, требуя терпения с позиции Re-searcher! Сначала я обратился к сообщению друга в Sun*, чтобы настроить среду. Но после запуска он был довольно медленным и очень ненадежным, поэтому я решил установить его самостоятельно!
В моей среде используется виртуальная машина с Ubuntu Desktop 18.04. Первый — сначала настроить набор GitLab GDK:
Код:
apt update
apt install make git -y
curl "https://gitlab.com/gitlab-org/gitlab-development-kit/-/raw/main/support/install" | bash

Подождите 15-30 минут, и мы завершим настройку GDK. Теперь мы можем проверить уязвимую версию GitLab:
Код:
cd gitlab-development-kit/gitlab/
git checkout v15.1.0-ee

После оформления отредактируйте следующие файлы:
  • config/gitlab.yml, найдите строку конфигурации хоста gitlab и измените ее на IP-адрес виртуальной машины, чтобы вы могли просматривать снаружи
Код:
gitlab:
## Web server settings (note: host is the FQDN, do not include http://)
host: 192.168.139.137
port: 3000
https: false
/[CODE]
Найдите строки с ключом webpackи установить enabledк false:
[CODE]
webpack:
dev_server:
enabled: false
После редактирования перейдите в папку GitLab/ и введите следующую команду для компиляции ресурсов сервера:
Код:
rake gitlab:assets:compile
  • config/puma.rb, найдите строку, объявляющую workersи снова комментарий:
# workers 2

Когда вы закончите редактирование файлов конфигурации, введите следующие команды, чтобы запустить соответствующие службы:
Код:
gdk stop
gdk start webpack rails-background-jobs sshd praefect praefect-gitaly-0 redis postgresql
Что касается IDE, я использую RubyMine (продукт Jetbrains) . Используя RubyMine, мы можем просмотреть и открыть gitlabпапка. Среда IDE автоматически обнаружит и установит соответствующие компоненты. Добавьте конфигурацию отладки, открыв Run > Edit Configurations
1663863770688.png

Добавьте в Railsконфиг, как показано ниже:
1663863779627.png

Затем добавьте в sidekiqконфигурация:
1663863791718.png

В случае использования клона исходного кода из репозитория GitLab конфиги для отладки уже доступны.
1663863804686.png

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

CVE-2022-2185 АНАЛИЗ

Я полагаюсь на более раннюю ошибку, о которой сообщил @vakzz, чтобы проанализировать эту ошибку. Хотя взаимосвязь между обеими ошибками едва ли значительна в том смысле, что только точка входа одинакова, а все остальные методы разные, я бы порекомендовал вам сначала прочитать это здесь, где вы обнаружите, что некоторые методы могут быть связаны со старым.

Исправлены версии 15.1.1, 14.10.5. Я выбираю v15.1.1, чтобы начать охоту​

Читая коммиты на gitlab-v15.1.1 , мне повезло, что коммитов не так много, но я нашел замечательный коммит ниже:


изображение.png



Коммит 5d58c705 имеет интересное название и звук, связанный с этой ошибкой. security-update-bulk-imports-project-pipeline-15 -1
Некоторые замечательные изменения в этом коммите: В lib/gitlab/import_export/decompressed_archive_size_validator.rb


изображение.png


Код:
The validate_archive_pathметод проверяет случаи, когда @archive_pathэто символическая ссылка, а не строка и не файл.
     def validate_archive_path
        Gitlab::Utils.check_path_traversal!(@archive_path)

        raise(ServiceError, 'Archive path is not a string') unless @archive_path.is_a?(String)
        raise(ServiceError, 'Archive path is a symlink') if File.lstat(@archive_path).symlink?
        raise(ServiceError, 'Archive path is not a file') unless File.file?(@archive_path)
      end
После validate_archive_path, этот метод продолжит вызывать Open3.popen3(command, pgroup: true)для запуска команды. Объявление команды выглядит следующим образом:
Код:
 def command
         "gzip -dc #{@archive_path} | wc -c"
       end
Этот метод напрямую добавляет строку @archive_pathк команде gzip -dc, поэтому я предполагаю, что ошибка внедрения команды происходит в этом месте!
учебный класс DecompressedArchiveSizeValidatorиспользуется в 2-х местах:
  • file_importer.rb
  • file_decompression_service.rb
изображение.png


Дополнительное примечание о воркерах в GitLab

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

Случай 1:


Сначала мы рассмотрим file_importer.rbфилиал, Мы начинаем с Import::GitlabProjectsControllerи он создает и вызывает Projects::GitlabProjectsImportService.new(current_user, project_params).executeдля создания работы.

изображение.png


Lines 17, 18 and 19были прокомментированы для облегчения отладки. Я не понимаю, почему среда отладки с GDK неисправна. Все загруженные файлы считаются недействительными!! Эта проблема не возникает в версиях продукта.
project_params, который использовался для создания проекта, был ограничен, позволяя передавать только параметры: name, path, namespace_id, file

изображение.png


Трассировка стека в это место:

изображение.png

Из GitlabProjectsImportService.executeпродолжать звонить prepare_import_paramsредактировать, добавлять и удалять другие важные параметры (1)
изображение.png


Затем, GitlabProjectsImportService.execute Projects::CreateService.execute снова, чтобы создать проект с параметрами, переданными из GitlabProjectsController. В Projects::CreateService.execute, если импортируемый проект не является шаблоном, метод продолжит инициализацию объекта Project с переданными параметрами:

изображение.png


Как только проект будет создан, метод продолжит переходить в ветку, вызывающую validate_import_source_enabled! import_type:

изображение.png


Будет две ветки, которые будут удовлетворять условию, причем первое условие, import_typeпринадлежность к одному из следующих типов.
INTERNAL_IMPORT_SOURCES = %w[bare_repository gitlab_custom_project_template gitlab_project_migration].freeze

Во втором случае import_type должен быть в списке Gitlab::CurrentSettings.import_sources:

изображение.png


После создания объекта Project и выполнения других различных модификаций этот метод вызовет Projects::CreateService.import_scheduleчтобы добавить расписание для работника, чтобы сделать импорт:
Код:
  def import_schedule
      if @project.errors.empty?
        @project.import_state.schedule if @project.import? && !@project.bare_repository_import? && !@project.gitlab_project_migration?
      else
        fail(error: @project.errors.full_messages.join(', '))
      end
    end

Для добавления в расписание импорта этот проект должен иметь тип импорта gitlab_project.
После добавления в расписание рабочий получит задание и выполнит следующее:

изображение.png

Stacktrace к DecompressedArchiveSizeValidator.execute

изображение.png


Однако, согласно этой ветке, мы не можем контролировать @archive_path

изображение.png


@archive_fileберется из Project.import_source. Однако этот атрибут не установлен по умолчанию и имеет нулевое значение!

изображение.png


Это значение остается нулевым в Gitlab::ImportExport::FileImporter.new

изображение.png


Только при Gitlab::ImportExport::FileImporter.copy_archive, будет ли установлено это значение:

изображение.png


@archive_file_name генерируется на основе полного_пути проекта, поэтому этим значением нельзя манипулировать. Согласно этой ветке, у нас не может быть внедрения команды ошибки ¯_(ツ)_/¯

изображение.png

Случай 2

The file_importer.rbветка была подтверждена как неэксплуатируемая, поэтому я переключился на вторую ветку для анализа. Вторая ветвь file_decompression_service.rb
изображение.png


Эта ветвь довольно сложна для анализа и требует дополнительных сведений, чтобы получить доступ к нужной полезной нагрузке.
Во-первых, вы должны перейти к функции группы импорта GitLab и заполнить информацию, такую как URL-адрес GitLab и токен доступа.

изображение.png


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

изображение.png


Вернемся к истории запросов Burpsuite:

изображение.png


Searching for the group_entity ключевое слово в исходном коде, я обнаружил, что помимо group_entity, существует также project_entity:

изображение.png


Я не могу найти эту функцию в Интернете или в каком-либо документе. Скорее всего, это скрытая, развивающаяся фича GitLab!
Эта функция массового импорта обрабатывается Import::BulkImportsController.

изображение.png


После выполнения create_bulk_import, BulkImportsController.execute метод продолжает вызывать BulkImportWorker.perform_async, содержание метода следующее:

изображение.png


Обратите внимание на вызов BulkImports::CreatePipelineTrackersService.new(entity.execute!). Этот метод рассматривает, какие конвейеры подходят для выполнения с переданными параметрами:

изображение.png


Например, с project_entity, у нас есть такие конвейеры:

изображение.png


Примечания сайта о конвейере в массовом импорте

Эта концепция является эксклюзивной для массового импорта. Исполняемый файл для этих конвейеров lib/bulk_imports/pipeline/runner.rb.

изображение.png

изображение.png


Конвейеры будут иметь объявления и методы переопределения, такие как extract, transform, loadа также after_run.
Бегун будет просматривать и выполнять эти методы по порядку: extract data, transform data, load data, after_run
И будет выполнять конвейеры по очереди в порядке, указанном в stage.rb файл.

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

изображение.png


Содержимое ProjectPipeline:

изображение.png


в ProjectPipeline.load, есть звонок Projects::CreateService.executeс параметром params = data. Как

изображение.png

ProjectPipeline:
extractor ::BulkImports::Common::Extractors::GraphqlExtractor, query: Graphql::GetProjectQuery
transformer ::BulkImports::Common::transformers::ProhibitedAttributesTransformer
transformer ::BulkImports::Projects::transformers::ProjectAttributesTransformer

Согласно потоку Pipeline:
  • GraphqlExtractor.extract будет получать данные от цели через graphql
  • ProhibitedAttributesTransformer& ProjectAttributesTransformerизменит полученные данные
С GraphqlExtractor, в фиксации исправления GitLab Graphql::GetProjectQuery фиксируется следующим образом:

изображение.png


Здесь отчетливо видно, что количество переменных, подлежащих извлечению, в основном уменьшено.
Вот пример данных, полученных из GraphqlExtractor:

изображение.png


С ProhibitedAttributesTransformer, основная функция этого преобразователя — удалить некоторые чувствительные атрибуты:

изображение.png


С ProjectAttributesTransformer.execute:

изображение.png


Этот метод берет данные, делает несколько дополнительных шагов для установки необходимых атрибутов, таких как import_type, name, pathа потом звонит data. transform_keys!(&:to_sym) для выполнения конвертации. Все ключи хэша только что перешли в форму символа.

// В Ruby есть концепция Symbol vs String. Примерно символ будет иметь двоеточие «:» впереди

Вот пример после выполнения data.transform_keys!(&:to_sym)

изображение.png

  • И помни, dataпо-прежнему полностью управляем, поскольку он извлекается из GraphQL, GraphQL и берется с нашего веб-сайта.
Возвращаясь к импорту, упомянутому в случае 1, мы можем полностью записать project.import_source, из которого мы можем управлять @archive_fileи RCE (〜 ̄▽ ̄)〜

изображение.png

изображение.png


В исправлении фиксации ProjectAttributesTransformer, вместо того, чтобы принимать полученное data, этот преобразователь создал новый хэш и добавил только некоторые необходимые ключи/значения и вернул чистый хеш, что означает, что другие атрибуты не были добавлены:

изображение.png


В настоящее время, data после извлечения и преобразования будут переданы в Projects::CreateService.execute

изображение.png


К сожалению, проекты, созданные из ProjectPipeline, могут иметь только import_type = gitlab_project_migration

изображение.png


Однако на import_schedule, этот проект будет отклонен по условию !@project.gitlab_project_migration?
Код:
   def import_schedule
       if @project.errors.empty?
         @project.import_state.schedule if @project.import? && !@project.bare_repository_import? && !@project.gitlab_project_migration?
       else
         fail(error: @project.errors.full_messages.join(', '))
       end
     end

Хотя можно управлять атрибутами объекта Project, самый важный атрибут был перезаписан, и нет возможности его переопределить (на самом деле да, но я расскажу об этом в другом посте).

Случай 1 + 2 = 3

И… я застрял там почти на 2 недели, выясняя возможности отладки ruby. Иногда он касается точки останова, а иногда нет. Иногда RubyMine внезапно падает, что вызывает у меня головную боль.
До последних нескольких дней я находил другой способ ускорить отладку без включения сервера GitLab, а именно использовать для отладки GitLab RSpec. Удобство этого метода заключается в том, чтобы не ждать sidekiq
Оттуда я начал отлаживать ProjectPipeline с помощью project_pipeline_spec.rb, изменить некоторые данные, связанные с project_dataа затем запустите его:

изображение.png


Благодаря этому моя отладка проходит очень быстро, и я достиг новых результатов.
После внимательного повторного прочтения Projects::CreateService.executeветка, я понял, что пропустил ветку обработки шаблона:

изображение.png


изображение.png


Эта ветвь звонит Projects::CreateFromTemplateService.executeс params, который dataвзято из ProjectPipeline.
Этот метод в основном проверяет наличие template_name, затем звонит GitlabProjectsImportService.execute вместе с paramsдля дальнейшей обработки:

изображение.png


Как обсуждалось в части 1, GitlabProjectsImportService.execute затем позвонит prepare_import_paramsдля обработки параметров:

изображение.png


Здесь, если template_file существует, программа перезапишет параметр import_typeк gitlab_project.
После обработки параметра GitlabProjectsImportService.execute позвоню Projects::CreateService.execute чтобы воссоздать проект с измененными параметрами.
Итак import_type был изменен на gitlab_project, который по-прежнему повторно использует старый Pipeline params=> RCE ( ͡° ͜ʖ ͡°)( ͡° ͜ʖ ͡°)( ͡° ͜ʖ ͡°)
Там было примечание, что введенная команда не будет выполнена немедленно!
В Gitlab::ImportExport::FileImporter.import, wait_for_archived_file метод будет вызываться для ожидания @archive_file наличие перед входом в нижнюю обрабатывающую ветку (ветвь, в которую мы вводим команду)
Содержание wait_for_archived_file:
С MAX_RETRIES= 8, эта программа будет выполнять цикл 8 раз, чтобы дождаться существования файла, каждый раз, когда она будет спать 2 ^ i, я применяю формулу для вычисления суммы степенного ряда. Это могут проверить первокурсники, и мы знаем, что нам придется ждать 2^8 -1 = 255 секунд, если файл не существует:
И в случае, если файл не существует, этот метод также продолжает вызывать yield внизу, что означает, что операторы после wait_for_archived_fileпо-прежнему вызываются нормально, например:
На данный момент с этим багом все понятно, хотя процесс чтения Ruby/Rails достаточно болезненный, но и он приносит много знаний и кое-что интересное.

Видео с доказательством концепции:

Пришло время демонстрации!

Если видео не удалось загрузить, вот резервная копия
Спасибо за чтение!

© 2022 СТАР Лабс Питаться от Хьюго и БумагаМод
 
Пожалуйста, обратите внимание, что пользователь заблокирован
cool article! thanks mate.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх