Cheshire Cat

Pendant le CTF Root-Me pour les 10k membres sur Discord, j’ai pu créer deux challenges sur Discord, et voici un writeup pour expliquer comment ils fonctionnent et comment les exploiter.

Introduction

  • Nom : Cheshire Cat
  • Catégorie : Misc
  • Points : 500 -> 409
  • Résolutions : 46
  • Niveau : facile

Exploitation

Comme le dit la description, nous pouvons essayer d’interagir avec le bot dans le canal #ctf-bot. Nous pouvons essayer de talk avec Cheshire, demander de l’help ou un password, mais nous n’obtenons aucune réponse intéressante.

Il semble qu’il ne réponde qu’à son master, et ici, son master est Ech0, le propriétaire du serveur.

Dans la description, il est dit “Il ne vous répondra que dans le canal #ctf-bot, mais il ne vous donnera aucun flag”, imaginez que c’est bien vrai ? Nous pouvons essayer de lui envoyer un message privé pour obtenir un flag en DM, mais il semble que cela ne fonctionne pas, cependant il nous donne une nouvelle réponse intéressante.

Ok donc, Cheshire n’aime pas discuter en DM, il veut une invitation pour une tasse de thé. Nous pouvons essayer de l’inviter sur notre serveur pour boire une petite tasse de thé 🍵

Mais, comment pouvons-nous inviter le bot sur notre serveur ? Il n’a pas de bouton “inviter”, et il ne semble pas aimer le lien d’invitation en message direct.

Nous pouvons donc nous pencher sur la fonctionnalité Discord OAuth2. Dans la section bot, nous pouvons voir le lien OAuth2 pour inviter un bot sur notre serveur, mais nous avons besoin de l’ID du bot pour le faire.

Il suffit d’activer le Mode Développeur Discord dans l’application dans Paramètres utilisateur > Avancés > Mode Développeur, puis de faire un clic droit sur le profil du bot et Copier l'ID.

Voici notre lien final pour inviter le bot sur notre serveur :

https://discord.com/oauth2/authorize?client_id=980846289723994122&scope=bot

Et après que le bot ait rejoint le serveur, nous pouvons lui demander le password.

Okay c’est bon, nous avons le flag, mais comment cela fonctionne ?

How it’s work ?

Le code du bot est assez simple :

#!/usr/bin/env/python3

from discord.ext import commands
import discord # discord.py==1.7.3
import random
from os import getenv

FLAG = getenv("FLAG")
TOKEN = getenv("TOKEN")

# #ctf-bot on the Root-Me channel
CTF_BOT_CHANNEL = 1032971020807712768
# Root-Me server id
SERVER_ID = 700478419527270430

bot = commands.Bot(command_prefix='!', help_command=None, connect=True)

# DM messages and server spam prevention
@bot.event
async def on_message(message):
    if bot.user != message.author:
        if isinstance(message.channel, discord.channel.DMChannel):
            await message.channel.send("I don't like to talk in DM, invite me for a cup of tea 🫖")
        elif message.guild.id == SERVER_ID and message.channel.id != CTF_BOT_CHANNEL:
            return
    await bot.process_commands(message)

# !help
@bot.command(name='help')
async def help(ctx):
    await ctx.send("```Avalaible commands:\n\t!help : Display this menu\n\t!password : A password for my master\n\t!talk : One of my favorite expressions```")

# !password
@bot.command(name='password')
async def password(ctx):
    if ctx.author.guild_permissions.administrator:
        message = f"Hello master, here's your password: `{FLAG}` 🤪"
    else:
        message = "I only respond to my master... and you'r not !  🫠"

    await ctx.send(message)

# !talk
@bot.command(name='talk')
async def talk(ctx):
    expressions = ["Visit either you like: they're both mad.", 
                    "We're all mad here. I'm mad. You're mad.", 
                    "Imagination is the only weapon in the war with reality.", 
                    "Only a few find the way, some don't recognize it when they do some… don't ever want to", 
                    "I am not crazy, my reality is just different from yours.", 
                    "Every adventure requires a first step.", 
                    "Not all who wander are lost.", 
                    "If you don't know where you are going any road can take you there."] 
    await ctx.send(f"*{random.choice(expressions)}*")

bot.run(TOKEN)

Pour résumer, le bot va contrôler que l’utilisateur sur le serveur actuel à un rôle avec les permissions administrateur (ou propriétaire), si oui, il enverra le flag. Ok, ça semble sécurisé, pas vrai ? Et bien oui.

La vulnérabilité provient de la mauvaise configuration (par défaut) du [portail des développeurs de Discord] (https://discord.com/developers/applications). Par défaut, l’option Public Bot est activée, donc comme Discord le dit, n’importe qui peut inviter votre Bot.

Les bots publics peuvent être ajoutés par n’importe qui. Lorsque cette option n’est pas cochée, vous seul pouvez joindre ce bot aux serveurs.

Cette option peut être très dangereuse quand le bot a des fonctions d’administration, et beaucoup de bots n’ont pas cette option désactivée.

Donc si vous voulez créer un Bot Discord, n’oubliez pas de désactiver cette option !

Configuration du challenge

Architecture complète et fichiers ici

Architecture

L’architecture du challenge est assez simple :

.
├── cheshire.py
├── docker-compose.yml
├── Dockerfile
├── .env
└── requirements.txt
  • cheshire.py -> code python du bot
  • docker-compose.yml -> fichier docker-compose pour démarrer le challenge
  • Dockerfile -> Image Docker de l’environnement du bot
  • .env -> contient le flag et le token du bot
  • requirements.txt -> requirements python du bot

Démarrage

docker-compose -f docker-compose.yml up -d 

Merci d’avoir lu, si vous avez une question à propos de l’exploitation, la configuration, l’architecture ou autre, vous pouvez m’envoyer un message sur Twitter ou Discord Nishacid#1337. Je voudrais aussi remercier Ruulian pour m’avoir aidé à créer ce challenge !