Windows Compress Utility SEH-Based Stack Overflow Exploit

Posted by Tech on July 2, 2019

Windows Compress Utility SEH-Based Stack Overflow Exploit

Some Context: I was working on an assessment and found open read/write shares on a machine. There was batch file that would auto-run every few hours. Inside was the Compress.exe command with arguments (In-file, Out-file). I can only modify said arguments, and end up getting command execution via SEH-Based stack overflow via the Out-file argument.

Windows Compress.exe?

The Microsoft® Windows® Server 2003 Resource Kit Tools are a set of tools to help administrators streamline management tasks such as troubleshooting operating system issues, managing Active Directory®, configuring networking and security features, and automating application deployment.

You can use compress.exe utility to compress a file from command line. It can be downloaded from the below location.

After installing the above, you can find compress.exe tool in the directory “C:\Program Files\Windows Resource Kits\Tools”

The usage of this tool is explained below with examples:

To compress a file example.txt: compress example.txt compressedfile1.txt

Another example below:

    C:\>”Program Files\Windows Resource Kits\Tools\compress.exe” sysinfo.txt sysinfo_compressed.txt

    Microsoft (R) File Compression Utility  Version
    Copyright (C) Microsoft Corp. 1990-1999.  All rights reserved.

    Compressing sysinfo.txt to sysinfo_compressed.txt.
    sysinfo.txt: 14211 bytes compressed to 3519 bytes, 76% savings.

    Total savings: 1 files, 14211 bytes compressed to 3519 bytes, 74% savings.



To start, I broke my metasploit packages and didn’t want to spin up a new vm. Most of this tutorial will be without using msfvenom/any of the metasploit framework suite.

Start with fuzzing the application, in this case I went straight for the out-file name as there is always a character limit in that field. (Prior to this, I pulled compress.exe into IDA and found the buffer only 300 in len.)

Typical output, it finished running and saved the file AAAAAAAA.. to the Desktop. Keep throwing the A’s. Eventually it will crashed:

Looks like it was around 500 * As.

Immunity Debugger:

Going to run the same exercise within Immunity:

Arguments are set, going to Open and click the play sign. Play out the application and wait for the victory of Access Violation when Executing to show.

Awesome, a fews things to note:

  • All of the A’s (hex 41), turned to a’s (hex 61). The application is going through the outfile name and changing all capitalize letters to lowercase. No issue with this as we learn later on.
  • Both nSEH and SEH are over written!
  • To many A’s, need to find out the exact location of nSEH and SEH.

Sometimes, when on engagements you don’t have your toolkits all in working order. I created a quick python script that will print the ABCs, 4 characters each, over the course of 624 characters. End results:

Running this back through immunity via same way as before:

Here are the new results:

This is intersting, you can see from the SEH chain of main thread that nSEH is overwritten by 71717171 (qqqq), and SEH is overwritten by 72727261 (arrr).

Were going to utilize POP POP RETN. Using a series of POP POP RET instructions will cause another exception after the original which will use the next SEH in the chain, the second EH will be a short jump over the first SEH into a shellcode. The idea is to not make a JMP SHORT but let the program execute normally the instructions contained in Next SEH and SEH Handler hoping it won’t crash. Then, if this instructions don’t cause any harm, we will be able to execute a shellcode situated after the SEH structure.

Before we find a POP POP RETN, lets set the nSEH to CC. (more info below)

Set the Breaks:

An INT3 instruction found within the code causes a pause of execution when run in the debugger — it’s the assembly instruction equivalent of setting a breakpoint in the debugger.


Using these characters in the data you send to an application is a great way to pause execution during the actual process of crafting an exploit.

Since this exploit isn’t script based but hands on text, we’ll need to convert ‘CC’ hex into ASCII.

All in all, CCCCCCCC = ÌÌÌÌ

Going back to the exploit payload, switch out the 71717171 (qqqq), for ÌÌÌÌ


Using the python plugin for Immunity debugger called Use the command “!mona seh” in the dialog box at the bottom of Immunity debugger.

Mona brought up a bunch of results. If you don’t have mona installed in immunity, it’s best to also do it manually.

  • To do this, rightclick anywhere in the disassembly portion -> search for -> sequence of commands
  • Or you can also just press Ctrl + S

A text field box would pop up, and you would want to search for:

POP r32
POP r32


We’ll be using the highlighted address, the start of the POP. Which is 02D228CA. Because of the awesome little endian, we’ll need to reverse the order:


and use our trusty online hex to ascii converter:

CA28D202 = Ê(Ò

Going back to the exploit payload, switch out the 72727261 (arrr)), for Ê(Ò

Rerun 0x2:

Reruning the new payload in Immunity, led to this:

From the SEH chain, we see that nSEH and SEH is both over written, on the stack we can also confirm. You’ll notice the EIP is set to 02D228CA. Lastly, were at the start of the POP POP RETN execution.

When we step through it, after the return it takes us to our 4 CC’s.

After the breakpoints, it goes back to our payload, opcodes 73 to 6F. Lets go ahead and change all those text to NOPs.

Fixing Payload:

With the current payload, it looks fine. Were going to switch out all the characters after the exploit text to NOPs.

Using the handy hex to ascii, were going to input 90 (nop) and covert that to ascii.

Here is what the payload now looks like:

Rerun 0x3

Reruning the new payload in Immunity, led to this:

A bunch of NOPs, we need to jump over the RET into the OP codes below. To do that we’ll need to do a short jmp, can’t do a long jmp as their isn’t enough bytes.

To figure out what opcodes you need, modify any of the NOP ones by double clicking on it and adding:

JMP short 0018FF86

In your case, you would want to see if it’s the same or different reg.

We learn that the OPcodes to jump over the ret, are EB FF.

Using the hex to ascii, were going to input EBFF and covert that to ascii.

Were going to take the ëÿ and switch it out with the two first ÌÌ, from the nSEH payload.

Here is the new looking payload:

I highlighted the part we switched out in RED.

Rerun 0x4

Using the new payload, and steping over until we get to our jmp, this is where you end up:

I quickly noticed that I didn’t jump long enough, going to now jump a little further down in the NOP sled.

I set a new Jmp short more down, hex codes: EB16:

and for the new payload:

I highlighted the part we switched out in RED.

Rerun 0x5

You know what to do by now, we’ll go to where we set the last breakpoint:

and now lets step into/over it: