Heal

Port Scan

We always begin by scanning for open ports to identify services exposed by the target. For this, I used RustScan, a faster alternative to Nmap that works well for large port ranges. Once it identifies the open ports, we feed them to Nmap to get detailed service and version information.

An initial Nmap scan detects an SSH service running on port 22 and a nginx web server on port 80 .

To access the webapp we have to add it to our /etc/hosts file

The first page is a normal login page but we dont have an account so lets create one

Login Page

Creating an account we get an error saying something went wrong which is weird

Signup page

A good habit to develope is look at the requests in burp and track everything from there. from the request Header Host we can see a subdomain called api.heal.htb we dont have this subdomain in our /etc/hosts file hence the error occured

new subdomain found

so we also add it to /etc/hosts and try to sign up again

Since we found a new subdomain its worth taking a look at it before we conitune.

we find that the application is built on

  • ruby rails version 7.1.4

  • ruby version 3.3.5.

take note of that since it's always good to take note of versions and in a real engagements this counts as a finding especially if they’re outdated or known to be vulnerable

api.heal.htb ruby rails page

Coming back to the main domain, we find a normal resume builder page that asks for some basic information.

After filling out the form, there's a button to download the resume as a PDF.

Resume builder page

Shell as www-data

Again we have to look at each function in burp to analyze the request. here we find that the server sends an OPTIONS request to

This stands out immediately. A filename parameter in the URL that isn’t sanitized or validated is a classic signal for Path Traversal. Let’s test for that.

Default pdf download

But the response comes back with an error: "Invalid token".

File read to /etc/passwd

Looking at previous successful requests, we spot the Authorization Bearer token. We resend the Path Traversal request with the same token added to the headers.

We now get the contents of /etc/passwd, which confirms an Path Traversal vulnerability.

File read to /etc/passwd working

From the /etc/passwd file, we can find some usernames such as

Now that we know the app is Ruby on Rails, and we have Path Traversal, the next logical step is to extract application files. Let’s start with the Gemfile.

What is the Gemfile?

  • In Ruby on Rails applications, the Gemfile defines all the libraries (called "gems") the app depends on. It’s usually found in the project’s root directory.

Using our Path Traversal endpoint again:

We get:

This confirms the app is using:

  • SQLite3 (not a server-client database, but a file-based DB)

  • JWT and bcrypt, used to create the cookies

  • imgkit, related to the resume PDF generation

The sqlite3 gem is the most important clue here, as it suggests the app stores its database in a local file. Let’s try to find where.

Database.yml

In Rails, the config/database.yml file tells us where the database files live.

Using our Path Traversal again:

We get:

And there it is. The SQLite database is stored at: storage/development.sqlite3

This file likely contains all user data, including emails, password hashes, tokens, and potentially session data.

From the output, we find a username and a hash

let's try cracking it using John

We continue to enumerate the web application and spot a feature named “Survey”. Attempting to access it redirects us to a new subdomain:

new subdomain take-survey.heal.htb

Since this subdomain is unknown to our machine, we register it locally:

Now, accessing http://take-survey.heal.htb, we explore the page source and identify the platform powering it: LimeSurvey.

found limesurvery

According to LimeSurvey documentation, the default admin panel is available at /admin. Checking http://take-survey.heal.htb/admin, we are greeted with a login page. Let’s try the credentials we previously found:

Lime survey login

lets try logging in using the username and password we have

We’re now logged into the LimeSurvey admin panel.

lime survey main page

Navigating to the Configuration → Plugins reveals that we can upload a plugin. That’s a juicy attack surface—especially when combined with a known version. and as we can see, it runs on version 6.4.4 so all we're missing is the payload.

limesurvey Plugin page

A quick GitHub search turns up a plugin-based reverse shell exploit specifically crafted for LimeSurvey. After cloning the repo, don’t forget to edit the revshell.php file to set your IP and port for the callback.

And of course ready your netcat at the port you chose

We now have a basic shell:

now you might see the shell is very unstable and extremly limited to upgrade it we can do the following

Shell as ron

With shell access, we explore the LimeSurvey directory and locate the main config file:

in the config file, we see the db password

Since the /etc/passwd file (from earlier Path Traversal) listed ron as a user, let’s attempt an SSH login using the PostgreSQL password as Ron's system password:

PrivEsc

After gaining access, our first step is to enumerate open services:

We observed the following unusual open ports:

These ports are strongly associated with HashiCorp Consul, a popular tool for service discovery and configuration in microservice environments.

to confirm that we can use

But hey, what even is Consul ?

Consul is an open-source tool by HashiCorp that helps services in your infrastructure automatically find and communicate with each other. It provides service discovery, health checks, and secure communication—making it easier to manage microservices and distributed systems.

Since Consul is only accessible locally (127.0.0.1:8500), we need to port forward to access it through our machine:

Now we can access the UI via: http://localhost:8500

Upon visiting the UI we see some services running, also we note that the Consul version is 1.19.2.

Visiting the Consul documentation and read a bit to understand how to work with this api. we note that the service list is typicly listed in /v1/agent/services

We Continue reading the Consul documentation and learn that we can register a new health check or service via an HTTP PUT request. If the Consul agent is running as root, and our HTTP call triggers a script execution, our script will also run as root..

So, we prepare a shell script to:

  1. Copy /bin/bash to /tmp/shell.

  2. Set the setuid bit on /tmp/shell to run it as root later.

  • #!/bin/bash This specifies that the script should be run with Bash.

  • cp /bin/bash /tmp/shell This copies the system’s bash binary (the shell executable) to /tmp/shell.

  • chmod +s /tmp/shell sets the setuid bit. This means when you run /tmp/shell, it will run as the owner of the file—which in this case, is root.

  • chmod +x /tmp/rev.sh Make it executable.

to understand more about the setuid bit check this out ⬇️

Post Exploitation

Now that our shell is ready lets register it on the server, for this we can use this curl command

  • curl -X PUT http://localhost:8500/v1/agent/check/register Makes a PUT request to the Consul agent API to register a new health check.

  • -H "Content-Type: application/json" Tells the API the data is in JSON format.

  • -d '{ ... }' The JSON payload describes the new health check:

    • "ID":"rce" The unique ID of the check.

    • "Name":"shell service" The name of the check.

    • "Shell":"/bin/bash" Specifies /bin/bash as the shell to run the command.

    • "Interval":"5s" Run the check every 5 seconds.

    • "Args":["/tmp/rev.sh",""] The arguments: here, it tries to run /tmp/rev.sh.

After 5 seconds we can list the files of the /tmp file and see a new file called shell

Running this file will grant us a new shell but this time with root access

We add -p to preserve privileges and invoke a root shell:

PrivEsc the easy way

Since we now know consul version is 1.19.2 we look for known exploits on exploitdb and github

searching on metasploit we find an exploit for conusl on that version so we go ahead and use it

Just becasue the metasploit way is easier doesnt mean it's wrong. always remember that hacking is about finding the easiest way to achive your goal not the most complex one 😉

Summary of Full Attack Chain

Stage
Action Taken

Recon

Nmap identifies SSH + Web

Web Exploitation

Ruby on Rails → Path Traversal

Loot Config Files

Extracted secrets from config files

User Shell

SSH access as ron

PrivEsc Recon

Found localhost-bound Consul services

Pivoting

Port forward 8500 → local

Exploit

Consul v1.19.2 → RCE via Metasploit

Root Shell

Reverse shell opens as root

Read Flag

Read /root/root.txt

Last updated

Was this helpful?