Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions NullCTF/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM python:3.7.8-alpine

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

RUN apk add gcc python3-dev musl-dev

COPY nullctf.py .
COPY cogs .
COPY help_info.py .
COPY magic.json .
COPY config_vars.py .
COPY requirements.txt .

RUN pip install -r requirements.txt

CMD ["python", "nullctf.py"]
674 changes: 674 additions & 0 deletions NullCTF/LICENSE

Large diffs are not rendered by default.

129 changes: 129 additions & 0 deletions NullCTF/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<img src="https://i.imgur.com/mZ2bVY4.png"/>

>### *A [discord.py](http://discordpy.readthedocs.io/en/latest/) bot focused on providing CTF tools for collaboration in Discord servers (ctftime.org commands, team setup + ctfd integration, utilites, etc)! If you have a feature request, make it a GitHub issue or use the >request "x" command.*

[Invite to your server](https://discordapp.com/oauth2/authorize?client_id=455502163452362753&permissions=268528720&scope=bot)
\
[Join the support server](https://discord.gg/yf8E2s8)

# How to Use
*If you ever forget a command, use `>help`!*

After inviting to your server, it is recommended that you first configure the categories you want active and archived CTF channels to go into.
* When you create a ctf, it will by default go into the "CTF" category (it will create one if it is not present), and when you archive a ctf it will go into the ARCHIVE category.
* You can configure this with `>config ctf_category "Category for CTFs"` and `>config archive_category "Category for Archived CTFs"`
---

## CTF Commands

The following commands are the ones you will most likely want to pay attention to, although if you do not expect to use this bot to manage CTFs you can skip this category and go down to CTFTime commands.

* `>ctf create "ctf name"` This is the command you'll use when you want to begin a new CTF. This command will make a text channel with your supplied name under the designated CTF category. *Must have permissions to manage channels*
![ctf create](https://i.imgur.com/6PUPIX3.png)


*NOTE: the following ctf specific commands will only be accepted under the channel created for that ctf. This is to avoid clashes with multiple ctfs going on in the same server.*

* `>ctf join/leave` Using this command will either give or remove the role of a created ctf to/from you.
![ctf join/leave](https://i.imgur.com/R1ktkMv.png)

* `>ctf challenge add/working/solved/remove "challenge"` Allows users to add or remove challenges to a list, and then set the status of that challenge. *Use quotations*

* `>ctf challenge list` This is the list command that was previously mentioned, it displays the added challenges, who's working on what, and if a challenge is solved (and by who).
![desc](https://i.imgur.com/l9jsuLz.png)

> NOTE: There is shorthand! challenge -> chal/chall, add -> a, working -> w, solved -> s, remove -> r

* `>ctf challenge pull "http(s)://ctfd.url"` Pull challenges and their solved states from a CTFd hosted CTF, and add them to your challenges list. Requires the username and password to be set with `>ctf setcreds "username" "password"`

* `>ctf setcreds "ctfd username" "password"` Pin the message of ctf credentials, can be fetched by the bot later in order to use `>ctf challenge pull`. Credentials are never stored outside of Discord.
![ctf pull and setcreds](https://i.imgur.com/Z3e0pE3.png)

* `>ctf creds` Gets the credentials from the pinned message.

> *IMPORTANT: credentials are never stored outside of the pinned message on Discord. They are needed to pull challenge data and solve state from the CTFd platform.*


* `>ctf archive` Move the CTF channel into the Archive category. *Must have permissions to manage channels*

* `>ctf delete` Delete the CTF info from the database, and delete the role. *Must have permissions to manage channels*

---

## [CTFtime](https://ctftime.org) Commands

* `>ctftime countdown/timeleft` Countdown will return when a selected CTF starts, and timeleft will return when any currently running CTFs end in the form of days hours minutes and seconds.
![enter image description here](https://i.imgur.com/LFSTr33.png)
![enter image description here](https://i.imgur.com/AkBfp6E.png)

* `>ctftime upcoming <number>` Uses the api mentioned to return an embed up to 5 upcoming CTFs. If no number is provided the default is 3.
![enter image description here](https://i.imgur.com/UpouneO.png)

* `>ctftime current` Displays any currently running CTFs in the same embed as previously mentioned.
![enter image description here](https://i.imgur.com/RCh3xg6.png)

* `>ctftime top <year>` Shows the ctftime leaderboards from a certain year *(dates back to 2011)*.
![enter image description here](https://i.imgur.com/jdPWmCV.png)

---
## Utility Commands
* `>help` Returns the help page

* `>magicb filetype` Returns the mime and magicbytes of your supplied filetype. Useful for stegonography challenges where a filetype is corrupt.

* `>rot "a message"` Returns all 25 possible rotations for a message.

* `>b64 encode/decode "message"` Encode or decode in base64 *(at the time of writing this, if there are any unprintable characters this command will not work, this goes for all encoding/decoding commands).*

* `>b32 encode/decode "message"` Encode or decode in base32

* `>binary encode/decode "message"` Encode or decode in binary.

* `>hex encode/decode "message"` Encode or decode in hex.

* `>url encode/decode "message"` Encode or decode with url parse. This could be used for generating XSS payloads.

* `>reverse "message"` Reverse a message.

* `>counteach "message"` Count the occurrences of each character in the supplied message.

* `>characters "message"` Count the amount of characters in your message.

* `>wordcount a test` Counts the amount of words in your message (don't use quotations).

* `>cointoss` Get a 50/50 cointoss to make all your life's decisions.

* `>request/report "a feature"/"a bug"` Dm's the creator (nullpxl#3928) with your feature/bug request/report.

## Have a feature request? Make a GitHub issue or use the >request command.

# Setup - General Overview
---
General guide for setup, not very detailed currently but I will expand in the future if people actually end up wanting to host locally :P
* This may be necessary in the future because of Disord's recent [verification requirements](https://support.discordapp.com/hc/en-us/articles/360040720412-Bot-Verification-and-Data-Whitelisting) for bots in over 100 servers (which this bot is already over). This rule, which will disallow users to invite bots that have not been verified by the owner (which requires photoid) will be enforced starting october 7th.
```
Create a discord bot on discord's developer portal -> get the bot token -> clone this repo -> install discord.py >= 1.3.3 ->
Create mongodb account -> create project -> create cluster -> create db user ->
add your ip to db whitelist access -> connect to cluster and select python as driver ->
get connection string and follow mongodb's steps with your password ->
pip install pymongo and dnspython -> use the following template for creating dbs and collections under the file config_vars.py
```
```
# config_vars.py
from pymongo import MongoClient

discord_token = ""
mongodb_connection = ""

client = MongoClient(mongodb_connection)

ctfdb = client['ctftime'] # Create ctftime database
ctfs = ctfdb['ctfs'] # Create ctfs collection

teamdb = client['ctfteams'] # Create ctf teams database

serverdb = client['serverinfo'] # configuration db
```
```
invite the bot to your server
```
Binary file added NullCTF/__pycache__/config_vars.cpython-38.pyc
Binary file not shown.
Binary file added NullCTF/__pycache__/help_info.cpython-38.pyc
Binary file not shown.
Binary file added NullCTF/cogs/__pycache__/cipher.cpython-38.pyc
Binary file not shown.
Binary file not shown.
Binary file added NullCTF/cogs/__pycache__/ctf.cpython-38.pyc
Binary file not shown.
Binary file added NullCTF/cogs/__pycache__/ctftime.cpython-38.pyc
Binary file not shown.
Binary file added NullCTF/cogs/__pycache__/encoding.cpython-38.pyc
Binary file not shown.
Binary file added NullCTF/cogs/__pycache__/utility.cpython-38.pyc
Binary file not shown.
40 changes: 40 additions & 0 deletions NullCTF/cogs/cipher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import collections
import string
import discord
from discord.ext import commands

class Ciphers(commands.Cog):

def __init__(self, bot):
self.bot = bot

@commands.command()
async def rot(self, ctx, message, direction=None):
# Bruteforce a rot cipher.
allrot = ''

for i in range(0, 26):
upper = collections.deque(string.ascii_uppercase)
lower = collections.deque(string.ascii_lowercase)

upper.rotate((- i))
lower.rotate((- i))

upper = ''.join(list(upper))
lower = ''.join(list(lower))
translated = message.translate(str.maketrans(string.ascii_uppercase, upper)).translate(str.maketrans(string.ascii_lowercase, lower))
allrot += '{}: {}\n'.format(i, translated)

await ctx.send(f"```{allrot}```")

@commands.command()
async def atbash(self, ctx, message):
# Return the result of performing the atbash cipher on the message.
normal = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
changed = 'zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA'
trans = str.maketrans(normal, changed)
atbashed = message.translate(trans)
await ctx.send(atbashed)

def setup(bot):
bot.add_cog(Ciphers(bot))
59 changes: 59 additions & 0 deletions NullCTF/cogs/configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import discord
from discord.ext import tasks, commands
import sys
sys.path.append("..")
from config_vars import *

# Extension for per-discord-server configuration.
# Configurations are logged in the database under the server id (right click on your server icon in discord dev mode).

class Configuration(commands.Cog):
def __init__(self, bot):
self.bot = bot

@commands.group()
async def config(self, ctx):
if ctx.invoked_subcommand is None:
# If the subcommand passed does not exist, its type is None
config_commands = list(set([c.qualified_name for c in Configuration.walk_commands(self)][1:]))
await ctx.send(f"Current config commands are: {', '.join(config_commands)}")

@commands.bot_has_permissions(manage_channels=True)
@commands.has_permissions(manage_channels=True)
@config.command()
async def ctf_category(self, ctx, category_name):
# Set the category that new ctf channels are created in by default.
category_name = category_name.replace("$", "")
category = discord.utils.get(ctx.guild.categories, name=category_name)

if category == None: # Checks if category exists, if it doesn't it will create it.
await ctx.guild.create_category(name=category_name)
category = discord.utils.get(ctx.guild.categories, name=category_name)

sconf = serverdb[str(ctx.guild.id) + '-CONF'] # sconf means server configuration
info = {"ctf_category": category_name}
sconf.update({"name": 'category_name'}, {"$set": info}, upsert=True)
categoryset = sconf.find_one({'name': "category_name"})['ctf_category']
await ctx.send(f"CTF category set as `{categoryset}`")

@commands.bot_has_permissions(manage_channels=True)
@commands.has_permissions(manage_channels=True)
@config.command()
async def archive_category(self, ctx, category_name):
# Set the category that archived ctf channels are put in by default.
category_name = category_name.replace("$", "")
category = discord.utils.get(ctx.guild.categories, name=category_name)

if category == None: # Checks if category exists, if it doesn't it will create it.
await ctx.guild.create_category(name=category_name)
category = discord.utils.get(ctx.guild.categories, name=category_name)

sconf = serverdb[str(ctx.guild.id) + '-CONF'] # sconf means server configuration
info = {"archive_category": category_name}
sconf.update({"name": 'archive_category_name'}, {"$set": info}, upsert=True)
categoryset = sconf.find_one({'name': "archive_category_name"})['archive_category']
await ctx.send(f"Archive category set as `{categoryset}`")


def setup(bot):
bot.add_cog(Configuration(bot))
Loading