Semgrep
ОРИГИНАЛЬНАЯ СТАТЬЯ
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro
$600 ---> 0x5B1f2Ac9cF5616D9d7F1819d1519912e85eb5C09 для поднятия ноды ETHEREUM и тестов
Команда исследователей безопасности Oxeye обнаружила уязвимость в приложениях на базе Golang. При определенных условиях она позволяет угрожающему субъекту обойти проверку на основе параметров HTTP-запроса из-за использования небезопасного разбора URL.
Анализ URL 101
URL представляют собой интернет-адрес. Браузер инициирует HTTP-запрос для получения определенного ресурса, идентифицируемого по его URL. Целевой веб-сервер переводит текстовый URL в понятную ему форму - это называется парсингом URL. Этот процесс обычно выполняется в соответствии с RFC 3986, который содержит инструкции по правильному парсингу URL. Ниже приведен отрывок, иллюстрирующий компоненты URL:
Каждый я
Golang + парсинг URL = ?
Недавно основная команда Golang выпустила патч, который коренным образом меняет то, как этот язык парсит URL. До версии 1.17 Golang считал точку с запятой в части запроса URL допустимым разделителем, подобно тому, как оценивается амперсанд. Это поведение исходит из метода parseQuery:
Например, следующий URL дает два параметра запроса, имя и компания, а не один параметр имени:
Golang изменил это поведение, начиная с версии 1.17. Теперь его метод parseQuery возвращает ошибку, если запрос URL содержит точку с запятой:
Хотя метод parseQuery был исправлен, чтобы правильно возвращать ошибку, когда входные данные содержат точку с запятой, один из методов, отвечающий за получение разобранной строки запроса, легкомысленно игнорирует возвращаемую ошибку:
Более того, Golang предлагает встроенную функцию для проксирования HTTP-трафика с помощью модуля httputil/reverseproxy.go. Для создания нового обратного прокси он предоставляет метод NewSingleHostReverseProxy:
При этом берется сырая строка запроса, а не после парсинга. Но вместо того, чтобы парсить ее, он отправляет ее как есть. Теперь вы увидели поведенческие различия между версиями Golang, но как эти точки связаны между собой?
Соединяем все вместе
Рассмотрим следующие два сервиса на базе Golang. Первый - это приложение для пользователя, работающее на версии 1.17 или более поздней, а второй, бэкенд-сервис, работает на более ранней версии.
Пользователь делает HTTP-запрос к первому сервису, предоставляя параметр запроса name. Первый сервис принимает решение о прохождении запроса, основываясь на заданном параметре.
Предположим, пользователь добавляет точку с запятой к параметру name при разборе строки запроса. В этом случае первая служба игнорирует ее существование; вместо того чтобы принять логическое решение на ее основе, запрос пересылается второй (бэкенд) службе. Последний получает транзакцию и обрабатывает параметр без точки с запятой.
Это означает, что злоумышленники могут незаконно передавать запросы, содержащие параметры запроса, которые в обычных условиях были бы отклонены. В ходе нашего исследования Oxeye обнаружил множество примеров этой уязвимости в нескольких проектах с открытым исходным кодом.
Harbor
С официального сайта Harbor: "Harbor - это реестр с открытым исходным кодом, который обеспечивает безопасность артефактов с помощью политик и ролевого контроля доступа... Harbor, проект CNCF Graduated, обеспечивает соответствие, производительность и совместимость, чтобы помочь вам последовательно и безопасно управлять артефактами на облачных вычислительных платформах, таких как Kubernetes и Docker".
Другими словами, Harbor позволяет управлять артефактами приложений. Одна из его многочисленных особенностей заключается в том, что вы можете централизовать несколько реестров образов под одной крышей. В ходе нашего исследования мы обнаружили, что при определенных условиях развертывания аутентифицированный пользователь (даже с самым низким уровнем прав) может отправить специальный запрос на чтение слоев образов ограниченных проектов, к которым у него нет доступа. Harbor использует реестр контейнеров дистрибутива для хранения и управления образами Docker. Читая API V2 реестра Docker, мы увидели конечную точку, которая позволяет выполнять Cross Repository Blob Mount. Эта функция позволяет экономить дисковое пространство и пропускную способность сети при хранении/загрузке образов Docker, указывающих на одни и те же слои файловой системы.
Здесь показан запрос, который пытается смонтировать пакет из другого репозитория:
Чтобы смонтировать блоб из другого хранилища, конечная точка ожидает получить один параметр пути URL - имя (указывающее целевое хранилище) - и два параметра запроса:
mount (хэш-дайджест монтируемого слоя)
from (имя иностранного хранилища, в котором уже находится слой, на который ссылаются).
Когда Harbor получает такой запрос, он пытается выполнить две проверки прав доступа:
Проверяет, имеет ли пользователь push-доступ к хранилищу, указанному в параметре URL <name>.
Проверяет, есть ли у пользователя pull-доступ из хранилища, указанного в параметре запроса <from>.
Oxeye искал как разверннуть Harbor, в котором есть это расхождение в версии Golang. Мы обнаружили, что Bitnami (компания, приобретенная VMWare, которая хранит графики Helm и образы Docker для различных проектов) имеет именно такую настройку. Его основной микросервис Harbor работает на Golang 1.17, а микросервис реестра Docker - на версии 1.15.
Дважды мы пытались отправить запрос на монтирование блобов из разных хранилищ через Harbor - один раз с правильным значением from и один раз с тем же параметром, который включал точку с запятой.
Оригинальный запрос - заблокирован из-за проверки авторизации Harbor:
Измененный запрос - в обход проверки авторизации Harbor, ответ подтверждает, что пакет (полученный из реестра) успешно смонтирован:
Чтобы успешно использовать эту уязвимость, злоумышленнику потребуется знать хэш дайджеста пакета для его монтирования. Эта проблема будет рассмотрена в следующей статье блога.
Дополнительные примеры
В ходе нашего исследования Oxeye обнаружил другие проекты, которые разбирают строки запросов, используя тот же небезопасный метод, который мы рассмотрели.
Traefik
Traefik - это современный обратный HTTP-прокси и балансировщик нагрузки, который упрощает развертывание микросервисов. Одна из функций позволяет блокировать определенные запросы на основе параметров запроса. Например, следующая конфигурация не передает запросы, содержащие параметр foo:
За кулисами Traefik использует уязвимый метод разбора строки запроса. При проксировании запроса на внутренний сервер передается как исходный запрос, так и строка запроса. Запрос с параметром foo, содержащим точку с запятой, обойдет этот фильтр правил.
Skipper
Skipper — это HTTP-маршрутизатор и обратный прокси-сервер для составления сервисов, который также использует анализ уязвимых запросов. Например, запросы с параметром foo будут отклонены при использовании следующей конфигурации:
Запрос с параметром foo и точкой с запятой в конце обошел фильтр соответствия правилам, в результате чего Skipper не отклонил запрос.
Исправление
Golang предлагает другие методы для разбора строк запроса, поэтому рассмотрите возможность их использования. Например, метод ParseQuery позволяет разработчикам рассмотреть возвращаемую ошибку:
Если вы по-прежнему хотите использовать уязвимый метод, рассмотрите возможность очистки необработанного запроса таким образом, чтобы ввод, содержащий точку с запятой, отклонялся до вызова метода (хотя существуют и другие способы маскировки ввода, например, кодирование URL).
Чтобы помочь сообществу найти такие уязвимости, Oxeye написал Semgrep, которое предупреждает вас, если в вашей кодовой базе используется уязвимый метод. Оно предлагает автоматическое исправление с использованием более безопасного метода:
https://semgrep.dev/s/daniel-abeles:golang-http-parameter-smuggling-assignment