An Unauthorized RCE in VMware vCenter - CVE-2021–22005
These days, my mind is in a mess, in a sea of things, sitting and thinking forever but can't come up with a reasonable name for this blog, without a name, it can't be, so let's just let it be concise!
A few days ago, some members of the group lamented that the CyberSec world is so peaceful these days: no drama or CVSS 9.8/10 at all,
Just finished talking, a few hours later, VMware released an advisory about some CVSS 9.8 bugs on vCenter.

When reading the name of this bug a little bit confused her well: "In a large product such that the holes also" arbitrary file upload "are you?"
With that big question in mind, I embarked on patch analysis to see where the bug was!
The setup debugging part, in the previous post ( https://testbnull.medium.com/a-quick-look-at-cve-2021-21985-vcenter-pre-auth-rce-9ecd459150a5 ) I also explained how to fix it. config file to be able to debug, you can refer back to the old guide and edit it a bit to be able to debug as you like!
The Patch
In VMware's advisory, they provide pretty detailed Workarounds,
It's so detailed that I could use the entire request to do PoC
Actually, it's just a joke, but for RCE, the story is not such a straight line.
My initial direction was to see the Workarounds script but soon realized this was the wrong direction. The short path does not mean it will reach the destination, the workaround script only deletes the endpoints related to the VMware-analytics service as follows:
- /analytics/telemetry/ph/api/hyper/send
- /analytics/ph/api/dataapp/agent
Based on the information from the workarounds, the first bug in this patch is immediately visible!
- BUG 1 — Arbitrary File Creation (Required CEIP enabled)
Based on the workaround, it can be seen that at the endpoints “ /analytics/telemetry/ph/api/hyper/send ” and “ /analytics/ph/api/dataapp/agent ” there is something dangerous, of which only The endpoint “ /analytics/telemetry ” is directly accessible due to the config of the RhttpProxy service:
Endpoint “/analytics/telemetry” được handle bởi class AsyncTelemetryController

When calling the /analytics/telemetry endpoint , a "TelemetryRequest" is generated from the parameters passed in via HTTP, including: collectorId , collectorInstanceId


Continue going into TelemetryService. processTelemetry() , where the TelemetryRequest just created above will be submitted to a queue and executed shortly after, at the TelemetryRequestProcessorRunnable. run():


After going into TelemetryLevelBasedTelemetryServiceWrapper . processTelemetry() , the server implements getTelemetryLevel() from the passed collectorId , collectorInstanceId parameters :

According to the current program flow, DefaultTelemetryLevelService.getTelemetryService() will continue to be called to get the TelemtryLevel. The code does the following:

Here we can easily see: if the CEIP feature is disabled, the program will always return the Telemetry Level as OFF!
Done leveling and return to TelemetryLevelBasedTelemetryServiceWrapper . processTelemetry(), we can see, if TelemetryLevel = OFF, the server will not continue to process the request and return.

Therefore, this bug has a special requirement to enable the CEIP feature to be able to continue!
Assuming our environment has CEIP enabled, the server continues into the LogTelemetryService branch . processTelemetry (), the processing code here simply logs the TelemetryRequest just passed in, with the content of the log being the body request:

The log file is stored at /var/log/vmware/analytics/prod/_c_i< instance name >.json



And because the filename contains both collectorId and collectorInstanceId, as soon as I saw this, I thought of the case where I could add the characters "../" to path traversal and create a file in another folder at will.
However, when I tried the first request, nothing happened 🤷♀️, the log was not created, and there was no error returned.
Continue debugging into the “this. _logger .info()” to see more clearly, F7, F8 for a while to reach the location with the fileName of the logger after being traversal path as follows:

In " / var / log / vmware / analytics / prod / " no folder named "_c_i", so that the path traversal " / var / log / vmware / analytics / prod / _c_i /../../ ../../../../../../../../tmp/test1111111.json” will also return not found.

This path traversal will only work when the previous folder also exists:

Fortunately, after a while I had miscellaneous fuzz, I was able to create a new folder on the server (actually, my initial request was a bit different, then I could comment with a new, more compact request):


With _c="" and _i="/<name>", the full path will now be:
“/var/log/vmware/analytics/prod/_c_i/11247.json”
When Logger calls RollingFileManager . createManager() , the server will check for the existence of parent folders, here " _c_i ", and since this folder does not exist, it will be created shortly.
With the folder " _c_i " created, the request path traversal to create the above arbitrary file can be done successfully:


However, that is not the end, the problem is still very difficult,
The content and path of the file can be modified arbitrarily, but the file name must have the extension ".json", cannot write web shell and execute!
A lot of people have found the above bug, but also stuck here and can't RCE, I am one of them,
About the method to be able to RCE with this bug, I would like to ask permission not to mention it here due to the commitment to the sharer, as well as the opening for readers to study further.
- BUG 2 — Arbitrary Web Shell Creation
Feeling that the workaround is not enough to find a perfect bug: RCE without strict conditions, I have to download the patch and diff,Some minor changes between the 2 patches are as follows:

Most of them are adding mechanisms to check collectorId, filename ... to avoid writing shell.
In it, there is a bug in the AsyncTelemetryController I mentioned above, the remaining bug must be in "DataAppAgentController"!
In the new release, the “/dataapp/agent” endpoint with action=collect has been completely removed:

Oh damn, there's still a very important issue I haven't mentioned yet:

In the declaration of rhttpproxy, there is no declaration that allows access to the endpoint “ /analytics/ph/api/dataapp/agent ”, currently this endpoint can only be accessed from local via port 15080,
" If that's the case, why do you have to patch so hard, no one can access it??? There must be something to bypass and gain access to… ” I thought to myself…
Going back to the advisory, among the bugs patched this time, there is one more bug that is CVE-2021-22017 — rhttpproxy bypass, and also reported by the author who reported CVE-2021-22005.

So sure, this author has found a way to bypass rhttpproxy and combined with the vulnerability at the endpoint “/dataapp/agent” to form an RCE-In-Onehit chain.
there's research on Envoy's bug, allowing to bypass URL filter stuff:
https://github.com/envoyproxy/envoy/security/advisories/GHSA-4987-27fx-x6cf sounds pretty reputable but when applied If you use it in this environment, it won't work.
I was swimming in desperation when suddenly I hit a familiar interface...
I was swimming in desperation when suddenly I hit a familiar interface...

Isn't that tomcat...
Come to think of it, from the beginning until now, I forgot about the classic tomcat case that is still used to bypass proxy filter: “ ..;/ ” divine.
And as expected, “..;/” is the key:
Come to think of it, from the beginning until now, I forgot about the classic tomcat case that is still used to bypass proxy filter: “ ..;/ ” divine.
And as expected, “..;/” is the key:

The problem of how to access the endpoint has been solved, now it's just how to write the file again.
There is also a bug that can write files directly to the server, but the content is quite limited as well as the filename will have the .properties extension, so I don't follow this bug, readers can see back and further develop this direction
I focused on the code that was removed in the new patch: dataapp collect and especially focused on the agent branch . collect()

The debugging process here is quite long and tricky, after a while of F7 and F8 constantly, and with a certain method of linking, I stopped at ResourceItemToJsonLdMapping . map() :

ResourceItemToJsonLdMapping. evaluateMappingExpression() then continue to call Velocity to evaluate() template:

With “this. _mappingCode ” is the controllable value:

Http Request:

Stack trace:

From here we can evaluate() an arbitrary template, but that's not enough to RCE with common paylaks:

The new Velocity version has some blacklists to block calls to methods of class “java.lang.Class”:

Therefore, it is not possible to directly call Class.forName() or a certain method of the Class to execute.
I have referenced some methods of @pwntester ( https://i.blackhat.com/USA-20/Wednesday/us-20-Munoz-Room-For-Escape-Scribbling-Outside-The-Lines-Of- Template-Security.pdf ), roughly where abuse of loading ClassLoader instead of Class.
However, in the current context of vCenter there are no such appropriate variables, only some vars as follows:

Last hope is placed in these 26 available contexts,
And luckily, one of them has worked and can be used to write arbitrary files. That is the “ GLOBAL-logger ”:

Based on each picture described above, readers can already imagine what they will do with it .
Here are the steps to use $GLOBAL-logger to write shell:
- Set the log path to an arbitrary file,
- Write web shell via logging
- Close the log file and return the old value of the log file name

For the PoC to run smoothly, there are a few more steps needed, but in my opinion that is enough for readers to understand and create a PoC for themselves.
Due to time and memory limitations, if readers really want to learn, they should reset the lab and perform to better understand the steps they skipped, so it will be more memorable than riding a horse to see flowers. !
Good luck!
PoC video: https://www.youtube.com/watch?v=WVJ8RDR7Xzs
PoC script: https://gist.github.com/testanull/c2f6fd061c496ea90ddee151d6738d2e
Post a Comment