-
Notifications
You must be signed in to change notification settings - Fork 14.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CVE-2020-8956] Pulse Secure Connect Client Credentials Gatherer #14314
Conversation
A known limitation is the inability to extract the username linked to saved credentials. One potential avenue would be to hint the module user that it might just be the currently logged in user's username. |
It doesn't look like this module works after the fix is in place yet in your blog post you were still able to get the password if elevated. Is there a way to include this contingency into the module? (Awesome work btw!) |
Thanks :) There's definitely a way to include that, but I need to check each client versions to make sure they use the same fixed optional entropy value (the |
I just figured I'm only checking the running version against the 9.1 branch and not the 9.0 branch, this is something I'll need to add before it gets merged into main. I'll edit the PR with a to-do list. |
…ile subsequent ones will be stored in connstore.tmp. Note that even if it ends with '.tmp', this file is not temporary. This commit provides support for deployments with multiple VPN connections.
…registy by previous version and dumping when in elevated mode.
…vated user. Lots of cleanup to do.
…fixed the data structure to reflect that.
Encodings Conversion DanceI got a running version that supports extraction and decryption of credentials when executing in elevated mode (SYSTEM). My love/hate relationship with Ruby is not getting better. If someone with experience on Ruby encodings could take a look at the insane conversion dance I have to perform on registry data to get it to work with DPAPI, that would be great. There must be a better way. metasploit-framework/modules/post/windows/gather/credentials/pulse_secure.rb Lines 158 to 167 in d8f5dc5
Some notes on Pulse Secure Config FilesPulse Secure Connect client saves IVE definitions in three different files:
These files differ from build to build. Some builds use Usernames ExtractionWhen running in elevated mode, we can read the following files:
The files are only readable by SYSTEM and hold the VPN username, along with other details. If we're executing in elevated mode we extract the usernames from these files. Fun FactWhen uninstalling or upgrading Pulse Secure Connect client, the software does not wipe out entries in the registry it had created. This means we can still recover previously saved passwords, even if the client is running a fixed version. This is now supported by the module. |
I'll do a refactoring pass on the module this week-end to make it cleaner and idiomatic. |
…y using print_good and print_warning.
… exception if it's not.
Checked against all versions up to 9.1r9 while running as normal user and system. I'm ready for the actual code review @gwillcox-r7 :) |
@qkaiser Sorry for the delay on this was on a 2 week rotation of research and now back on the 2 week rotation of PR review. I've added this to my list and I'll make sure to get a review of this code back to you by EOD Monday or latest early Tuesday. If by some small chance I don't, feel free to prod me until I do, as you ideally should have gotten a review by now. |
@qkaiser Made a quick update to your PR description to reflect the fact that 9.0.5 is not a vulnerable copy of the software according to your article you linked in the references. |
@qkaiser Just a quick heads up but the review will contain a lot of changes. Please wait until I have committed some of the changes and given the go ahead, as otherwise it may look a little overwhelming; some of the changes are easy to make and are more in there for my reference (unfortunately GitHub doesn't have a good way to assign fixes to one person or another to help me make it clear to you which I need some more info on and which are things that I will fix myself). That being said hopefully the extra comments will allow you to see what sort of things I am fixing if you are curious. If not or if you don't have time then feel free to ignore these extra comments; I'll mark them as resolved so you know that you only need to focus on the ones marked as unresolved where I may need some more info or want to double check things with you. |
@qkaiser Left some more comments that should resolve the remaining few issues. Let me know if you have any remaining questions on them. |
@gwillcox-r7 just answered the remaining comments. You can download the Pulse Secure Connect server virtual machine from https://www.pulsesecure.net/trynow/pulse-connect-secure/ and use it to test the module if you need to. There is a limitation on the amount of users you can create but it is sufficient to establish sessions and test the module. That's what I used throughout my testing. |
Co-authored-by: Grant Willcox <63261883+gwillcox-r7@users.noreply.github.com>
@qkaiser Thanks, going to check this code out and try set up a machine to test this against so long. |
Quick link for anyone trying to get this module tested since it seems the old versions of the Pulse Secure Connect Client are no longer available on the website anymore (and whilst I did try some methods to get the older versions from the official website, that only goes back a very limited number of versions): https://www.jwu.edu/files/utility/ps-pulse-win-9.1r3.0-b1313-64bitinstaller.msi |
Output from test running as normal user against the most recent version of the client:
And as the SYSTEM user:
|
Hmm looks like your code may be opening a handle to Edit: Looks like this also applies to at least three other files in addition to the one mentioned above. Its probably worth me reviewing the code again and checking where these handles are being opened and closed as right now it seems like you may have just done a few calls to open files and then forgot to call the corresponding close function to close the handle once you were done with those files. Edit 2: Well its still an issue but its not the only thing causing the uninstall to fail. Still not sure what is causing it right now. |
Hmm something odd is going on with the older version. Tried reverting my machine due to the new version not uninstalling properly but this is what I am getting now:
Then removed the saved connection, but got the same results:
Finally got it after creating a new profile and clicking the save credentials button:
Might be a good idea to print out a warning if no credentials are gathered as this was a bit of a confusing experience: to the end user this looks like a module error rather than the fact that it couldn't find any credential file. |
Two things I need to look at so far:
|
…r in get_username.
…ework into enum_pulsesecure
Oh man good catch on the file handle issue !! The uninstall issue happened to me but I never thought of leftover file handles. I fixed it in 4f947ac. This is what it looks like prior to the fix:
With the fix:
Side note: I just skimmed through some modules and there are lots of post exploitation modules that do not properly close files, leaving dangling file handles. |
The module now prints out a status message if it did not find credentials (see e8ea9e5). |
Changes look good @qkaiser, will retest this again today and see if I can't land it 👍 |
Running like a charm now 🥳
Removed the credentials at this point and tried again:
And finally uninstalled the product, which had no errors as there were no extra file handles, and we got the right error message that the product is not installed on the target system:
This should be good to land now, just got to wait for Travis to finish its tests which may take a while (unfortunately it has been getting very slow as of late). Should hopefully be done later this evening or tonight though. Thanks for your contribution and patience @qkaiser! |
Release NotesNew module |
Okay so did run into one weird scenario with this which is that if we are running as The more serious issue though is this:
It seems that at least in some cases, running this as SYSTEM will result in the module not outputting any credentials. This was tested against PulseSecure Connect Client 9.1 R3 |
Looks also like running this as SYSTEM on the latest version produces similar output:
|
For version 9.1.3 this is normal that the module doesn't report credentials. Windows Data Protection API provides two different scopes when encrypting data:
Pulse Connect Secure use the CurrentUser context, which means calling CryptUnprotectData from another user context will not work. So if you want to retrieve the save credentials of user 'jdoe', you have to execute as 'jdoe' without elevation. Technically, it could be possible for a process executing with SYSTEM privileges to decrypt the content anyway given that it will have access to any user password hash, master key, and SID. Re-implementing DPAPI key derivation and decryption in Ruby is clearly out of scope of this module though, especially when tools provide that capability if you give them the right files. "Reversing dpapi and stealing windows secrets offline" is an interesting research on the subject, although quite old now. Regarding version 9.1.9.4983 I'll give it a try tomorrow. The failure looks weird to me given that you successfully executed the module against that exact version earlier and the only changes since then were commits 4f947ac and e8ea9e5, which simply added a print statement and proper closing of files. |
This is why I'm not testing with SYSTEM privileges on versions prior to the fix that moved the DPAPI call to a service running as SYSTEM. See the table at #14314 (comment). |
@qkaiser thanks for the explanation, that makes a lot of sense. I'll focus just on trying to fix the issues with the latest version then. EDIT: Alright confirmed the code from commit 585bc99 works fine, so something must have changed after then to make it not work. Will investigate some more... EDIT #2: Okay the latest version of the code on this branch is working as expected when running as system....the plot thickens... |
Huh so I tried this again and it seems to work fine. Its quite likely that there was some configuration issue that was temporary. As this is not a standard setup issue I'm going to leave this as is since it was likely a temporary local issue. I'm not sure what caused it to be honest, but attempting a clean up after installing the old version over the new one and removing some of the old registry keys seems to have helped. |
This post-exploitation module takes advantage of how Pulse Secure Connect clients save user credentials in order to recover them.
TL;DR; Pulse Secure Connect versions up to 9.1R4 and 9.0R5 included rely on Windows DPAPI with a known IV to encrypt credentials before saving them to the registry. This means a malicious process can recover the user's credentials (if they chose to save them).
More details can be found on the accompanying blog post at https://quentinkaiser.be/reversing/2020/10/27/pule-secure-credentials/.
Vulnerable Application
Pulse Secure Connect VPN Client for Windows 9.1.x < 9.1R4 and 9.0.x < 9.0R5.
An end-to-end setup with working Juniper Pulse Secure VPN server, Pulse Secure client on Microsoft Windows, and valid credentials are required for Pulse Secure client to save credentials locally and therefore test this.
Verification Steps
To verify that it works, you should obtain a Meterpreter shell on a Windows host running Pulse Secure client version 9.1.x prior to 9.1R4 or 9.0.x prior to 9.0R5. That client should have a saved VPN connection where the user explicitly ticked the 'save password' box at some point in time.
use post/windows/gather/credentials/pulse_secure
Example Run
Normal mode
Output:
Scenarios
Run on all sessions
If you wish to run the post against all sessions from framework, here is how:
msf > resource path-to-resource-script
References
TODO