Кто угодно может получать доступ к данным из удалённых форков, удалённых репозиториев и даже приватных репозиториев GitHub. И эти данные доступны всегда. Это известно разработчикам GitHub, и они намеренно спроектировали систему таким образом.
Это настолько огромный вектор атак для всех организаций, использующих GitHub, что мы решили ввести новый термин: Cross Fork Object Reference (CFOR). Уязвимость CFOR возникает, когда форк одного репозитория может получить доступ к требующим защиты данным из другого форка (в том числе и к данным из приватных и удалённых форков). Аналогично Insecure Direct Object Reference, при CFOR пользователи передают хэши коммитов, чтобы напрямую получать доступ к данным коммитов, которые иначе были бы для них невидимыми.
Давайте рассмотрим несколько примеров.
Получение доступа к данным удалённых форков
Возьмём такую часто используемую в GitHub последовательность действий:
- Пользователь создаёт форк публичного репозитория.
- Далее он выполняет коммит кода в свой форк.
- Он удаляет свой форк.
Доступен ли по-прежнему код, коммит которого был выполнен в форк? Вроде бы не должен быть, правда? Мы ведь его удалили.
Но он доступен. И будет доступен всегда. Вне вашего контроля.
В показанном ниже видео мы создаём форк репозитория, коммитим в него данные, удаляем форк, а затем получаем доступ к «удалённым» данным коммита через исходный репозиторий.
Вы можете подумать, что защищены необходимостью знать хэш коммита. Но это не так. Хэш можно узнать, подробнее об этом ниже.
▍ Как часто можно находить данные из удалённых форков?
Достаточно часто. Мы изучили три часто форкаемых публичных репозиторя крупной ИИ-компании, и с лёгкостью нашли 40 валидных ключей API из удалённых форков. Похоже, пользователи применяли такой паттерн:
- Создавали форк репозитория.
- Прописывали ключ API в файле примера.
- Выполняли нужные им действия.
- Удаляли форк.
Но ещё хуже, что можно сделать и наоборот:
Доступ к данным удалённых репозиториев
Рассмотрим следующий сценарий:
- У вас есть публичный репозиторий GitHub.
- Пользователь создаёт форк этого репозитория.
- Вы коммитите данные после того, как он создал форк (и он никогда не синхронизирует свой форк с вашими обновлениями).
- Вы удаляете репозиторий целиком.
Будет ли код, который вы закоммитили после форка, по-прежнему доступным?
Да.
GitHub хранит репозитории и форки в сети репозиториев, где исходный upstream-репозиторий используется в качестве корневого узла. Когда удаляется публичный upstream-репозиторий, для которого создавались форки, GitHub назначает на роль корневого узла один из downstream-форков. Однако все коммиты из upstream-репозитория всё равно существуют и доступны из любого форка.
В показанном ниже видео мы создаём репозиторий, форкаем его, а затем демонстрируем, что к данным, не синхронизируемым с форком, всё равно можно получить доступ через форк после удаления исходного репозитория.
И это не просто какой-то пограничный сценарий. Вот, что произошло у меня недавно:
Я отправил уязвимость уровня P1 крупной технологической компании, показав, что она случайно закоммитила приватный ключ к аккаунту GitHub сотрудника, имеющего высокий уровень доступа к GitHub всей организации. Компания немедленно удалила репозиторий, но поскольку к нему создавались форки, я всё равно смог получить через форк доступ к коммиту, содержащему конфиденциальные данные, хотя форк никогда не синхронизировался с исходным upstream-репозиторием.
Из этого следует сделать вывод, что любой код, закоммиченный в публичный репозиторий, будет доступен вечно, пока существует хотя бы один форк этого репозитория.
Но и это ещё не всё.
Получение доступа к данным приватных репозиториев
Рассмотрим часто используемый процесс вывода нового инструмента в open source на GitHub:
- Вы создаёте приватный репозиторий, который позже будет сделан публичным.
- Далее создаёте приватную внутреннюю версию этого репозитория (форкнув его) и коммитите дополнительный код фич, которые не будете делать публичными.
- Вы делаете upstream-репозиторий публичным и оставляете форк приватным.
Будут ли приватные фичи и связанный с ними код (из шага 2) видны широкой публике?
Да. Любой код, закоммиченный между моментом создания внутреннего форка инструмента и опенсорсингом инструмента, будет доступен в публичном репозитории.
Все коммиты, внесённые в приватный форк после того, как вы сделали upstream-репозиторий публичным, доступны не будут. Это вызвано тем, что изменение видимости приватного upstream-репозитория приводит к созданию двух сетей репозиториев — одной для приватной версии, другой — для публичной.
В видео ниже мы показываем, как организации выводят в open source новые инструменты, в то же время сохраняя приватные внутренние форки, а затем демонстрируем, как можно получить доступ к закоммиченным данным приватной внутренней версии через публичную.
К сожалению, именно так чаще всего пользователи и организации подходят к разработке опенсорсного ПО. Из-за этого возможно непреднамеренное раскрытие конфиденциальных данных и секретов в публичных репозиториях GitHub организации.
Как же получить доступ к данным?
Операции уничтожения в сети репозиториев GitHub (подобные описанным в трёх приведённых выше сценариях) удаляют ссылки на данные коммитов из стандартного UI GitHub и обычных операций git. Однако эти данные всё равно существуют, и к ним можно получить доступ (если знать хэш коммита). В этом заключается связь между уязвимостями CFOR и IDOR — если вы знаете хэш коммита, то сможете напрямую получать доступ к данным, не предназначенным для вас.
Хэши коммитов — это значения SHA-1.
Если пользователь знает хэш SHA-1 конкретного коммита, который он хочет просмотреть, то он может напрямую перейти к этому коммиту в конечной точке: https://github.com/<user/org>/<repo>/commit/<commit_hash>
. Он увидит жёлтый баннер с информацией о том, что «этот коммит не принадлежит ни к одной из ветвей этого репозитория и может принадлежать форку вне репозитория».
Откуда взять эти значения хэшей?
Хэши коммитов можно подобрать брутфорсом через UI GitHub, и в особенности потому, что протокол git допускает использование коротких значений SHA-1 при ссылках на коммит. Короткое значение SHA-1 — это минимальное количество символов, необходимое для предотвращения коллизий с хэшем другого коммита. Абсолютно минимальное значение равно 4. Пространство ключей всех значений SHA-1 из четырёх символов составляет 65536 (16^4). Брутфорс всех возможных значений можно выполнить достаточно просто.
Рассмотрим для примера этот коммит из нашего репозитория:
Для получения доступа к этому коммиту пользователи обычно переходят к URL, содержащему полный хэш SHA-1 коммита: https://github.com/trufflesecurity/trufflehog/commit/07f01e8337c1073d2c45bb12d688170fcd44c637.
Но им необязательно знать всё значение SHA-1 из 32 символов, достаточно лишь правильно угадать короткое значение SHA-1, которое в данном случае равно 07f01e
.
https://github.com/trufflesecurity/trufflehog/commit/07f01e
Но вот, что ещё более интересно: GitHub раскрывает публичную конечную точку API событий. Можно также запрашивать хэши коммитов в архиве событий, управляемом третьей стороной. Он сохраняет все события GitHub за последний десяток лет вне пределов GitHub даже после удаления репозиториев.
Политики GitHub
Недавно мы отправили отчёт о своих находках в GitHub в рамках его программы VDP. Был получен следующий ответ:
После изучения документации стало абсолютно понятно, что GitHub осознанно спроектировал репозитории таким образом.
Мы ценим то, что GitHub прозрачно раскрывает свою архитектуру и уделил время документированию того, что должны ожидать пользователи в подобных случаях.
Но для нас проблема заключается в следующем:
Среднестатистический пользователь рассматривает такое разделение репозиториев на приватные и публичные как границу безопасности и вполне логично предполагает, что все данные, хранящиеся в приватном репозитории, будут недоступны для публичных пользователей. К сожалению, как показано в приведённой выше документации, это не всегда так. Кроме того, процесс удаления подразумевает уничтожение данных, но, как мы видели выше, удаление репозитория или форка не означает реального удаления данных коммита.
Последствия
Из этого можно сделать несколько выводов:
- Пока существует хотя бы один форк, любой коммит в эту сеть репозиториев (то есть коммиты в upstream-репозиторий или в downstream-форки) будет существовать вечно.
- Это ещё больше убеждает нас в нашем мнении о том, что единственный способ устранить последствия утечки ключа в публичный репозиторий GitHub — это ротация ключей. Мы потратили много времени на документирование способов ротации ключей для самых популярных утёкших типов секретов, изучите нашу работу здесь: howtorotate.com.
- Архитектура репозиториев GitHub нуждается в этих слабостях дизайна и, к сожалению, подавляющее большинство пользователей GitHub никогда не будет разбираться в том, как работает сеть репозиториев, что повредит их защите.
- Мы надеемся, что благодаря совершенствованию сканирования секретов мы сможем просканировать все коммиты в сети репозиториев и будем отправлять предупреждения о секретах, которые могут принадлежать не нам (например, они могут принадлежать тому, кто форкнул репозиторий). Это потребует более тщательной сортировки.
- Эти три сценария могут выглядеть пугающими, однако это не все способы, которыми GitHub способен хранить удалённые из репозиториев данные. В нашем недавнем посте рассказывается о том, что вам нужно сканировать на наличие секретов и удалённые ветви.
Кроме того, несмотря на то, что наше исследование посвящено только GitHub, важно отметить, что некоторые из данных проблем свойственны и другим системам управления версиями.
Автор: ru_vds