The Magic Informer

3 minute read

A web challenge ranked easy in the HTB university CTF

Challenge description:

challenge description

I started doing web things about a month ago so i am still a begginner but i easily flagged this one and did a big part of the medium one so i am still happy about my performance.

Initial recon

We perform a pentest in a black box environment and have access to a website probably built in nodejs (Wappalyzer).

website root: website root

Uppon clicking on the bottom right button “WE ARE HIRING” we are prompted to a register page (/register)

register page

  • We can’t create an account with username admin because it does already exist.
  • Creating an account redirect us to /login.
  • Logging in redirect us to /dashboard

dashboard page: regular dashboard page

  • On the dashboard we can find a form to apply to be an editor, but it doesnt seems to be vulnerable to SQLi
  • We also have the ability to upload a resume, it gets rename to a “random” name and has the extension .docx
  • The download resume feature is vulnerable to a directory traversal attack.

Directory traversal via resume

The url to download your resume will look like this:
http://134.209.22.69:32474/download?resume=788b6eafb766a230c8bef6b741cc2d90.docx

Changing the resume query parameter allow us to read any file on the server.
It tries to protect against directory traversal by removing any ../ in finds in the resume parameter but we can bypass this using a common ....// payload (shrinked to ../ by the server)

Example:
http://134.209.22.69:32474/download?resume=....//....//etc/passwd

Getting index.js with resume=....//index.js, we learn the existence of a 2 new files, database.js, debug.env

In index.js we obtain the port number the application binded itself to: 1337

In debug.env we obtain the following DEBUG_PASS: CzliwZJkV60hpPJ (which will be used later on)

Auth bypass via flawed jwt

The website is using jwt token for it’s authentication process, the signature of the token is not checked by the server and thus, we can craft arbitrary tokens.

Here we can just change "username": "avan", to "username": "admin", in Payload, paste the new token (wich has an erroneous signature) into our session cookie and here goes the admin account.

Using token.dev token.dev extract of the jwt

admin account dashboard admin account dashboard

Admin dashboard recon

The admin account has access to more functionnality compared to a regular user:

  • An “SMS Gateway” which by default contact an external api to send sms (SSRF redflagged)
  • An “SQL Prompt” which require a debug password (we got it earlier) but whitelisted to localhost (we didnt even bother trying to bypass the localhost directly because we had an SSRF)

SMS gateway menu: sms gateway

SQL Prompt menu sql prompt

SSRF to SQLi

We had a complete control over the request parameter in the SMS Gateway menu thus, we opened burp to intercept the request made to the backend and modify it with more ease.

Here’s how the sms request looks like (POST /api/sms/test) (need the admin session cookie obviously) sms request

And here’s the sql request (POST /debug/sql/exec) (admin cookie blablabla…) sql request

Now we need to build our exploit to make a request to /debug/sql/exec via the SSRF

Here how the SMS request looks like now sql request

The server gladly request the /debug/sql/exec which execute our SQL payload and we back who created an account on their platform.

SSRF to SQLi to RCE

We did recon the database but didn’t find anything useful, only to realize some minutes later the presence of a command injection vulnerability. So it’s more like SSRF to RCE right now

SSRF to RCE

Using revshells.com we craft an mkfifo reverse shell and put it inside the SQL query

Final payload final payload

Listening on port 4444 on my public IP we get back a shell and we can retreive the flag !

reverse shell

HTB{br0k3n_4u7h_55RF_4s_4_s3rv1c3_d3bug_ftw}

ps: ty to Camille (Royal) and Arthur (Pasteque)