Quick Tip: Building a CTF Alert system

Quick Tip: Building a CTF Alert system

No matter if we are talking about CTFs, Bug Bounties or Pentests, automating basic recon tasks as much as possible will give you enormous advantages.

The best part is, that you not only can automate your initial recon but also scheduling these tasks. The company currently does not have any open redirects or subdomain takeovers? Well, maybe that will change in a few months as new assets will be created, old assets will be dumped and forgotten.

In this case, we will cover how to build a small, hacky alert bot for new 0x00sec CTF challenges.

And this is less about the technical part as this could be done in a thousand different ways. It is more about the process. What steps are involved in solving such a problem and what thinking was involved. Personally, I find it often way cooler to see how other people approach problems and finding ways for solutions, then the solution itself.

Problem

We all know the 0x00sec ctf challenges. And we all know that they are shock dropped. And if you are a bit competitive you may want to solve them as fast as possible to get points and rankings.

You could constantly hit F5 in the discord or challenge site and wait for a new challenge. Or you automate this task.

Our goal is to find a way to get notified about a new challenge.

The solution

Let’s try to find a solution. For the initial brainstorming how we could solve this problem, you may have some ideas:

  • Listening in the discord for the drop message
  • Scraping the webpage

I though scraping the web page may be the easiest way as I am most comfortable with web technologies.

The recon

The first step was to visit the ctf page, opening up chrome devtools and inspecting the network tab.

And we are super lucky because it looks like we have an API we can work with. Because there is a request to https://ctf.0x00sec.org/api/v1/challenges

And we get a nice response with all available challenges.

{
    "data": [
        {
            "category": "Web",
            "name": "Exercise #1",
            "template": "/plugins/challenges/assets/view.html",
            "tags": [],
            "script": "/plugins/challenges/assets/view.js",
            "type": "standard",
            "id": 1,
            "value": 15
        },
        {
            "category": "Web",
            "name": "Exercise #2",
            "template": "/plugins/challenges/assets/view.html",
            "tags": [],
            "script": "/plugins/challenges/assets/view.js",
            "type": "standard",
            "id": 2,
            "value": 15
        }
    ],
    "success": true
}

Fetching the challenge data

We can now try to curl the API endpoint to see if we can fetch data unauthenticated.

➜ curl https://ctf.0x00sec.org/api/v1/challenges                                                 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/login?next=%2Fapi%2Fv1%2Fchallenges%3F">/login?next=%2Fapi%2Fv1%2Fchallenges%3F</a>.  If not click the link.%   

However, this was not working. If we inspect the request more closely in Chrome Devtools we see in the headers section that we send some cookies with the request.

We send a __cfduid and a session. So let’s try to send these along with curl.

And we are lucky again because we got a response back. Sometimes you need to specify more attributes for requests to work, as a proper user-agent as sometimes specific agents are blocked.

A cool trick, if you want to save time is to copy the request from Chrome Devtools.

d

Now we need to work with the response data. I used a tool called jq for this. It is an awesome tool if you are working with json data.

We can just pipe the output of our curl command into jq and we are getting a pretty json output.

First, we should add the -s flag to our curl command, so we can silence the output and progress.

curl -s https://ctf.0x00sec.org/api/v1/challenges -H 'cookie: __cfduid=ID; session=SESSION' | jq
{
  "data": [
    {
      "category": "Web",
      "name": "Exercise #1",
      "template": "/plugins/challenges/assets/view.html",
      "tags": [],
      "script": "/plugins/challenges/assets/view.js",
      "type": "standard",
      "id": 1,
      "value": 15
    },
    {
      "category": "Web",
      "name": "Exercise #2",
      "template": "/plugins/challenges/assets/view.html",
      "tags": [],
      "script": "/plugins/challenges/assets/view.js",
      "type": "standard",
      "id": 2,
      "value": 15
    }
  ],
  "success": true
}

We know that we have two challenges up. So we want some notification if there are more then 2 challenges. We can access the json and data array in jq with

jq '.data'

Which results in nearly the same output:

➜ curl -s https://ctf.0x00sec.org/api/v1/challenges -H 'cookie: __cfduid=ID; session=SESSION | jq '.data'
[
  {
    "category": "Web",
    "name": "Exercise #1",
    "template": "/plugins/challenges/assets/view.html",
    "tags": [],
    "script": "/plugins/challenges/assets/view.js",
    "type": "standard",
    "id": 1,
    "value": 15
  },
  {
    "category": "Web",
    "name": "Exercise #2",
    "template": "/plugins/challenges/assets/view.html",
    "tags": [],
    "script": "/plugins/challenges/assets/view.js",
    "type": "standard",
    "id": 2,
    "value": 15
  }
]

But now we have the content of the data array. The last step is to get the length of the array with

jq '.data | length'
➜ curl -s https://ctf.0x00sec.org/api/v1/challenges -H 'cookie: __cfduid=ID; session=SESSION' | jq '.data | length' 
2

And as we see, we get the length of 2. So the last thing we need to do is to write a script to automate this and to send us a notification if we have more then 2 challenges up.

Like I said, in the beginning, there are a thousand ways of doing it. I was super lazy and choose a small bash script to do this and send a message over telegram.

Scripting and notifications

Let’s write a small script to automate this step.

#!/bin/bash


number_of_challenges=$(curl -s https://ctf.0x00sec.org/api/v1/challenges -H 'cookie: __cfduid=ID; session=SESSION' | jq '.data | length' )

echo "Number of challanges: $number_of_challenges"

if [ $number_of_challenges -gt 2 ]
  then
    echo "Wake up boi! New challenge is up!"
fi



The next task is to send us a notification. This can happen over your prefered service:

  • Telegram
  • Discord
  • Slack
  • SMS (Twilio)

I’ve gone the Telegram route. You can setup a new bot quite easily and then just do a POST request. Just follow this guide to setup the bot and get your api tokens.

So our final script will look like this:

#!/bin/bash

TOKEN=
CHAT_ID=
MESSAGE="GET UP! New 0x00sec ctf is up!"
URL="https://api.telegram.org/bot$TOKEN/sendMessage"

number_of_challenges=$(curl -s https://ctf.0x00sec.org/api/v1/challenges -H 'cookie: __cfduid=ID; session=SESSION' | jq '.data | length' )

if [ $number_of_challenges -gt 2 ]
  then
    curl -s -X POST $URL -d chat_id=$CHAT_ID -d text="$MESSAGE"  > /dev/null
fi



Last but not least, we need to periodically check for this. So setting up a cronjob on a server might be the easiest.

crontab -e

And let’s check for this every 15 minutes:

*/15 * * * * bash /home/web/status.sh

And that’s it. You’ve got your own CTF alert system.

The drawback of this method is that the session is not valid for an infinite time.
If you want it more sophisticated, you can implement a login and grep the session automatically.

8 Likes

Ahaha this is awesome dude!!!

This is exactly the kind of awesome stuff we want to see :smiley:

I knew shock-dropping would induce some innovation, you sir are exactly what we need.

Next steps is making it a modular framework and publishing it GH / 0x00sec :slight_smile:

I wonder what next advancements we’ll make :wink: