
CVE-2020-13277 Полигон: логическая уязвимость Gitlab — несанкционированный доступ любого пользователя к приватным репозиториям
CVE-2020-13277 Лаборатория: логическая уязвимость Gitlab — несанкционированный доступ любого пользователя к приватным репозиториям
CVE-2020-13277
├── README.md ............... [данный файл README]
├── imgs .................... [изображения, дополняющие описание в README]
├── gitlab .................. [каталог монтирования контейнера Gitlab]
│ ├── Dockerfile .......... [Docker-файл сборки Gitlab]
│ ├── config .............. [каталог монтирования конфигурации Gitlab]
│ ├── data ................ [каталог монтирования данных Gitlab]
│ ├── logs ................ [каталог монтирования журналов Gitlab]
│ ├── keys ................ [каталог хранения взломанной лицензии Gitlab]
│ └── runner .............. [каталог монтирования контейнера Runner]
├── license ................. [каталог сборки контейнера для взлома лицензии]
│ ├── Dockerfile .......... [Docker-файл сборки лицензии]
│ └── license.rb .......... [Ruby-скрипт генерации взломанной лицензии]
├── docker-compose.yml ...... [конфигурация сборки Docker]
├── keygen.ps1 .............. [Windows: создание взломанной лицензии одной командой]
├── keygen.sh ............... [Linux: создание взломанной лицензии одной командой]
├── run.ps1 ................. [Windows: запуск лаборатории Gitlab одной командой]
├── run.sh .................. [Linux: запуск лаборатории Gitlab одной командой]
├── register.ps1 ............ [Windows: регистрация Runner одной командой]
├── register.sh ............. [Linux: регистрация Runner одной командой]
├── stop.ps1 ................ [Windows: остановка лаборатории Gitlab одной командой]
└── stop.sh ................. [Linux: остановка лаборатории Gitlab одной командой]
Ядро данной уязвимости — использование функции зеркальной синхронизации и резервного копирования репозиториев Mirror Repository.
Направление синхронизации Mirror бывает двух видов:
Данная уязвимость использует Mirror Repository в направлении Pull
Следует знать, что Gitlab делится на два варианта: CE (бесплатная версия для сообщества) и EE (платная версия для предприятия), а Gitlab официально заявляет, что эта уязвимость затрагивает следующие версии как CE, так и EE:
>=10.6, <12.9.10>=12.10, <12.10.11>=13.0, <13.0.6Но это не означает, что Gitlab Docker Image этих версий можно использовать для создания лаборатории, поскольку:
Иными словами, для развёртывания лаборатории с помощью Docker остаётся только Gitlab-EE, который необходимо взломать (состоятельные также могут купить License), чтобы активировать функцию Mirror Repository - Pull.
Но даже после взлома Gitlab-EE, будь то 10.x, или , когда URL для Mirror Repository - Pull содержит локальный путь, появляется ошибка .
./keygen.sh или ./keygen.ps1./run.sh или ./run.ps1При генерации ключевой пары для взлома открытый ключ уже был записан внутрь контейнера Gitlab; осталось загрузить закрытый ключ через веб-интерфейс, чтобы завершить взлом:
./gitlab/keys/; скопируйте содержимое файла .gitlab-license (закрытый ключ)Enter license key, вставьте закрытый ключ и нажмите кнопку Upload license — взлом завершёнНа этом функция Mirror Repository - Pull активирована

Outbound requests, отметьте флажком Allow requests to the local network from hooks and services и сохраните измененияНа этом Mirror Repository - Pull поддерживает втягивание локальных Repository

./register.sh $TOKEN или ./register.ps1 $TOKENНа этом все Repository смогут использовать этот Runner для выполнения CI-скриптов (Pipeline Jobs)

Процесс проверки можно подсмотреть в официальном Issue, однако приведённый ниже процесс проверки содержит небольшие корректировки некоторых шагов под данную лабораторию
Под пользователем root откройте страницу http://127.0.0.1/admin/users и создайте 3 учётные записи:
victim: учётная запись жертвыattacker1: учётная запись атакующего 1attacker2: учётная запись атакующего 2При создании учётной записи задать начальный пароль нельзя — Gitlab по умолчанию отправляет начальный пароль на указанный Email. Для удобства можно указать любой Email, создать учётную запись, а затем сразу же отредактировать её: в этом случае root сможет задать начальный пароль без обращения к Email

victimNew Project:
targetPrivateREADME.md и задайте его содержимое, например mykey is abcxyzОчевидно,
target— приватный репозиторийvictim, и наша цель — получить содержимое этого репозитория, используя уязвимость

attacker1New Project:
pocPublic.gitlab-ci.yml со следующим содержимым:image: "ruby:2.6"
rspec:
script:
- git clone http://gitlab-ci-token:[email protected]/victim/target.git
- cd target
- ls -lah .
- cat README.md
【Цель】В ходе последующих операций с помощью некоторых приёмов victim, не подозревая об этом, выполнит данный CI-скрипт со своими правами.
Поскольку в текущей лаборатории не настроены сертификаты, доступен только протокол
http; кроме того,172.168.30.2— это IP, назначенный контейнеру Gitlab вdocker-compose.yml. Так как CI-скрипт в конечном счёте выполняется через Runner, а Runner и Gitlab в данной лаборатории находятся в разных контейнерах, нужно использовать IP-адрес, назначенный Docker.

attacker2New Group:
testPublictest создайте полностью пустой репозиторий New Project:
pocPublic
Настройте зеркальный репозиторий в разделе Settings => Repository => Pull from a remote repository:
Mirror repository: установите флажокGit repository URL: укажите http://GITLAB/attacker1/pocPassword: (оставьте пустым — репозиторий для Pull имеет уровень доступа Public, пароль не нужен)Trigger pipelines for mirror updates: установите флажок (запуск CI-скриптов при синхронизации зеркала)После успешной настройки каждые 30 минут будет проверяться, изменился ли исходный репозиторий; если изменения есть, выполняется принудительная синхронизация.
Git repository URLуказывает на созданный ранее poc-репозиторий. 127.0.0.1 не используется, потому что Pull запрещает втягивание локальных репозиториев, но это можно обойти с помощью DNS:GITLAB— это hostname, назначенный контейнеру Gitlab вdocker-compose.yml; по умолчанию он прописывается в/etc/hosts. Хотя хост-машина не может разрешитьGITLAB, внутри контейнера это эквивалентно обращению кhttp://127.0.0.1/attacker1/poc

attacker2testvictim в группу:
Add new member to test: victimPermissions: OwnerExpiration date: (оставьте пустым — без срока действия)=> Setting => Account => Delete account, чтобы удалить текущую учётную запись (attacker2)【Цель】В группе test есть только два Owner — victim и attacker2. После удаления пользователя attacker2 право владения группой test и всеми её репозиториями принудительно перейдёт к victim. Сейчас в группе test есть репозиторий test/poc, синхронизированный зеркалом из attacker1/poc, то есть репозиторий test/poc принудительно передаётся пользователю victim.


Сначала подведём итог текущей ситуации:
victim репозиторий test/poctest/poc синхронизируется зеркалом из attacker1/poc (каждые 30 минут проверяется, не изменился ли он и не нужна ли синхронизация)attacker1/poc, включая CI-скрипт .gitlab-ci.yml, контролируется атакующимtest/poc включён Trigger pipelines for mirror updates, при каждой синхронизации выполняется CI-скриптOwner репозитория test/poc — victim, CI-скрипт выполняется с правами victim
Вспомним содержимое CI-скрипта .gitlab-ci.yml, заданного ранее: этот poc в Runner с правами victim обращается к структуре каталогов и README.md его Private-репозитория target:
image: "ruby:2.6"
rspec:
script:
- git clone http://gitlab-ci-token:[email protected]/victim/target.git
- cd target
- ls -lah .
- cat README.md
После этого атакующему достаточно произвольно изменить какое-либо незначительное содержимое в репозитории attacker1/poc — в худшем случае просто подождать 30 минут, — и заданный CI-скрипт выполнится с правами victim.
Хотя атакующий не имеет прямого доступа к результатам выполнения Pipeline Jobs, достаточно изменить цель вывода скрипта — например, отправлять содержимое репозитория на указанный Email или FTP-сервер — и код репозитория будет похищен.

12.x13.xImport url is blocked: Requests to localhost are not allowedХотя после установки параметра Allow requests to the local network from hooks and services в разделе Admin area => Settings => Network => Outbound requests локальный URL настроить можно, при синхронизации зеркала возникает ошибка 2:Fetching remote upstream failed: fatal: unable to access http://127.0.0.1/xxxx/: The requested URL returned error: 301. Иными словами, работать будет только Pull Remote Repository.
К счастью, хотя в 12.x и 13.x проверка локального URL очень строгая, в версии 10.x есть способ обойти её: при настройке Pull URL достаточно указать имя локально настроенного DNS-сервиса.
Таким образом, для построения данной лаборатории в итоге подходит только Docker Image версии gitlab-ee:10.6.0-ee.0.
Собственно, из описанного выше видно, что условия эксплуатации этой уязвимости довольно жёсткие, и в основном небогатым пользователям она вряд ли угрожает