Есть такая штука nextjs, несколько месяцев назад они добавили новую "фишку" распределение нагрузки - под капотом поднимается несколько микросервисов. Каждый из которых занимается своей частью обработки запроса. Работает все примерно так-же как и обычный прокси сервер, запрос приходит, обрабатывается, и через стороннюю библиотеку проксируется на другой порт.
Собственно именно это место нас и интересует.
В HTTP есть такой метод PRI (используются, если мне не изменяет память, чтобы проверить возможность HTTP/2 подключения). Нас интересует что запросы этого метода имеют cимвол * в пути, вместо привычного /.
PRI * HTTP/1.1.
Далее, все http-парcеры работают по разному, у nodejs он немного особенный, и url вида http://example.com:3456*@127.0.0.1:8080 будет разобран как:
hostname: 127.0.0.1,
port: 8080,
auth: example.com:3456*,
А самое важное тут то, что http-парсер nodejs считает валидными запросы вида:
GET *@127.0.0.1/ HTTP/1.1
Host: somehost
А знаете кто еще так делает - haproxy!
Ну и последнее: код который формирует url для проксирования внутрь nextjs выглядит так:
const targetUrl = `http://${
targetHost === 'localhost' ? '127.0.0.1' : targetHost
}:${routerPort}${pathname}`
Собственно, т.к. мы контролируем pathname - мы можем отправить запрос вида:
GET *@127.0.0.1:11211 HTTP/1.1
Host: some
и запрос уйдет напрямую в memcached, получаем SSRF.
Но что более важное - это SSRF с возможностью чтения ответа: если внутри, в инфре, есть приватная веб-морда, мы можем спокойно обращаться к ней, как будь то бы она торчит наружу.
Уязвимые версии:
>= 13.3.0 | <=13.4.12
фича проксирования включена по умолчанию, она так же включается если установлен флаг appDir: true в конфигурации experimental.
Из интересного: vercel отказал в CVE, так как не считает что это уязвимость (на момент репорта уязвимость воспроизводилась в последних версиях). Однако с версии 13.4.13 изменили код. Теперь в части внутренних запросов используется fetch. Что дает некоторую защиту, т.к. fetch не принимает запросы содержащие авторизацию.
>