В прошлый раз наша команда провела небольшой семинар по JavaScript (Посмотрите статью здесь: 8 викторин по Javascript, которые могут сбить вас с толку). И у нас все получилось довольно хорошо. Всем понравилось решать эти небольшие задачи.

На этот раз мой товарищ по команде принес практикум по поиску уязвимостей в веб-приложениях Node.js.

Это несложно. В веб-приложении есть 2 уязвимости. Способ "захватить флаг" — означает успешно войти в систему. Если вам все удалось, текст flag будет отображен на странице.

Начать работу

Следуйте инструкциям и убедитесь, что вы все сделали правильно. Теперь вам разрешено просматривать только файлы репозитория

  • README.md

  • vuln1/index.js

  • uploads/*

  • package*.json

Помните, что нельзя мошенничать до завершения теста (Всевышний наблюдает за вами). Вот несколько предупреждений перед началом.

  1. Пожалуйста, не открывайте файл .env.

  2. Пожалуйста, завершите тест, не изменяя исходный код (однако, отладка разрешена).

  3. Пожалуйста, следуйте приведенным выше советам

0. Предварительные условия

Убедитесь, что в вашей системе установлен Node 16+ (требуется NPM v8). Рекомендуемый способ установки Node.js через nvm

$ nvm install 16
...
$ nvm use 16

1. Клонируйте репозиторий

Вот ссылка на репозиторий.

$ git clone git@github.com:daiyanze/vulnerability-code-challenge.git
$ cd crack-the-code-challenge & npm i

2. Запустите сервер

$ npm -w challenges/vuln1 run start

> vuln1@1.0.0 start
> node index.js

Started on http://localhost:81

3. Откройте http://localhost:81

Затем появится следующий экран.

Все, что касается взлома кода, начинается отсюда.

На этой странице администратора можно сделать 4 вещи:

  1. Изменить имя и содержимое файла JSON.

  2. Загрузить свой JSON-файл.

  3. Распечатать содержимое JSON-файла.

  4. Вход в систему.

Теперь ваша задача — пройти этап входа в систему, чтобы на странице отобразился текст flag.

Подсказки

Если вы все еще не знаете, как это сделать, воспользуйтесь приведенными ниже подсказками, по порядку. Но, пожалуйста, не забудьте сначала прочитать код в файле index.js. В нем вы найдете много полезных сведений.

Подсказка 1

А как насчет просмотра других файлов на сервере?

Подсказка 2

Посмотрите внимательно на сообщения вашего терминала.

Подсказка 3

Как насчет изменения содержимого файла parsed.json?

Подсказка 4

Вы же знаете, что могли бы мутировать prototypes и __proto__, не так ли?

Подсказка 5

Просто перейдите в раздел ответов. Там будет написано, как это сделать.

Когда флаг захвачен

Не читайте этот раздел, если вы еще не захватили флаг. Но я очень надеюсь, что вы уже сделали это. В противном случае изложенное ниже просто даст вам возможность подробно разобраться с загадкой, прежде чем вы сумеете разгадать ее самостоятельно.

Объяснение

В первых нескольких абзацах этой статьи упоминается, что у нас есть 2 уязвимости. И обе они представляют угрозу на этапе входа в систему.

Ключевыми моментами являются:

  • Где мы получаем пароль?

  • Как мы можем обмануть аутентификацию?

Когда мы поймем, как справиться с этими проблемами, ответ окажется прямо на поверхности.

Не так уж сложно связать пароль с таинственным файлом .env, верно? Поскольку вам сказали не смотреть на него, то любопытство могло подтолкнуть вас к "методу" открытия этого файла, как написано в "Подсказке 1".

Итак, если вы попытаетесь открыть содержимое файла .env через "Read uploaded JSON (Прочитать загруженный JSON)", появится пароль. О, теперь у нас есть пароль. Значит, это должно сработать? Конечно, нет. Это только начало.

Помните, что написано в исходном коде об аутентификации?

const user = {
  // just pretend this comes from a db
  password: "$2a$12$3cSdZFdEIG9FizllB9.5E.M8DTQQ185zyITtBTBz7Lz3Va8s0xjSy",
};

switch (user.auth_method ?? "bcrypt") {
  case "bcrypt":
    passed = bcrypt.compareSync(req.body.password, user.password);
    break;
  case "superadmin":
    passed = req.body.password === process.env.SUPER_ADMIN_PASS;
    break;
  default:
    throw new Error("invaid auth method");
}

Зашифрованный password уже имеется, но расшифровать его не так-то просто. Поэтому точкой прорыва является user.auth_method. Вам нужно найти способ превратить значение user.auth_method в "superadmin". Но как?

Что ж, вот одна полезная информация из терминала при инсталляции пакета. В вашем пакете есть одна уязвимость с критическим уровнем серьезности.

added 110 packages, and audited 111 packages in 8s

5 packages are looking for funding
  run `npm fund` for details

1 critical severity vulnerability

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

Когда вы пробовали npm audit fix, решение легко могло прийти в голову, если у вас имелся опыт работы с "загрязнением прототипов".

added 1 package, removed 1 package, and audited 120 packages in 2s

10 packages are looking for funding
  run `npm fund` for details

# npm audit report

lodash  <=4.17.20
Severity: critical
Prototype Pollution in lodash - https://github.com/advisories/GHSA-jf85-cpcp-j695
Prototype Pollution in lodash - https://github.com/advisories/GHSA-fvqr-27wr-82fm
Command Injection in lodash - https://github.com/advisories/GHSA-35jh-r3h4-6jhm
Regular Expression Denial of Service (ReDoS) in lodash - https://github.com/advisories/GHSA-x5rq-j2xg-h7qm
Prototype Pollution in lodash - https://github.com/advisories/GHSA-p6mc-m468-83gw
fix available via `npm audit fix --force`
Will install lodash@4.17.21, which is outside the stated dependency range
node_modules/lodash

1 critical severity vulnerability

To address all issues, run:
  npm audit fix --force

Вот очень хорошая статья о "загрязнении прототипов".

Простыми словами, "загрязнение прототипа" — это "расширение/изменение глобальных объектов".

Object.__proto__.auth_method = "superadmin"
Object.constructor.prototype.auth_method = "superadmin"

С помощью описанной выше уловки вы запросто добавите некоторые дополнительные специи к содержимому JSON. Теперь можно захватить флаг.

{"__proto__":{"auth_method":"superadmin"}}

Уязвимость, которая помогает вам «преодолеть» аутентификацию по паролю, исходит от lodash <= 4.17.20. Обратитесь к этой странице, чтобы получить более подробную информацию.

Ответ

Решение занимает всего 3 шага.

  1. Введите следующее в «Secure JSON formatter» <textarea> и нажмите кнопку «Format & Upload».

    {"__proto__":{"auth_method":"superadmin"}}
  1. Введите ../.env в поле "Read uploaded JSON" и нажмите кнопку "Read".

  2. Введите то, что вы получили на шаге 2, в поле "Логин".

Затем на странице вы получите текст flag .

Резюме

Сама по себе эта практика не особо сложна. Тем не менее, я думаю, что она предоставила нам несколько хороших предостережений о том, как заботиться о безопасности проекта.

Первое, что важно в создании безопасных приложений — это постоянно проводить "валидацию и санацию пользовательских входов" и никогда не использовать их сразу напрямую.

Когда мы будем доверять конечным пользователям? Когда нет никакого ввода вообще. 

Второй важный момент — наши проекты. Некоторые разработчики обычно не обращают внимания на информацию из npm audit. "Это нам не нужно". Но когда речь идет о некоторых проблемах безопасности, npm audit уже дал нам достаточно подсказок для дальнейших действий. Таким образом, постоянная проверка и обновление наших зависимостей, вероятно, помогут в создании достаточно безопасного приложения.


Сегодня вечером в Otus состоится открытое занятие по теме «Web Servers», на котором расскажем про HTTP серверы, разберем плюсы и минусы популярных серверов. Регистрация доступна по ссылке для всех желающих.

Комментарии (0)