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.
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.