One Click to Compromise -- Fun With ClickOnce Deployment Manifests
ClickOnce Deployment Manifests are a relatively unknown way to both get an initial payload into an environment as well as remotely retrieve NTLM challenge-response hashes over HTTP. Hash retrieval occurs on initial file open (before any warnings pop) meaning that even if the user opts to close out on the warning, we still have a hash we can attempt to crack.
IE also has some interesting interactions with these files and will automatically execute a manifest upon browsing to it – even on an internet-zoned site. This means that on a hyperlink click we’ll be getting an NTLMv2 remotely over HTTP at a minimum (if the remote user is configured with IE as their default browser) and can get a shell with one more click. This also gives us several different options for execution, as the deployment manifest can be sent directly as an attachment, embedded into an HTML and hosted, or hosted on a webserver and linked directly to via hyperlink. For the POC|GTFO crowd, here’s demos of two different methods that could be used:
Run via IE + hyperlink click:
Run via web delivery:
Background and ClickOnce Execution Process
A few months ago I was on an assessment for a client that had done a pretty solid job of locking down the ‘standard’ types of payloads we would use to try and get an initial foothold in their environment (HTA’s, all types of WScript payloads, macros / DDE, etc.). In my efforts to find payloads that would get us execution on their systems, I happened to stumble across the “.application” file extension, which after a quick google search got me to a Microsoft article on deploying applications to systems over a corporate network with ClickOnce Deployment Manifests. This seemed like an interesting lead, so I spent a bit of time digging into them further, and ended up finding quite a bit more good info about them.
ClickOnce was designed to be used as a deployment technology in enterprise environments and allows for the creation of self-updating C# or C++ applications that can be installed and ran with minimal user interaction (https://docs.microsoft.com/en-us/visualstudio/deployment/clickonce-security-and-deployment?view=vs-2019). The deployment process consists of several steps and various manifest files, which are really just XML's. First, a deployment manifest (.application) file is somehow executed by the end-user (file click, browse via IE, etc.). Upon this first open of the deployment manifest, Windows loads the ClickOnce runtime (dfsvc.exe), which parses this initial file and handles all additional calls out to grab other required files. Applications spawned via ClickOnce deployment are children of the ClickOnce process:
The first file dfsvc pulls in is the remotely hosted version of the same deployment manifest that it is currently parsing, which is done in order to determine if updates to the deployment have occurred and need to be processed. The version numbers are compared, and if the most current version is not installed, dfsvc will use the relative pathname of the application manifest (relative to the included path to the deployment manifest) to next request this second manifest file:
This second file (application manifest / .manifest) is then parsed by the runtime. It contains more configuration options than the initial manifest, as well as relative paths to the files that need to be deployed to the remote system. The entire deployment process took me a bit of time to get my head around, but it can be summed up with the following example:
1. Initial call to the server to grab the deployment manifest, this is what kicks off the ClickOnce runtime on the remote system
2. The now-running ClickOnce runtime connects back to our system, requesting a server-side copy of the deployment manifest its currently running to check for version updates. It determines that although there are no deltas, there is no local installation of this manifest on the remote host, so it proceeds to request the application manifest.
3. The runtime next requests the application manifest, which contains paths to the files to be installed on the remote system.
4. In our simple example, the only file in our deployment is a shellcode injector, which is requested by the remote system, downloaded, and ran, prompting the user if they want to execute or not.
Testing and Use Cases
After getting a better idea of the above process, I set about creating a super simple deployment manifest to test the functionality out; using the guide here was quite helpful: https://docs.microsoft.com/en-us/visualstudio/deployment/walkthrough-manually-deploying-a-clickonce-application?view=vs-2019. I uploaded the generated files to an Apache server on an AWS box, and found that I was successfully able to execute the .Net assembly I had included in the project after clicking through a security warning. Having success here, my next thought immediately went to authentication – since we’re forcing a remote system to connect to a server of our choosing, I was curious if it would actually go through and complete an NTLM authentication. I quickly fired up Responder on the remote system, attempted to re-download the manifest, and saw the following immediately upon opening the file, even before accepting the execution warning:
At this point I was most definitely interested and started doing a bit more research to see if anyone had done anything from a payload perspective with these before. I found a page on LOLBAS which referenced a talk that @SubTee gave that had a slide on them from back in 2015, but outside of that there really wasn’t much (https://lolbas-project.github.io/lolbas/Binaries/Dfsvc/). However, I’m still always amazed at how bad I am at googling things, so if there is more info out there on this type of payload, my bad.
Edit: yep, I'm still bad at googling stuff. @0xF4B0 gave a talk on ClickOnce last year at BlackHat, his talk + whitepaper cover some different use-cases and setup methods for deployment manifests. I would highly recommend giving it a read: https://www.blackhat.com/us-19/briefings/schedule/index.html#clickonce-and-youre-in---when-appref-ms-abuse-is-operating-as-intended-15375
To ensure this wasn’t just something tied to the systems I was testing with being on the same local network, I next created a new manifest that pointed to an externally-facing IP, and re-sent the application manifest file to myself across two different email providers. Upon receiving the attachment and opening I was greeted with a very standard ‘open-save-cancel’ dialog box, and upon clicking it I once again immediately got an NTLMv2 challenge-response hash back on my remote system.
If I wanted to continue past just getting a hash here, in order to get to the point I would actually be able to execute code on the remote system, one additional warning message must be clicked through as well:
Note: having a valid code signing cert makes this error look less scary
When the assembly is ran, it is downloaded to disk (located in the \Users\*user*\AppData\Local\ Apps\2.0\*randomChars* folder). For that reason I would recommend keeping your stage-0 pretty small & keeping shellcode or anything risky in additional files downloaded as a part of your manifest that are referenced from your assembly, no differently than keeping any other initial payload sent via macro / attachment / web delivery compact. I would probably also migrate off any processes ran out of here immediately and then remove the folder structure unless you want to use it in the future as a persistence mechanism.
While it’s great and all that we can execute basically any .net code we want this way, what I found most interesting about this filetype was the ‘no warning’ remote hash disclosure over HTTP. This grants us the ability to still get ‘some’ value out of a click, even if actual execution fails or is cancelled by the user. However, there really wasn’t a great solution that I was able to find that handled both delivery of multiple files, as well as collection of hashes.
After a weekend of messing around with a variety of types of webservers and the impacket libraries, I finally settled on modifying some of the functionality that already existed within the Responder project (https://github.com/lgandx/responder) to allow for both hash retrieval and file delivery. I’ve got more details on these changes down in the ‘Building ClickOnce Deployment Manifests’ section below, but really what I put together was really intended only as a PoC and not something that I recommend copying without some pretty substantial improvements.
With this setup in place, I tossed the initial application manifest payload (.application) into an html, which my hybrid Responder / web server hosted as well. Testing this whole execution chain yielded the following from a server-side perspective:
Up until this point I had been testing my payloads almost exclusively by tossing a deployment manifest onto the desktop of one of my VM’s and then double-clicking it for the initial execution vector. This worked well from a speed-of-testing perspective, but wasn’t a great representation of a ‘realistic’ attack vector (psshhh…). As I started working through various deployment methods I turned off my Responder server and was once again testing with a default Apache setup. During this time I found that if I navigated directly to my web-hosted deployment manifest using IE it would automatically execute, without even prompting me with an open/save dialog. This behavior was quite interesting, and after comparing some traffic captures in Wireshark I made some additional changes to my HTTP server that allowed for NTLM capture remotely over HTTP while also delivering a payload transparently, allowing us to get down to a true ‘One Click’ deployment:
As I kept digging into why the ClickOnce runtime was being automatically loaded by IE, I found that this was actually the expected / default behavior when opening manifest deployment files:
At this point I spent more time looking at some of the specific features of different configuration options within the manifests themselves, but from an actual delivery perspective there wasn’t really anything new beyond this. Below, I’ve included some of the other unique features of the manifests that have potential applications, as well as a step-by-step on creating deployment manifests if you’re interested in trying out:
Some organizations may have perimeter controls in place blocking inbound downloads of .exe / .dll files from unstrusted sources. ClickOnce manifests give us a way to get around some of these by including the following option in the deployment options tab of the deployment manifest build process:
With this option checked you would just need to toss on an extra ‘.deploy’ to the end of every file you’re deploying (ex. shellcodeInjector.exe.deploy). The ClickOnce runtime will download the .deploy files over the wire, and then strip off the extra extension on the remote system before saving and running.
Usage as a Persistence Mechanism
The last cool thing I found about manifests is their potential usage as a persistence mechanism. After initial install of a manifest, further runs of the already-installed manifest no longer prompt the user with a security warning, because no new files are downloaded if the ClickOnce runtime performs its version check and finds no updates on the server-side manifest. Paired with the previously noted Internet Explorer interactions, this can allow for a shell callback by simply running IE and pointing it to an externally hosted .application file:
While at the end of the day there is still a .exe on disk that’s being called by this, and you could just as easily set up a persistence mechanism to call it directly, this was a bit interesting to me as it allows us to set up a mechanism that instead calls IE and gives the remotely hosted manifest as an arg.
Building ClickOnce Deployment Manifests
Requirements: Visual Studio (I’m using VS 2019 in this example)
Note: This walkthrough uses a tool installed with Visual Studio called MAGE, but everything covered here can also be done through Visual Studio directly as well.
Pre-step: In your project in Visual Studio, ensure you go into the 'Application' tab of the project properties, and under the "Manifest" drop-down, select "Create application without a manifest". If this is not selected you will not be able to deploy your application using ClickOnce and the below steps.
To start, open up the developer command prompt for VS 2019. From here type in ‘mage’. This loads the Manifest Generation and Editing tool GUI (MAGE)
We’ll be working backwards here, first creating an application manifest (the second stage in the delivery chain) before creating our initial deployment manifest. To create this manifest, click the left-most button directly under the file menu. After creating the application manifest, begin to configure it by selecting the appropriate processor architecture for your assembly and giving your manifest a name + version.
On the files menu on the left-hand side, select a folder containing an assembly that is to be configured as a ClickOnce application. This can be any type of assembly with a valid entry point, and can be a single file or multiple files of various types (ex. a .exe and a .txt file containing the shellcode to execute, etc.). This can allow you to do some pretty neat stuff, for example to evade detections I built a payload that had functions in different files and referenced base64-encoded shellcode I had stored as a string in a text document that was also downloaded by the manifest.
If you don’t have a specific folder set up for this deployment manifest yet, I would do so now in order to keep track of the variety of files you’ll be creating. I recommend creating a subfolder for your application manifest in this new folder, as when linking it to your deployment manifest, MAGE will always take the current folder the application manifest is in as a part of the file path (more on this later).
Everything else should be good to leave at default values. When done, save the application manifest. Either sign the code with a valid code signing cert or have MAGE create a key to sign it with
Once the application manifest has been created and saved, move on to creating a deployment manifest by clicking the ‘Create a new deployment manifest’ button immediately next to the one that was clicked to create the application manifest. Configure this in the same manner, changing the name and processor architecture.
Set a publisher and product in the Description tab (these can be anything), and then in the Deployment Options tab select ‘online only’ for application type, and check the ‘include start location’ box, which allows you to enter the address you’ll be hosting your deployment manifest on:
Finally, in the ‘Application Reference’ tab, select the application manifest that you just created. This will link the application manifest to the deployment manifest. Remember that paths between the two files are relative, so if your application manifest is in a folder called “220.127.116.11” as in the example below, this folder hierarchy needs to be replicated (or simulated) server-side when deploying (see example below). When selecting the application manifest, MAGE will always include the name of the current folder it’s located in but will not take any portion of the path beyond that.
Once this is done, simply sign and save it– using your previously generated cert / valid code signing cert.
At this point, you should now have both a completed application manifest and deployment manifest. Upload them to the web / file server you indicated when building the deployment manifest, and you should be good to start delivering.
On the webserver side of things, it really depends what your end goal is – hash interception, payload delivery, or both. If you’re looking to do both, I really suggest doing something more scalable than what I threw together, but if you’re looking to test on your own network, here are roughly the changes I made to responder:
Most of the changes were made to the PacketSequence function in the HTTP.py library, and primarily consisted of adding some additional rules to return files based on name. Some of these (.application, .exe, optional .html) are done pre-NTLM auth, with the manifest being done after NTLM auth in order to allow us to grab the hash. And yep my search terms are hardcoded values and are really janky :)
On top of these changes, I modified the ServeExeFile and ServeHtmlFile classes in packets.py to remove unnecessary headers that broke things (X-CCC and X-CID) and changed the html mimetype to “application/x-ms-application”. Finally, I updated the conf file to include the following settings:
Note that the .html serving thing is completely optional, this was just done so that I could host a web page that contained an embedded deployment manifest file in it.
Random Other Stuff
· There are some other options within the manifest that you can manually modify that can allow you to attempt to install / run as an admin. I tried messing around with these but all my attempts ended in execution failures.
I’ve had varying degrees of luck sending the .application as a direct attachment. I’ve had pretty good luck getting it past corporate mail filters into inboxes, but the default O365 outlook filter appears to block it as untrusted, so YMMV.
· The documentation for these files is super disjointed and kind of all over the place. I’m pretty confident that this is only scratching the surface of what can be done through these files, but still figured it was decent enough to warrant a blog post. Hopefully it gives some inspiration to go find some additional functionality within this framework.
· Disallow .application and .deploy files at the perimeter
· AppLocker blocks ClickOnce assemblies in its default config as files are ran out of Appdata/Local, AWL can work as a solution.
· Disable the ClickOnce trust prompt via registry key as outlined here (change default behavior for the internet zone): https://docs.microsoft.com/en-us/visualstudio/deployment/how-to-configure-the-clickonce-trust-prompt-behavior?view=vs-2019 Note, this only partially mitigates the risk – the ability to run the manifest is disabled, but hash retrieval is still possible: