Normal view

There are new articles available, click to refresh the page.
Before yesterdaySecurifera

Attacking .NET Web Services

By: b0yd
6 March 2023 at 22:00

This article is in no way affiliated, sponsored, or endorsed with/by Siemens Healthineers or Microsoft Corporation. All graphics are being displayed under fair use for the purposes of this article.

Last year I spent some time looking for vulnerabilities in a commercial cardiovascular imaging web application called  Syngo Dynamics. This product is developed by Siemens Healthineers. Syngo Dynamics is a rather complex application that consists of a web application, .NET web services, native binaries, and a database.

For the purposes of this blog post, I’m going to focus on the web services portion of the application. I gave a BSIDES talk on this topic for those that may prefer that format. The remainder of this post will detail my approach when performing white-box vulnerability research on a .NET web application. The first thing I like to do is open up the IIS config and any “web.config” files for the application. The IIS config can be found at “C:\Windows\System32\inetsrv\config” and the “web.config” files are typically in the app pool directories with a format similar to “<Drive Letter>\inetpub\temp\appPools\<App Name>\web.config“.

When performing white-box vulnerability research, my primary goal is to locate the code so I can look for bugs. There’s two important things we can take away from these config files to further this effort. The “application” node defines the mapping of the application endpoint to the physical path on disk where code exists. The “endpoint” node inside the “service” node specifies the class contract that defines the endpoint behavior. If we navigate to the “physicalPath” for the “DataTransferServices” we find “svc” files that describe each service.

If we look at the contents of the “CommonService.svc” file, we can see that the assembly that implements this service is called DataTransferServices and the service name is DataTransferServices.CommonService. At this point we need to locate the assembly to begin the reverse engineering process. But what is an assembly?

So we’re looking for an assembly named “DataTransferServices.exe.” or “DataTransferServices.dll” likely in the same directory tree. If we do a quick search in explorer we find what we are looking for.

Now that the assembly has been located, it’s time to open up the binary in a decompiler. Since the binary is a managed C# assembly, we can decompile it back into readable C# code. There are two tools I like to use for this, dotPeek & dnSpy. They are mostly feature equivalent with the exception of dotPeek also providing debugging capabilities.

Opening up the “DataTransferServices.dll” binary we find the contract interface that defines the functions that are exposed through the service. From dnSpy we select “Go to Implementation” to view the code for one of the functions.

One of the first things that jumped out to me is the concatenation of user controlled parameters for the file path that is then read and returned in the response. Praetorian wrote up an interesting article about abusing the Path.Combine function in C# . Basically, if an absolute path is given as one of the parameters to concatenate, the others are ignored. To confirm this suspicion, I needed to craft a web request to hit this code. The first thing I did was navigate to the service endpoint in a browser to see what it displayed.

It looks like the metadata output is disabled. Unfortunately without metadata output enabled we won’t be able to easily generate sample web requests for the web service endpoints. Luckily the webpage tells us how to enable it inside the “web.config” file for the web service. After making the modification we are greeted with a slightly different landing page.

The service endpoint shows a link to a new “wsdl” endpoint that describes the functions published by the service. Unfortunately without something to ingest, the “wsdl” XML file it isn’t very useful. Fortunately, Burp has an extension called Wsdler that was made for this very purpose. If we navigate to the “wsdl” endpoint we can then right click on the request in the Burp Proxy -> HTTP history tab and send it to the Wsdler extension.

Using Wsdler you can select the operation in the list and it will generate a sample HTTP SOAP request. The request can then be sent to the Repeater tab to test out. I enter the full path in the “fileName” field to confirm the ability to read arbitrary files using the Path.Combine function.

Our suspicious was correct. We successfully read an arbitrary file rather than files in the intended directory. Often times it isn’t this simple and having the ability to debug the code is pivotal. In the next section, I’ll walk through how to do that using dnSpy.

Debugging .NET web services using dnSpy and dotPeek

To begin debugging a .NET web service, we need to locate the IIS w3wp.exe process. Click the Debug-> Attach to Process menu in dnSpy to bring up the process selection dialog. If no w3wp.exe process exists, send a web request to the web service to ensure a work process is created.

If the w3wp.exe process still isn’t listed, there could be an architecture mismatch either between the dnSpy application or the IIS server process. It is important to ensure the architecture of dnSpy, the .NET assembly, and the IIS server process are all the same. The architecture of the IIS process can be modified in the Advanced Settings of the IIS Application Pool.

Once you attach to the w3wp.exe process you should be able to drop breakpoints in the decompiled source that will get hit when the code executes. If you drop a breakpoint and the circle is an outline rather than filled-in, you have one more step to complete. This means that the debugger could not find symbols for the application you are debugging.

In this case we can use dotPeek to generate the symbols for the assembly. Simply open the assembly in dotPeek, right click on the assembly in the “Assembly Explorer” and click Generate Pdb.

After the PDB has been generated, copy the file into the same directory as the assembly that is being debugged and it should be automatically loaded. Restart the debugger and begin stepping through the code.

As shown in the screenshot above, I can confirm at the code level using the debugger that Path.Combine does indeed allow for the opening of arbitrary file paths if one of the arguments is absolute.

Finding Variants

With the discovery of one misuse of the Path.Combine function resulting in an arbitrary file read, it makes sense to search the rest of the code base for additional instances that may have more impact. Since this vulnerability isn’t particularly complex, I chose to use grepWin to search for other uses of Path.Combine who’s arguments may be controllable.

Reviewing the results led to the discovery of a similar usage of Path.Combine but this time as a file write. The impact of an arbitrary file write is often more critical as it typically allows for code execution.

Ultimately an additional 7 instances of exploitable insecure Path.Combine usage were identified. These findings fell into three vulnerability categories, arbitrary file read, arbitrary file write, & server side request forgery (SSRF). I will leave exploitation of these vulnerability types as an exercise for the reader as this is a pretty well documented topic. For some examples feel free to watch my BSIDES Charleston 2022 talk.

Vendor Disclosure & Patch

I reported these issues through the Siemens Healthineers vulnerability disclosure process and can say everything went smoothly and they worked with me to get the issues fixed and patched in a reasonable time frame. Given the severity of these findings, we strongly encourage anyone that has Syngo Dynamics deployed to update to the latest version immediately. More details about the vulnerabilities can be found on the Siemens Healthineers security advisory page.

Vocera Report Server Pwnage

By: b0yd
24 April 2023 at 14:36

This article is in no way affiliated, sponsored, or endorsed with/by Vocera Communications or Stryker Corporation. All graphics are being displayed under fair use for the purposes of this article.

Quest for RCE

Last year during a routine penetration test, our team came across a interesting target called Vocera Report Server while reviewing web endpoint screenshots.

A little research revealed that the “Vocera Report Server software and the associated report console interface provide administrators, managers, and decision makers the ability to monitor system performance and generate reports for analysis” for the Vocera Communication System. When we click on the “Vocera Report Console” link we are greeted with the following login page.

Step 1:  head over to Google to see if we can find any documentation that might list default credentials for the application. As luck would have it, this page comes up and kindly tells us what the default password would be.

Fortunately the system owner didn’t change the password and we log right in. Once inside we start perusing the various endpoints to get a feel for what the application is used for. Right off, the first thing that stands out is the menu that is named “Task Scheduler”. Clicking on the menu brings up a panel that appears to let you create tasks that will be executed.

After tinkering with the various tasks, it appears we can only edit existing tasks. We also can’t seem to get arbitrary command execution or injection by modifying the existing entries. At this point we decided it would likely be more fruitful to move on to a white box approach and see what the code is actually doing. We reached out to a colleague to get us access to the server using some credentials they had cracked after pulling the hash with Responder.

Since the application is written in Java, we open up the class files in JD-GUI to begin analyzing the function responsible for executing tasks. The first issue we notice is that the function that parses the user-controlled task execFileName attempts to retrieve the filename portion of the path by searching for the last occurrence of a backslash. Unfortunately in Java, forward slashes are automatically normalized into directory separators in a file path. This means we can traverse out of the intended directory.

While the path to the executable task is controllable, a check is performed that ensures that the file contains the word “java” before executing. This means if we can control the contents of a file on disk, we can execute arbitrary commands.

How then do we get a file on disk that we control? What if we can affect the log file? It looks like if an exception happens when executing the task, a log entry is created.

Sure enough, if we specify a task execfilename that doesn’t exist, an entry is created in the log file that also includes the task parameters that we can inject arbitrary data. With a little creativity, we are able to inject arbitrary commands and then point to the log file using the directory traversal to achieve remote code execution.

Can we do better?

With a path to execute arbitrary commands identified, we shifted our focus to finding a way to accomplish the same thing but without having to authenticate first. While investigating the task execution code in the previous exercise, we noticed there is a websocket interface that the web server communicates with when executing a task. After some testing it was determined that this interface was unauthenticated. In addition to the “runTaskPage” function mentioned above, there are a few other operations that appear to be related to database management functions that are worth investigating.

If we look at the code for the restoreSqlData operation we see that it executes a bat file that in turn executes another Java JAR. Inside that JAR, the function that handles the restoreSqlData operation parses the “uploadFile” parameter and appends it to the local “backupDir”. This instance is also vulnerable to directory traversal like the one mentioned previously.

The specified file is expected to be a zip file that is then programmatically unzipped and written to disk. The problem, as you could probably guess, is the unzip function is vulnerable to directory traversal which could lead to an arbitrary file write. If the unzip succeeds, a particular file is read from the archive that is then used to completely overwrite the database. DANGER: THIS OPERATION OVERWRITES THE DATABASE SO ADDITIONAL MEASURES NEED TO BE TAKEN TO PREVENT THIS. 

Since this server hosts a web server and is running as SYSTEM, an arbitrary file write can be used to achieve remote code execution by writing a webshell in the webroot. To summarize, in this instance we have found an unauthenticated endpoint that allows for a privileged file write if we can place an arbitrary file somewhere on the file system. We lack one more primitive to pull off this exploit. Back to the code!

Given the specific requirement for a file write, we search for any references to “write” and work our way back to any web endpoints that would reach that code. This concept is often referred to as source to sink data flow analysis. After some time we find a class called MultipartRequest. This class is instantiated from an incoming multipart/form-data request. If the requests contains any parameters that are named filename, the data is read and written to a file in a temp folder.

If we search for references of RequestContainer, the class responsible for creating MultipartRequest instances, we see it is created by the BaseController class on the handling of each HTTP request. Since BaseController is an abstract class, we search for any child classes and find ReportController. This is perfect since ReportController is the primary endpoint for the application. This means if we send an HTTP request with Content-Type multipart/form-data to the ReportController endpoint, the contents of any parameters with a Content-Disposition that contains a filename will be written to disk in a temp directory. This best part is this is all unauthenticated (another bug)!

Now we have all the pieces necessary to construct an exploit chain to gain unauthenticated remote code execution on the Vocera Report Server. First we construct a malicious zip file with a webshell embedded with a directory traversal path. Next we upload a zip file to the temp directory with a multipart request. Finally we send a websocket request with the restoreSqlData operation with a directory traversal path to our uploaded zip file.

WAIT!!!  HOW DO I NOT CLOBBER THE DATABASE?!?!

As much as any customer likes red teamers proving exploitation, nuking an application’s database is probably not a reasonable loss to prove code execution. That means we needed to put in a little more effort into the exploit to avoid this. If we look at the SQL restore function, we can see that if a ZipException is thrown (that is not a version issue), the function will bail out.

How then do we cause a ZipException while also successfully executing our arbitrary file write? If we look at the JDK source code for ZipInputStream we can see a simple way to cause a ZipException to be thrown from a specific ZipEntry. If we set the first bit of the flag field in the ZipEntry a ZipException will be thrown since encryption is not supported.

If we lookup the offset for LOCFLG we see it is at index 6 in the ZipEntry header. We can code up some python to modify the zip entry contents after we zip up our payload as shown below.

Copy to Clipboard

Vendor Disclosure & Patch

I reported these issues through the Stryker vulnerability disclosure program and can say everything went smoothly and they worked with us to get the issues fixed and patched in a reasonable time frame. Given the severity of these findings, we strongly encourage anyone that has Vocera Report Server deployed to update to the latest version immediately. For tracking purposes, the vulnerabilities discussed here represent CVE-2022-46898, CVE-2022-46899, CVE-2022-46900, CVE-2022-46901, and CVE-2022-46902.

ScienceLogic Dumpster Fire

By: b0yd
16 August 2023 at 02:50

This article is in no way affiliated, sponsored, or endorsed with/by ScienceLogic, Inc. All graphics are being displayed under fair use for the purposes of this article.

Just another Day

During a penetration test for a client last year, our team identified a noteworthy target that piqued our interest. A screenshot of the website appeared in our scan findings.

After a brief investigation, we found a page that provided a clear overview of the web application’s potential function and its default credentials.

The default credentials for the phpmyadmin server on port 8008 were also provided. This detail becomes crucial later, as it grants the ability to directly modify records within the application database.

Regrettably, the system owner had not updated the default passwords, allowing us to access the system. Immediately noticeable was a menu named “Device Toolbox.” We started examining the parameters provided to these tools, as they frequently have command injection vulnerabilities due to inadequate input filtering.

After trying different payloads in the request fields, the following screen appeared as we navigated through the tool wizard.

Having demonstrated command execution, we swapped the payload for a reverse shell callback and launched it. This granted us shell access to the server. Considering the simplicity of uncovering this initial command injection vulnerability, we believed there might be more similar flaws. Now, with system access, we can observe process creation events using one of our preferred tools, Pspy. As expected, four additional command injection vulnerabilities were identified by exercising various endpoints in the web application and monitoring process creation events in Pspy. An example is illustrated below.

Having access to the file system, we proceeded to examine the web application’s source code. This would make identifying vulnerabilities simpler than through blackbox testing. However, when we tried to view the PHP files, they seemed to be indecipherable.

What about root?

Given our inability to access the web application’s source code, we shifted our focus towards pinpointing potential paths for privilege escalation to root. We uploaded and ran LinPeas on the system to identify any potential privilege escalation vulnerabilities. While LinPeas didn’t reveal anything of particular use, a copy of the sudoers file was located in one of the backup folders on the file system. It had several applications that could be executed as root, without a password, that have known privilege execution capabilities.

The find command is one of my gotos after running across this article years ago. Running the following command will execute the listed command as root on the system.

Copy to Clipboard

Having gained root privileges, we could now revisit the web application and hopefully find a way to review the source code for vulnerabilities.

Let’s briefly diverge to discuss the DMCA and the laws surrounding the circumvention of copyright protections. I bring this up because even basic encoding or encryption of source code might be viewed as a method to safeguard copyright. This could potentially hinder security researchers from examining the source code for vulnerabilities. Fortunately, Title 37 recently updated laws surrounding this topic effectively granting security researcher exception to this rule if done under the auspices of good-faith security research. Given that we are red teamers performing good-faith security research, securing our customers against critical vulnerabilities, we clearly fall under this exception.

We took a look at the PHP configuration and noticed a custom module was being used to load each source file. Analyzing the module in IDA Pro, it appears to be a simple function that decrypts the file with a static AES 256 key and then decompresses the output with zlib. Nothing fancy here.

Running this algorithm against the garbled source does the trick and we end up with normal looking PHP.

Beware what’s inside!!!

With access to the source code, we started scrutinizing it for more serious vulnerabilities. We had previously observed a command injection bug where the command was saved in the database and subsequently fetched and executed. This prompted us to search for potential SQL injection vulnerabilities, which might be escalated to remote code execution. As we examined the application endpoints, we discovered what seemed like systematic SQL injection issues. After pinpointing roughly 20 SQL injection vulnerabilities, we chose to conclude our search. A few examples are provided below.

Having identified a combined 25 command injection and SQL injection vulnerabilities we decided to stop bug hunting and reach out to the vendor to begin the disclosure process.

Vendor Disclosure & Patch

We’d love to say responsibly disclosing the vulnerabilities we discovered went smoothly, but it was easily the worst we’ve experienced. What will follow will be presented as a comical list of when responsible disclosure is probably going to go bad. In reality, all of these things happened during this one disclosure.

  • The vendor has no public vulnerability disclosure policy

  • The vendor has no security related email contacts listed on their website

  • The vendor Twitter account refuses to give you a security contact after you explain you want to disclose a security vulnerability.

  • After spamming vendor emails harvested from OSINT, the only response you get is from a random engineer. Fortunately, he forwards the email to the security director.

  • The security director refuses to accept your report, and instead points you to a portal to submit a ticket.

  • After signing up for the ticketing portal, you find that you can’t submit a ticket unless you are a customer.

  • When you notify the company that you can’t submit a ticket unless you are a customer, they tell you to have your customer submit the report.

  • When you send the report anyways, encrypted, hosted on trusted website, they refuse to open it because they claim it could be a phish.

  • Individuals from the vendor, reach out to arbitrary contacts in your customer’s organization to report you for unusual, possibly malicious behavior.

  • Upon verification of your identity by multiple individuals in your customer’s organization, they agree to open the results but go silent for  weeks.

  • You receive an email from @zerodaylaw (no seriously) saying they will be representing the vendor going forward in the disclosure process.

  • The law firm has no technical questions about the vulnerabilities themselves, but instead about behavior surrounding post-exploitation and why this software was “targeted“.

  • After multiple, unresponsive, follow-up emails with both the law firm & the vendor about coordinating with @MITREcorp to get CVEs reserved, you get an email asking to meet in person, that very week.

  • In the follow-up phone call (after declining to meet in person), the vendor claims most of the bugs were “features” or in “dead code“.

  • The primary focus on the call with the vendor is how we “got” the company’s code and not about vulnerabilities details.

  • The vendor claims that meeting the 90-day public disclosure is unlikely and given their customer base they have no estimate on when public disclosure could happen.

  • After the phone call, the vendor sends an email asking questions focused on exact times, people, authorizations, and details surrounding the vulnerability coordination with @MITREcorp

  • Lawyers from the vendor contact your customer’s organization requesting copies of all correspondence with @MITREcorp.

From the list, it’s evident that the interaction ranged from being challenging to outright hostile. However, there was a silver lining: Securifera opted to pursue the status of a CVE numbering authority (CNA). Obtaining CNA status enables an organization to reserve and disclose CVEs more conveniently.

In the last email correspondence with the vendor, nearly 9 months ago, the security director asserted that the vulnerabilities were addressed. However, they remained reluctant to proceed with CVE issuance. Considering the extensive duration that’s transpired, we opted to independently proceed with CVE issuance and disclosure. As a result, the vulnerabilities we identified are logged as CVE-2022-48580 through CVE-2022-48604. We hope the aforementioned list can act as a guide for vendors on practices to avoid in the realm of responsible vulnerability disclosure. For vendor’s looking for a good reference for how to properly run a coordinated vulnerability disclosure program, the following guide was put together to assist by the smart people at Carnegie Mellon.

CVE-2021-27198

By: b0yd
25 October 2023 at 01:26

This article is in no way affiliated, sponsored, or endorsed with/by Visualware, Inc. All graphics are being displayed under fair use for the purposes of this article.

Revisiting an Old Bug: File Upload to Code Execution

A colleague recently contacted me about a bug I discovered a couple of years ago (CVE-2021-27198). The vulnerability was an unauthenticated arbitrary file upload issue in version 11.0 to 11.0b of the Visualware MyConnection Server software. At the time I hadn’t actually proven remote code execution even though I rated it as critical. So when my colleague asked me how I exploited it, I felt like I had to show it was possible. This endeavor proved to be both challenging and enlightening so I thought I’d share the experience.

The MyConnection Server software is coded in Java and intended to work seamlessly across different platforms. In this case, the arbitrary file write was privileged, granting the file server elevated permissions, with SYSTEM on Windows and root on Linux. With a privileged file write, achieving code execution is often straightforward. This post by Doyensec outlines the most common approaches, which can be broadly categorized into two groups: web application-specific and operating system-specific. Web application-specific methods involve seeking ways to initiate execution within the web server process. Examples include uploading web framework configuration files or web application source code. On the other hand, operating system-specific techniques involve finding execution triggers controlled by the operating system itself, such as services, scheduled tasks, cron jobs, and so on.

Regrettably, in the case of this particular bug, I cannot directly target the web server itself since it’s a pure Java implementation, as opposed to using a web server framework like Apache or Nginx. As a result, our focus will primarily shift towards exploring operating system-specific possibilities or more innovative approaches.

Windows RCE

With this in mind, I began researching possible techniques for achieving code execution solely through a file write. In the Windows environment, my usual strategy would involve targeting vulnerable applications susceptible to traditional DLL hijacking or phantom DLL hijacking, leveraging the privilege granted by such writes.

I opened up Sysinternals Procmon tool and began looking for “NAME NOT FOUND” or “PATH NOT FOUND” results for CreateFile. I usually look for instances of executables or DLLs. While looking through the results, I noticed that every minute the MCS java process is attempting to open several “rtaplugin” JAR files, some of which do not exist.

Based on the output, it looks like the server is dynamically loading these JARs from disk into the java process. If this is the case, I may be able to get arbitrary code execution by simply placing a custom JAR file in a specific place on the file system. I decided to open up the MCS JAR in JD-GUI to investigate.

When I searched for “rtaplugin”, I found a class named RTAPlugin that has a function that appears to load a file from disk, create a custom ClassLoader, loads a class from the file, and then creates a new instance of the class. This is exactly what I need!

To confirm execution, I created a simple POC that executes calc.exe when an instance of the class is created.

I then just manually copied the JAR into the appropriate directory to see if it was loaded. I was thrilled to see that it worked! Now I just needed to test it using the file upload vulnerability.

I loaded up Burp and pasted in the JAR file as the body of the file upload request. Unfortunately when I sent it over, I didn’t see calc.exe in the process list.

When I opened up the log file for the MyConnection server, I found the following exception was thrown when attempting to unzip the JAR.

I took the original JAR payload and diff-ed it against the one that was uploaded into the rtaplugin directory. I found that certain bytes were getting corrupted. I opened up JD-GUI again to take a closer look at the code that performed the file write.

What wasn’t immediately obvious (at least to me) was that there was an implied encoding/decoding that was happening with the calls to String.valueOf and String.getBytes. As a result, certain ranges of bytes were getting corrupted. On Windows I found that bytes between 0x80 and 0x9f were being replaced with other values. This meant I had to do some bit fiddling to get the payload to work.

After doing a little bit of googling, I found a CTF writeup by Yudai Fujiwara that faced a similar encoding problem. The writeup provided code for generating a zip file that only contained bytes in the ASCII range, 0x0 – 0x7f. The script primarily focused on two structures in the zip protocol that needed to be free of non-ascii bytes, the CRC for zipped files, and any length fields.

The script brute-forces a valid ASCII value for the CRC field by iteratively modifying the inner file. In the CTF challenge, the zip  contained an ASCII script, whereas a JAR contains a binary class file. I updated the algorithm to perform a similar modification to the Java source, and then recompile the Java class file on each iteration to make the CRC update.

Copy to Clipboard

After creating the ascii-zip payload, I uploaded it using the vulnerable endpoint. As hoped, it was loaded and executed as SYSTEM on the target server. To keep from having to recompile a JAR to execute different commands, I generated a JAR that would execute a script at a known location. I could then separately upload that script with a new command whenever I wanted command execute. I added the “setExecutable” directive to ensure it worked on Linux as well.

Linux RCE

When dealing with Linux-based operating systems, one of the primary challenges when exploiting arbitrary file write vulnerabilities is ensuring that file permissions are correctly configured. Even if a file is executed, it won’t function if it isn’t marked as executable. To overcome this obstacle, I am targeting files that already have the execute bit set.

As I had anticipated, demonstrating exploitation on Linux turned out to be quite straightforward by simply overwriting scripts within the /etc/cron.* directories. On Red Hat distributions, you can achieve relatively timely execution by overwriting the /etc/cron.hourly/0anacron script. The drawback to targeting cron jobs is there is no guarantee specific scripts exist, and overwriting them can cause system instability.

Assuming outbound network connectivity isn’t blocked, a simple reverse shell is likely the simplest payload to put in the cron job. If that doesn’t work, the cron job can be modified to contain a base64 encoded copy of the unmodified rtaplugin JAR that is then copied to the correct directory. The same technique as described above can then be used to get repeated code execution by uploading different versions of the script to/tmp/b.bat.

No License, No Wurk!

After all the effort put in to develop the rtaplugin exploit, I was disappointed to find out that rtaplugins (and the associated thread that loads them periodically) are only active when the web server has a valid license. I wasn’t aware of this because my test instance was still within its trial phase. I decided to take another look and see if our privileged file write vulnerability (CVE-2021-27198) can be used once again, but this time to trick the server into thinking it is licensed. The hope here is to find a file or database entry that can modified with our file upload exploit to bypass licensing.  I want to be sure to clarify here, nothing I will describe here can be used to subvert or crack the license for this software on a fully patched system. The goal is to use our already privileged file system access to bypass any license checks.

When you navigate to the web application home page, you’ll see a menu on the left that contains a link to “Licensing”. It’s safe to assume this is the right place to look.

Sadly, when you click on it, you’re presented with the login page. If you’re fortunate and the password for the admin user hasn’t been changed, the next page you’ll encounter is shown below.

If you’ve made it this far, or happen to guess some credentials, you will have sufficient permissions to access the server licensing endpoint. There’s not much to go on in regards to the format of the expected key other than the hint that it starts with MCS.

I opened up the JAR again and tracked down the code responsible for handling the license activation. The handler performs a series of checks and transforms before making a web request to a visualware domain to verify the license.

If that fails (by exception), the server then attempts to validate the license by sending a specially crafted DNS request.

Leveraging the privileged file write capability of CVE-2021-27198, there are several methods at my disposal for circumventing the licensing without having to decipher the licensing key directly. As the software initiates a network request to validate the entered key, it can be rerouted to a server under my control to authenticate the supplied key.

The most straightforward method to achieve this is by modifying the hosts file, a file which contains mappings of IP addresses to hostnames, and is typically the first location an operating system checks when resolving a hostname’s IP address. On Windows systems, you can find this file at C:\Windows\System32\drivers\etc\hosts, and on *nix systems, it’s located at /etc/hosts. To manipulate the license verification process, I can simply insert a new entry into the hosts file, directing the license server domain to the IP address of a server under my control.

An alternative approach, specific to *nix systems, involves altering the /etc/resolv.conf file, which specifies the DNS server’s IP address used for domain resolution. By changing the DNS server address to a server that I manage, I can ensure that any DNS requests for the license server are resolved to my fake license server’s IP address.

WARNING: It should be noted that overwriting the /etc/hosts or /etc/resolv.conf file can cause system instability if certain configurations are expected to properly resolve custom DNS entries or resolvers.

My next step is to trace back the activate function to the web endpoint that can trigger it. Unfortunately, it appears the key entered into the web form is not the same format as what is sent to the license server. After performing a couple string-based checks, a validate function is called on the provided license key. If you look at the function closely, it may look familiar. It appears to be implementing a handcrafted version of RSA.

From a security standpoint, the rationale behind requiring an encrypted license key as the input into the web application doesn’t make sense. First and foremost, since the unencrypted license key is subsequently transmitted over the network, unencrypted, it can easily be captured with a tool like Wireshark. Secondly, the actual license key is verified on the vendor’s server so deducing how the license key is generated is impractical. Unfortunately for me however, this has introduced a minor obstacle in reaching the network activation function detailed in the previous section.

The astute reader may have already noticed an interesting detail about the RSA parameters. That public key looks awful small. Just barely over 256 bits. It appears our CTF challenge continues… (oh wait this is real software).

For those imposters in the infosec community that claim playing CTF doesn’t provide real world experience, I can confidently say you’ve never hunted real bugs. All too often I come across exploit chains that appear as if someone neatly setup each primitive for me to uncover. I personally don’t spend a ton of time attempting crypto challenges when playing CTFs, mostly because I’m not smart enough. Luckily for me, this example would fit nicely into the baby’s first category. With such a small public key, I should be able to find a CTF writeup that guides me through the process of breaking it down into its two prime numbers. A few searches later I found an article by Dennis Yurichev that demonstrates using a tool called CADO-NFS to do just that. After about 5 mins I was presented with the answer.

With the two prime numbers in hand, I attempted to reconstruct the private key using the script provided in Yurichev’s post. Unfortunately, the RSA crypto libraries in python didn’t play well with the provided primes. Namely because the block size rounded up to 257 bits and it complained about truncation. I also discovered that the server’s custom RSA implementation didn’t implement padding. To get around these issues I wrote code to perform the calculations manually rather than using a crypto library.

Copy to Clipboard

I used my script to generate an encrypted blob with the derived RSA private key and fired off a web request with a finished license key. Keep in mind, since I’ve already overwritten the hosts file, I only need to pass the decryption checks as the key will not be transmitted to the vendor’s server for verification. To my delight, it works!

Conclusion

My journey to developing a working exploit for CVE-2021-27198 finally comes to an end. What started as a simple exercise turned into quite an endeavor. For anyone interested, I’ve uploaded my exploit here.

❌
❌