Home Secret - HackTheBox
Post
Cancel

Secret - HackTheBox

Secret is an easy linux box where you have to exploit a sensitive information leak in a git repo to recover a JWT secret, which allows you to forge a JWT token that gives you access to an API endpoint that’s vulnerable to command injection. Once on the box, you will be exploiting a custom SUID binary that allows for core dumping.


Info




Recon


NMAP

# Nmap 7.70 scan initiated Sat Oct 30 20:00:45 2021 as: nmap -sV -sC -v -oN nmap.txt 10.10.11.120
Nmap scan report for secret.htb (10.10.11.120)
Host is up (0.25s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http    nginx 1.18.0 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: DUMB Docs
3000/tcp open  http    Node.js (Express middleware)
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: DUMB Docs
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Oct 30 20:01:41 2021 -- 1 IP address (1 host up) scanned in 56.17 seconds

Web

The /doc path in the website provides a demo for creating and accessing accounts using the API;

Possible credential?

Account login;

There is a link to download source code in the homepage;

It is a ZIP file, which I download and extract. It contains a .git folder in the root, so it’s a GIT repository. Running git log command showed something interesting;

So I ran the git diff command using the commit ID to see what changes were made, and I got the token used to sign JWT tokens as indicated by the file routes/verifytoken.js;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const jwt = require("jsonwebtoken");

module.exports = function (req, res, next) {
    const token = req.header("auth-token");
    if (!token) return res.status(401).send("Access Denied");

    try {
        const verified = jwt.verify(token, process.env.TOKEN_SECRET);
        req.user = verified;
        next();
    } catch (err) {
        res.status(400).send("Invalid Token");
    }
};

The login API is also vulnerable to user enumeration as login attempt using a valid email with a bad password said Password is wrong, while attempt with invalid email says Email is wrong.



Foothold


Using Burp Suite, I was able to create an account using the following request;

Logging into the account gave me a token, which I forged on https://jwt.io using the JWT secret obtained, and changed the username to theadmin (the administrative username obtained by going through the source files). Using the forged token, request to /api/priv showed I am now the admin;

I am now authenticated as an admin, but still have no idea how to utilise the API.

Using the package.json file in the root of the repo, which contains dependencies of the project and their version numbers, I started looking for exploits online, but couldn’t find anything interesting. So I continue to enumerate previous commits using git diff, and found an interesting commit with the ID e297a2797a5f62b6011654cf6fb6ccb6712d2d5b;

That looks like a shell command that can be exploited if I can control the value of req.query.file. Crafting a request in Burp Suite for the endpoint, I got this;

I assumed req.query is used to store all queries defined in the request, so I added a GET parameter named file with a test value, and it got included in the command;

Injecting an encoded bash reverse shell in the file parameter, I got a shell as the user dasith;



PrivEsc


Can’t list sudo permissions for the user as I still don’t have his password. netstat showed a service running locally, which was identified as MongoDB using telnet;

Checking the .env file, I got a path for the service;

The user does not have SSH public key authentication setup, so I used ssh-keygen to setup one, so I can setup port-forwarding to the MongoDB service over SSH. I still got the same error when trying to access the MongoDB service over HTTP.

After reading up on MongoDB, I found a cheatsheet at https://gist.github.com/michaeltreat/d3bdc989b54cff969df86484e091fd0c that helped me enumerate the service. I found a database named web-auth containing four (4) hashes;

Attempt to crack the hashes using john was taking way too long, so I aborted it and move on. One of the hashes was eventually cracked, but didn’t give me access to anything.

Inside /opt/, I found a SUID binary owned by root, and what looks like the source code. Running it, it asks for a filename, and then report some stats for the file;

Privileges are dropped when saving results, which means I can’t write anything to protected paths. Attempts to step through the process while loading protected files using strace and gdb failed because they can’t attach to a process of higher privileges.

Going through the source code, I noticed core dumping was enabled;

Fuzzing the program’s input did not cause the program to crash. After taking a sanity check, I realized I could cause a program to crash and dump core by sending it a SIGSEGV (signal ID: 11). Running kill -n 11 <PID> while the program is asking where the results should be saved, I was able to crash it;

Checking /var/crash, I found the crash file generated;

After googling around, I learned that such crash files can be processed using apport-unpack tool, so I did;

The file CoreDump was identified as the core dump;

So I load it in the gdb installed on the host, and searched the memory mappings of the program using thefind command, and manually go through the stack using x/s <some-address>, but could only locate the name of the file (/root/.ssh/id_rsa) that was given to the SUID binary before the crash. With the help of the strings command-line tool, I was able to extract the private key of root (which was the file given to the binary before the crash);

I used the key to gain access to the box as root over SSH;



Summary


  • Identified running services with nmap
  • Used the documentation to create an account on the web API.
  • Found the JWT token signing key in a previous commit in the source files using git diff, and used it to forge a token for the admin user.
  • Found the path /api/logs that’s accessible only to admins is vulnerable to command injection, which I exploited to gain access to the box as the user dasith
  • Inside the box as dasith;
    • Exploited a SUID binary by causing it to core dump after reading the private SSH key of the root user.
    • Retrieved the SSH key in the crash file using apport-unpack and strings, and gained root access to the box over SSH.
This post is licensed under CC BY 4.0 by the author.
Contents