Библиотека с 60 миллионами загрузок в неделю получает CVSS 10.0 - и сообщество предсказуемо раскалывается надвое. Одни кричат «всё пропало», другие - «это не эксплуатируемо». С CVE-2026-40175 в Axios произошло именно так. Заголовки в духе «полная компрометация облака» соседствуют с заявлением самого исследователя, нашедшего баг: «в реальном продакшене это не должно сработать». Истина посередине - и для пентестера критично понимать, где именно эта середина проходит.
Я разобрал всю цепочку руками: от prototype pollution через CRLF-инъекцию до request smuggling и обхода AWS IMDSv2. Согласно NVD, цепочка может быть эскалирована до RCE или полной компрометации облака - здесь фокус на SSRF/credential theft; RCE достижим через пост-эксплуатацию украденных credentials (например, через AWS SSM Run Command или Lambda). Дальше - пошаговый разбор каждого звена, точные места, где цепочка ломается в Node.js, и конкретные edge-кейсы, где она всё-таки работает. Плюс детект, митигация и маппинг на MITRE ATT&CK.
Три CWE в одном CVE: почему CVSS-вектор = 10.0
CVE-2026-40175 (advisory GHSA-fvcv-3m26-pcqx) - не одна уязвимость, а цепочка из трёх связанных слабостей. Именно их комбинация даёт максимальный CVSS:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H = 10.0 (CRITICAL)
Вектор по компонентам:
| Компонент | Значение | Что означает |
|---|---|---|
| AV:N | Network | Атака по сети, без физического доступа |
| AC:L | Low | Никаких специальных условий |
| PR:N | None | Привилегии не требуются |
| UI:N | None | Действие пользователя не требуется |
| S:C | Changed | Атака выходит за пределы уязвимого компонента |
| C:H/I:H/A:H | High | Полная компрометация конфиденциальности, целостности, доступности |
Ключевой момент - Scope: Changed. Уязвимость в Axios позволяет атаковать другие компоненты системы (AWS IMDS, внутренние сервисы). Именно поэтому 10.0, а не 9.8.
Три CWE из advisory (GHSA-fvcv-3m26-pcqx):
-
Ссылка скрыта от гостей- Improper Neutralization of CRLF Sequences in HTTP Headers. Отсутствие санитизации значений заголовков позволяет инъекцию
\r\n. - CWE-444 - HTTP Request/Response Smuggling. Инъецированные CRLF-последовательности формируют «контрабандный» HTTP-запрос внутри легитимного.
-
Ссылка скрыта от гостей- Server-Side Request Forgery (SSRF). Контрабандный запрос перенаправляется на внутренние эндпоинты, включая AWS IMDS.
Анатомия Gadget Chain: как Prototype Pollution превращается в SSRF и Cloud Compromise
По данным security advisory, уязвимый код сидит вlib/adapters/http.js - HTTP-адаптере Axios для Node.js. Разберём цепочку шаг за шагом.Шаг 1: Prototype Pollution в сторонней зависимости
Первое звено - не сам Axios. Атакующему нужен «вход» через prototype pollution в любой другой библиотеке из дерева зависимостей. Как указал мейнтейнер Axios Джейсон Сааймен, подходящие кандидаты -qs, minimist, ini, body-parser и подобные пакеты, парсящие пользовательский ввод.
JavaScript:
// Пример: prototype pollution через уязвимый парсер
// Атакующий контролирует входные данные, например через query string
// Уязвимая библиотека записывает свойство в Object.prototype
Object.prototype['x-custom-header'] = 'injected-value';
[B]proto[/B] или constructor.prototype знакомы многим по lodash, merge-deep и десяткам других пакетов.Шаг 2: Axios подхватывает «загрязнённые» свойства
Вот где Axios становится «гаджетом». При формировании конфигурации запроса Axios выполняет merge объектов. ЕслиObject.prototype загрязнён свойством, имя которого выглядит как HTTP-заголовок, Axios подхватит его при слиянии конфигов.
JavaScript:
// Приложение делает обычный запрос - никакого пользовательского ввода
axios.get('https://internal.service/api/data');
// Внутри Axios: при merge конфигурации
// polluted-свойство 'x-amz-target' наследуется из Object.prototype
// и попадает в headers итогового запроса
Шаг 3: CRLF-инъекция в заголовках (CWE-113)
До версии 1.15.0 Axios не проверял значения заголовков на наличие\r\n. Это позволяло сформировать payload, который «разрывает» HTTP-запрос:
JavaScript:
// Prototype pollution payload для CRLF-инъекции
Object.prototype['x-amz-target'] =
"dummy\r\n\r\nPUT /latest/api/token HTTP/1.1\r\n" +
"Host: 169.254.169.254\r\n" +
"X-aws-ec2-metadata-token-ttl-seconds: 21600\r\n\r\nGET /ignore";
// "GET /ignore" - absorber, поглощающий trailing-данные
// первого (легитимного) запроса, чтобы smuggled PUT был корректно разобран.
// Это только первый этап - получение IMDSv2-токена. Для полной эксплуатации
// нужен второй раунд инъекции с GET /latest/meta-data/iam/security-credentials/{role}
// и заголовком X-aws-ec2-metadata-token, содержащим украденный токен.
\r\n\r\n - терминатор HTTP-заголовков. Всё, что идёт после, интерпретируется как новый HTTP-запрос. Классический request smuggling (CWE-444).Шаг 4: Контрабандный запрос уходит в сокет
Axios передаёт заголовки низкоуровневой функции Node.jshttp.request(). В стандартном Node.js валидация заблокирует CRLF (подробности - в секции «Реальная эксплуатируемость»). Однако при использовании кастомного адаптера, пишущего напрямую в сокет, или альтернативного рантайма без такой валидации - контрабандный запрос будет записан в TCP-соединение. Сервер на другом конце видит два запроса вместо одного.Как smuggled-запрос попадает на IMDS. Контрабандный запрос сам по себе не перенаправляется на произвольный хост. Чтобы smuggled PUT к
169.254.169.254 дошёл до IMDS, нужен промежуточный компонент - reverse proxy, load balancer или HTTP-прокси, - который разбирает TCP-поток и маршрутизирует каждый HTTP-запрос независимо.Типичная модель: Axios шлёт запрос через HTTP forward proxy (например, Squid в режиме forward proxy), который видит в одном соединении два запроса - легитимный и контрабандный. Forward proxy обрабатывает первый, а второй (smuggled) маршрутизирует по указанному Host/URI на
169.254.169.254.Тут есть нюанс: reverse proxy (Nginx, ALB) и большинство load balancer'ов не маршрутизируют smuggled-запросы на произвольные хосты - они проксируют только на заранее сконфигурированные upstream'ы. ALB как AWS-сервис не будет проксировать запросы на link-local адреса IMDS. Без промежуточного hop - при прямом соединении Axios → целевой сервис - smuggled-запрос попадёт на тот же целевой хост, а не на IMDS.
Альтернативный сценарий: целевой сервис на EC2 сам обрабатывает smuggled-запрос и делает внутренний запрос к IMDS (SSRF на стороне backend'а). Это существенно сужает реальную поверхность атаки - нужен либо forward proxy с маршрутизацией по Host, либо backend, уязвимый к SSRF.
Обход AWS IMDSv2: как контрабандный PUT-запрос крадёт IAM-токены
Теперь самая «громкая» часть CVE-2026-40175 Axios уязвимости - обход AWS IMDSv2 и кража облачных credentials.Зачем нужен IMDSv2
AWS Instance Metadata Service (IMDS) доступен по адресу169.254.169.254 с любого EC2-инстанса. IMDSv1 отдавал данные на простой GET - это был любимый вектор для SSRF-атак. AWS ввёл IMDSv2, который требует двухэтапной аутентификации:
Bash:
# Шаг 1: получение сессионного токена через PUT-запрос
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
# Шаг 2: использование токена для запроса метаданных
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
"http://169.254.169.254/latest/meta-data/iam/security-credentials/"
Как цепочка обходит IMDSv2
Контрабандный запрос из Шага 3 выше - это именно PUT на/latest/api/token с заголовком X-aws-ec2-metadata-token-ttl-seconds: 21600. Если он дойдёт до IMDS, сервис вернёт валидный сессионный токен.Последовательность атаки для кражи IAM-credentials (Credential Access - Cloud Instance Metadata API, T1552.005):
- Prototype pollution через стороннюю зависимость
- Axios формирует запрос с CRLF-инъекцией в заголовках
- Контрабандный PUT-запрос попадает на
169.254.169.254 - IMDS возвращает сессионный токен в TCP-поток. В классическом request smuggling ответ на smuggled-запрос попадает не атакующему напрямую, а в response queue соединения - его получает следующий легитимный запрос в том же TCP-соединении (response queue poisoning). Для эксфильтрации атакующему нужен либо контроль над следующим запросом в соединении, либо out-of-band канал (smuggled-запрос перенаправляет ответ на контролируемый сервер), либо чтение ответа из потока Axios при кастомном адаптере.
- При успешной эксфильтрации токена - второй smuggled GET с этим токеном забирает IAM-credentials (требует отдельного раунда инъекции или заранее сформированной цепочки smuggled-запросов)
- Атакующий получает
AccessKeyId,SecretAccessKey,Token
Bash:
# Использование украденных credentials
export AWS_ACCESS_KEY_ID="украденный_key"
export AWS_SECRET_ACCESS_KEY="украденный_secret"
export AWS_SESSION_TOKEN="украденный_token"
# Проверка идентити
aws sts get-caller-identity
# Листинг S3-бакетов
aws s3 ls
Реальная эксплуатируемость: где цепочка ломается
А теперь - то, что русскоязычные источники практически не разбирают. Цепочка атаки красиво выглядит в теории, но в стандартном Node.js-окружении она не работает. И вот почему.Node.js блокирует CRLF в заголовках на уровне рантайма
Node.js отвергает символы\r\n в HTTP-заголовках начиная с ранних версий. Проверка зашита внутри нативного http.request():
JavaScript:
// Попытка отправить заголовок с CRLF в Node.js
const http = require('http');
http.request({
hostname: 'example.com',
headers: {
'x-test': 'hello\r\nInjected: yes'
}
});
// Результат: TypeError [ERR_INVALID_CHAR]: Invalid character in header content
Он же прямо сказал: «Это не должно быть возможно в реальных продакшн-приложениях».
Когда цепочка Prototype Pollution → RCE всё-таки работает
Но не спешите закрывать тикет. Есть конкретные сценарии, где цепочка эксплуатируема.1. Custom Axios adapter. Axios позволяет заменять HTTP-адаптер. Если приложение использует кастомный адаптер, который пишет данные напрямую в сокет через
net.Socket.write(), минуя http.request() - валидация Node.js не срабатывает. Лично я встречал такие адаптеры в проектах, где разработчики «оптимизировали» работу с HTTP, обходя стандартный стек.
JavaScript:
// Кастомный адаптер без валидации - концепт
const net = require('net');
function customAdapter(config) {
return new Promise((resolve) => {
const socket = net.createConnection({ host: config.host, port: 80 });
// Заголовки пишутся напрямую - без проверки CRLF
const raw = `GET ${config.url} HTTP/1.1\r\nHost: ${config.host}\r\n`;
for (const [key, value] of Object.entries(config.headers)) {
raw += `${key}: ${value}\r\n`; // value может содержать \r\n!
}
socket.write(raw + '\r\n');
// ... обработка ответа
});
}
3. Proxy-серверы и реверс-прокси. Если между Axios и целевым сервером стоит прокси, который обрабатывает запрос «as-is» без ре-валидации заголовков - контрабандный запрос может пройти.
4. Не-IMDSv2 цели. Даже если обход IMDSv2 не сработает, CRLF-инъекция открывает другие векторы: инъекция заголовков
Cookie или Authorization для доступа к внутренним сервисам, отравление кеша через заголовок Host.Итоговая оценка для пентестера
| Сценарий | Эксплуатируемость | Импакт |
|---|---|---|
| Стандартный Node.js + http.request | Нет | - |
| Кастомный Axios adapter (raw sockets) | Да | Критический |
| Альтернативный рантайм без CRLF-валидации | Возможно | Критический |
| Node.js + IMDSv1 (устаревший) | Частично | Высокий |
| Любой рантайм + не-IMDS цели (cache poisoning) | Зависит от конфигурации | Средний–Высокий |
Уязвимость реальна на уровне библиотеки, даже если рантайм её блокирует. Если завтра в Node.js найдут обход валидации заголовков - CVE-2026-40175 мгновенно станет эксплуатируемой. Defense in depth тут не пустые слова, а единственная стратегия.
Практика для пентестеров: детект и проверка
🔓 Эксклюзивный контент для зарегистрированных пользователей.
Шаг 1: определите версию Axios в проекте
Bash:
# Из корня проекта
npm ls axios
# Или через package-lock.json
cat package-lock.json | grep -A 3 '"axios"'
# Для yarn
yarn why axios
Шаг 2: проверьте наличие prototype pollution в зависимостях
CVE-2026-40175 - это гаджет, который «активируется» только при наличии prototype pollution в стеке. Проверяем зависимости на известные PP-уязвимости:
Bash:
# npm audit - быстрая проверка
npm audit --production
# Для глубокого сканирования - osv-scanner
npx osv-scanner --lockfile package-lock.json
qs, minimist, ini, body-parser, merge-deep, lodash (старые версии). На одном аудите мы нашли PP в qs версии 6.5.2 - она тянулась через три уровня транзитивных зависимостей, и разработчики о ней даже не подозревали.Шаг 3: проверьте использование кастомных адаптеров
Bash:
# Поиск кастомных адаптеров в коде
grep -rn "adapter:" --include="*.js" --include="*.ts" .
grep -rn "createAdapter\|customAdapter\|net.Socket\|net.createConnection" \
--include="*.js" --include="*.ts" .
Шаг 4: проверьте конфигурацию IMDS на EC2
Bash:
# Проверка текущего режима IMDS на инстансе
aws ec2 describe-instances --instance-ids i-xxxxxxxxx \
--query 'Reservations[].Instances[].MetadataOptions'
# Убедитесь что HttpEndpoint=enabled и HttpTokens=required (IMDSv2-only)
HttpTokens: optional - инстанс принимает IMDSv1-запросы, что расширяет поверхность атаки даже без request smuggling.Шаг 5: тестирование в Burp Suite
При пентесте приложения на Node.js/Axios - проверяем, пробрасываются ли пользовательские данные в конфигурацию Axios:
Код:
# Burp Suite: отправьте запрос с попыткой prototype pollution
POST /api/config HTTP/1.1
Content-Type: application/json
{
"__proto__": {
"x-injected": "test-value"
}
}
x-injected. Если появился - у вас есть вход в цепочку.
Маппинг на MITRE ATT&CK: полная цепочка атаки
Цепочка CVE-2026-40175 покрывает несколько тактик и техник из MITRE ATT&CK:| Звено цепочки | Техника | Тактика |
|---|---|---|
| Prototype pollution через уязвимую зависимость |
Ссылка скрыта от гостей
(T1190) | Initial Access |
| Выполнение JavaScript-payload в контексте Node.js | JavaScript (T1059.007) | Execution |
| Эскалация от PP к SSRF/RCE через Axios | Exploitation for Privilege Escalation (T1068) | Privilege Escalation |
| Обход IMDSv2 через request smuggling | Exploitation for Defense Evasion (T1211) | Defense Evasion |
| Запрос к
Ссылка скрыта от гостей
| Cloud Instance Metadata API (T1552.005) | Credential Access |
| Использование украденных IAM-credentials | Cloud Accounts (T1078.004) | Persistence, Defense Evasion |
| Загрузка дополнительных инструментов через скомпрометированный аккаунт | Ingress Tool Transfer (T1105) | Command and Control |
Цепочка проходит через семь тактик - от Initial Access до C2. Это не типичная «одна уязвимость - один импакт», а полноценный kill chain, который при успешной эксплуатации даёт атакующему постоянный доступ к облачной инфраструктуре.
Контекст: связь с атакой на supply chain Axios
По данным The Stack, уязвимость была обнаружена в ходе аудита безопасности Axios после недавней supply chain атаки. Мейнтейнер Джейсон Сааймен стал жертвой целевой атаки - предположительно, со стороны северокорейских группировок, которые использовали персонализированный фишинговый звонок. Это привело к компрометации пакета и последующему масштабному аудиту кодовой базы, в ходе которого и нашли CVE-2026-40175.Методология подсчёта не раскрыта - цифру «60 миллионов загрузок в неделю» стоит рассматривать как верхнюю границу, а не подтверждённое число эксплуатируемых систем. Axios - один из самых популярных npm-пакетов, широко используемый в серверных Node.js-приложениях.
Митигация Prototype Pollution и защита от CVE-2026-40175
Немедленные действия: обновление Axios
Патч доступен в версии 1.15.0 (основная ветка 1.x). Advisory также указывает fix в 0.3.1. Согласно NVD, fix доступен в 1.15.0 и 0.3.1. Формулировка «Prior to 1.15.0 and 0.3.1» в NVD неоднозначна: 0.3.1 - ранний релиз 2014 года, и неясно, уязвимы ли промежуточные версии 0.4.0–0.27.x. Пользователям 0.21.x–0.27.x рекомендуется мигрировать на 1.15.0+. Для точного определения затронутых диапазонов проверьте advisory GHSA-fvcv-3m26-pcqx.Обновление добавляет строгую валидацию значений заголовков - при обнаружении CRLF выбрасывается ошибка безопасности:
Bash:
# Обновление до исправленной версии
npm install axios@^1.15.0
# Для проектов на 0.x (0.21.x–0.27.x): мигрируйте на 1.15.0+
# Версия 0.3.1 - fix только для ранних 0.x (до 0.3.0), НЕ для 0.21–0.27
# См. advisory GHSA-fvcv-3m26-pcqx для точного диапазона
# Верификация установленной версии
npm ls axios
Архитектурная защита от Prototype Pollution в Node.js
Обновление Axios закрывает один гаджет, но prototype pollution как класс атак требует системного подхода.Object.create(null) для конфигурационных объектов:
JavaScript:
// Вместо обычного объекта, наследующего от Object.prototype:
const config = {}; // уязвимо к prototype pollution
// Используйте «чистый» объект без прототипа:
const config = Object.create(null); // не наследует загрязнённые свойства
Object.freeze(Object.prototype) - ядерный вариант:
JavaScript:
// Замораживаем прототип на старте приложения
// ВНИМАНИЕ: может сломать код, полагающийся на модификацию прототипа
Object.freeze(Object.prototype);
hasOwnProperty:
JavaScript:
// При обработке пользовательского ввода
function safeGet(obj, key) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
return obj[key];
}
return undefined;
}
169.254.169.254 через iptables или AWS VPC endpoint policies. Принудительно включите IMDSv2-only:
Bash:
# Принудительное включение IMDSv2-only на EC2-инстансе
aws ec2 modify-instance-metadata-options \
--instance-id i-xxxxxxxxx \
--http-tokens required \
--http-endpoint enabled
169.254.169.254, особенно PUT на /latest/api/token из процессов, которые обычно к IMDS не обращаются.Сканирование зависимостей
Включите регулярное сканирование зависимостей в CI/CD-пайплайн. CVE-2026-40175 показывает, что «безопасная» библиотека может стать эксплуатируемым гаджетом при наличии prototype pollution в любой другой зависимости изnode_modules. Один загрязнённый прототип - и весь npm-зоопарк превращается в поверхность атаки.Заключение: что означает CVE-2026-40175 для практики пентеста
CVE-2026-40175 - показательный пример того, как CVSS 10.0 и реальная эксплуатируемость могут расходиться. Уязвимость в Axios реальна на уровне библиотеки: отсутствие санитизации заголовков - объективный факт. Но Node.js, Bun и Deno блокируют CRLF-инъекцию на уровне рантайма, превращая теоретический kill chain в нерабочий в стандартных окружениях.Для пентестера это три конкретных действия:
- Проверяйте кастомные адаптеры. Нестандартный HTTP-транспорт - и цепочка может работать.
- Ищите prototype pollution в стеке. Без PP-входа гаджет в Axios бесполезен. Но если PP есть - даже пропатченный Axios не единственный возможный гаджет.
- Аудитируйте IMDS-конфигурацию. Принудительный IMDSv2-only и сетевая сегментация работают даже если все библиотеки окажутся уязвимы.
package-lock.json прямо сейчас: npm audit --production. Если там чисто - отлично. Если нет - вы знаете, что делать.