Vulnserver KSTET Socket Re-use

Contents

In a previous post, Vulnserver KSTET Egg Hunter, we looked at how we can use an egghunter to obtain code execution within a larger chunk of memory. In this post, we will look at the KSTET Socket re-use WS2_32.dll recv() function and how we can re-use this to pull in a larger chunk of shellcode within a buffer we allocate ourselves. In regards to Vulnserver.exe, the recv() function is used anytime we send data to the socket. The data is then parsed by the server and decides what to do with it.

Prerequisites:

  • Vulnserver.exe
  • Immunity or Ollydbg – I will be using Immunity
  • Mona.py
  • Windows VM – I am using a Windows XP Professional host

Resources:


I’m going to skip some of the set-up assuming you understand how to attach a debugger to a process or start a process within a debugger. If you don’t, go ahead and read some of my other buffer overflow tutorials.

The Crash

Part of why I decided to build this tutorial us because I am currently studying for the OSCE. As such, we are going to use the Spike Fuzzer (Native in Kali) to fuzz the VulnServer.exe application and just like the egg hunter tutorial, we will be fuzzing the KSTET parameter. Here is our Spike Fuzzing template (kstet.spk):

The VulnServer listens on port 9999 and is residing at an IP Address: 192.168.5.130. So, our Spike Fuzzer syntax will be:

During any fuzzing, it’s a good idea to keep a WireShark instance running in the background so you can manually analyze the packets if a crash occurs.

Fuzzing with the Spike Fuzzer

After less than 2 seconds, we have a visible crash that looks to have overwritten EIP and EBP.

Overflow success

Looking at Wireshark we see that the last valid connection contained the following data:

Wireshark analysis of spike fuzzer

Buffer Overflow Standard Steps

This is the repetitive part of any Buffer Overflow:

  1. Determine the offset in which we overwrote EIP.
  2. Find a JMP ESP Instruction we can use.
  3. Overwrite EIP with the JMP ESP address and control the execution flow.

Determine Offset:

We create a standard pattern using the Metasploit Frameworks pattern_create.rb. This generates a unique string n bytes long that is used to determine the actual offset of the overwrite. The syntax for the ruby script is:

Create pattern to determine EIP offset

Build a Python proof of concept to crash the application similar to the Spike Fuzzer:

We send the payload, overwrite EIP with the pattern, and now determine the proper offset

Observed pattern overflowing EIP
Determine EIP offset in base 10

After we have the correct offset, we can build a proof of concept that shows two things:

  1. We have control of EIP and therefore have control of code execution
  2. How much space after the overwrite we have to play with (i.e. store our shellcode)
Analysis of the Stack after the overflow

We control EIP and we only have 0x10 Bytes of room for shellcode. That’s not enough for anything. However, we have 0x44 Bytes above ESP that we can use to build something useful. In the previous KSTET tutorial, we used this space for an egg-hunter. Now, we are going to build our own shellcode to re-use the WS2_32.recv() function.

Find JMP ESP:

I personally like to use Mona.py with Immunity Debugger to help determine a valid JMP ESP. There are tons of ways to find a proper JMP ESP address, this one just fits my needs.

Finding JMP ESP

Verify we have EIP Control:

Set a breakpoint at the JMP ESP address you selected. Amend you python script to include the JMP ESP address and the EIP overwrite and verify you now hit the JMP ESP address and get back to your code (in this case, the C’s).


A Short Jump

In order to get more room to build our custom shellcode, we need to make a short jump to the top of the A’s. In the start of this tutorial, I posted a link describing the JMP SHORT instructions. Basically, these are two (2) byte instructions that will help us move around in memory without taking up a shit ton of space.

In this case, we want to get to address 0x00B8F9C6. An easy way to build the shellcode is write the instructions directly within Immunity by hitting the space bar and typing in the assembly.

Building a two byte jump instruction

As you can see, from the blue text on the left of the Assembly instructions, our opcode is going to be \xEB\xB8. We can simply add this into our python script and verify we have in-fact jumped backwards to our required address.


WS2_32.recv() Review

Before we dig into writing custom shellcode, let’s analyze what is happening locally when recv() is called in the non-altered code.Let’s also define some terms and parameters that are necessary to fully understand before we understand the exploit.

The recv() function is part of the winsock.h header and takes a total of 4 parameters:

  1. SOCKET – This is a Socket Descriptor and defines a valid socket. It’s dynamic and changes each time the binary/exe is run.
  2. *buf – A pointer in memory where we want to start storing what is received from the socket connection.
  3. len – The size of the buffer in Bytes
  4. flags – Will always be set to 0 in our case. We essentially do not use this but need it to complete the function call.
MSDN WS2_32.recv Function Call

Three (3) of the four (4) parameters can be generated by us, the attacker. We easily make up our own values, pop them onto the stack and have a good ol’ day. But, the SOCKET descriptor is the odd man/woman out. This is set dynamically, by the program. It is, however, set predictably and as such we can analyze a live recv() call in action and find where it’s obtaining the SOCKET descriptor from. Once we know where it comes from, we can dynamically pull that value in with our custom shellcode to make our recv() call as legitimate as the original.

Analyze a Legitimate recv():

In your debugger, start a fresh run of vulnserver.exe and put it into a running state. To make sure the debugger (olly or immunity) has analyzed the code properly, hit CTRL+A. This will tell the debugger to analyze the assembly and point out any objective function calls. With this analyzed, look for the recv() function call.

From left to right, the first picture is where the recv() function is called within the program. The second picture is where we land when the CALL is executed. The address in the second image 0x0040252C is very important as that is that address we will CALL at the very end of our custom shellcode.

Next, let’s place a breakpoint at the CALL shown in the first image (leftmost image), and execute our overflow python script so we can observe the legitimate recv() function call.

When the python script is executed, we hit our breakpoint and can view the recv() parameters cleanly located on the stack for us.

Analysis of a valid WS2_32.recv function call
  • SOCKET DESCRIPTOR = 0x00000080
  • BUFFER START ADDR = 0x003E4AD0
  • BUFFER LENGTH = 0x00001000 (4096 Bytes base 10)
  • FLAGS = 0x00000000

Again, at this point, we only care where that Socket Descriptor came from. If we set our breakpoint a few instructions above the CALL <JMP.&WS2_32.recv> we find that MOV EAX, DWORD PTR SS:[EBP-420] is responsible for pulling in the Socket Descriptor. Cool, let’s do some basic math:

EBP = 00B8FFB4 and if we calculate: (00B8FFB4 420) = 00B8FB94‬.

If we navigate to this address in the debugger, we should find our Socket Descriptor and, we do.

Confirming we have found the Socket Descriptor

Custom Shellcode

Now that we have the location of the Socket Descriptor, we can start to build our custom shellcode to setup the stack for our evil recv() function call. Since the Stack grow from bottom up, we need to build our stack with that in mind starting by adding the FLAG parameter first and the Socket Descriptor last.

The easiest way to build your custom shellcode is simply to do it in the Debugger. Set a breakpoint at your JMP ESP, get to you JMP instruction and start building your shellcode. Below is an example on how I setup my Stack:

Let’s go through this step by step:

  • Lines 2-3: I found that the stack was not aligned after the second payload was sent. This aligns the stack for our second payload
  • Lines 4-5: We XoR the EDX Register by itself to make it 0 (0x00000000) and PUSH it onto the stack. This will serve as our FLAG parameter.
  • Lines 6-8: We cannot have a null byte (0x00) in our shellcode so, we add 0x4 to EDX and shift it left by 1 Byte (8-bits) to give us 0x400 This serves as our Buffer Size.
  • Lines 9-12: Zero out EDX with XoR, MOV 0xB8F9FB90 into EDX, shift right to get rid of 0x90 and get our 0x00 for a final value of 0x00B8F9FB. This serves as our Buffer Start Address.
  • Lines 13-15: Load Socket Descriptor addr. into ECX, PUSH the value of the data located at ECX (denoted as [ECX]not ECX, note the difference). This serves as the Socket Descriptor.
  • Lines 16-18: Load the address of WS2_32.recv, that we found when we analyzed the legitimate recv(), into EDX and CALL EDX to complete the function call.

Exploitation

All we have to do is generate a second payload and send it right after our overflow payload. Since we have tricked vulnserver into running the recv() function, it will take our second send data and store it at the buffer address we specified. Let’s test this out with a payload of 0xCC (Int 3) so that the program will halt when it hits our shellcode.

Secondary payload made it to our memory location

Let’s add some basic shellcode to pop calc.exe and verify that our exploit works.

Shellcode injection to pop calc.exe after exploit
No Comments

Post a Comment