Hack The Box : Lernaean

The Pen-Testing website Hack the Box came up on Hacker news yesterday, so I figured out the challenge to login to the site, and started working on some of the challenges they have available. This post will be my solution to the Web/Lernaean challenge and all the mistakes it took to get to the solution.

The Lernaean challenge is a simple brute force password hacking challenge. Given a form that accepts a password, try as many combinations as you can until you unlock the site. While I think you are supposed to use a tool (Lernaean references hydra, which is a brute force password tool) I decide I would get more out of it if I just wrote my own tool in python.

Mistake 1

The first mistake I didn’t catch early on, was the misuse of the python requests library for making post requests; I’ll partly blame the internet for this as they can’t just come out and give you a decent answer for making a post request , and I’m pretty sure the first site I went to had bad code that didn’t even work. The correct syntax for making the POST request with python requests is:

r = requests.post(URL , {'password': password})

Mistake 2

My second mistake was going slow. I had one thread, going through each password. This was taking a long time, and I had read in the forums that someone had gotten the answer within 20 seconds, so they must have been doing something in a parallel manner; so I updated my code to run multiple threads all checking password off a queue.

Mistake 3

Coming in third, which really didn’t do anything but slow me down more, was over thinking the solution. Instead of just trying each password out of known password dictionaries, I also mutated each password a bunch of different ways which added a bunch of more time to the run. I guess this is only a mistake in hind site when you figure out what the password was.

Also, since Lernaean is a reference to a hydra, I thought maybe the password would be related to that, so I ended up scraping all the words off the wikipedia page and trying those.

Mistake 4

The last mistake that took me a lot of time to figure out was just not understanding the output once you get the correct password. When the message came back telling me I was “too slow” I assumed that they meant the password had changed and I need to keep searching for a new password; it was only after reading through the forums that I found they meant that the page was reloading before I could see the solution. So I had to load the page in something that wouldn’t refresh before I could see the answer: curl --data -I -X POST --data "password=????" http://docker.hackthebox.eu:39951

Mutating the Passwords

Sometimes people make simple changes to common dictionary words to get their password. I figured I should try some permutations of the words I was bringing in to help my chances of guessing the password:

def numletters(password):
  s  = {'a': 4, 'e': 3, 'i': 1, 'o': 0,}
  for letter in s.keys():
    password = re.sub(letter, str(s[letter]), password)
  return password

def mutations(password, final=False):
  ret = [password]
  ret.append( password.lower() )
  ret.append( password.upper() )

  ret += [ "{}!".format(x) for x in ret]
  ret += [ numletters(x) for x in ret]

  if not final:
    if  password[-1] == "s":
      ret += mutations(password[0:-1], True)
      ret += mutations( password + "s" ,True)
    ret += mutations(password[::-1], True)

  return list(set(ret))

This code would take something like password and output: [‘dr0wss4p!’, ‘drowssap’, ‘p4ssw0rds!’, ‘PASSWORD’, ‘p4ssw0rd’, ‘password’, ‘DROWSSAP’, ‘DROWSSAP!’, ‘PASSWORDS’, ‘password!’, ‘passwords!’, ‘PASSWORDS!’, ‘drowssap!’, ‘passwords’, ‘p4ssw0rd!’, ‘dr0wss4p’, ‘PASSWORD!’, ‘p4ssw0rds’] ; this was great for finding mutated passwords, but also meant that each word you checked had an additional 18 checks it had to perform which may or may not be what you are going for.

Parallel runs

To make the code run faster and complete in my life time, I created a python queue and put all the possible passwords I wanted to try in that. I then set up a worker:

def worker(stopEvent,trash):
  while not stopEvent.is_set():
    password = q.get()
    if args.verbose:
      print("Checking password:", password)
      r = requests.post(URL , {'password': password})
    except Exception as e:
      print("Error:", e)

    if r.text.find("Invalid password") == -1:
      print("Password is:", password)
      print("Password found stopping all threads")

that would check a password and check to see if it was correct. I ended up using a thread.event to trigger all the other threads to stop when a password was found.

The complete code for this can be found here. If you are just out to solve the puzzle, you are better off using a tool dedicated to this task.


Writing the code myself taught me way more than plugging a dictionary into some pre-made software, but this will always be the case; to understand things and learn new things, you need to write your own tools. Even if you don’t end up using your own tools, you should be trying to write them just so you can learn how everything works. Also As you go through these challenges you learn how other people think and build puzzles, which is probably also a worthwhile skill to have (although it can be frustrating sometimes).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s