Impersonation - The elephant in the room?

Product: PowerShell Universal
Version: 3.7.10

I would like to bump this topic as I’ve seen a couple posts about it but surprisingly, not as much as I think there should be. I’m hoping I have not searched through all the forums or re-read through the various PSU documentations regarding authentication and secrets, enough times and that a solution exists.

My Scenario: Engineer team (me) needs to create PS tools for the ServiceDesk (SD) team. Various SD users have separate elevated accounts e.g. cloud only Exchange Administrator, to perform day to day tasks such as managing mailboxes, distro groups, ect. That same team also has separate on-prem elevated accounts with delegated rights to manage on-prem AD objects.

My Configuration: The way we have PSU configured is that it’s running as a service and the service is running under a domain service account. Authentication is done via OIDC.

Issue 1: ZERO impersonation policy. We can’t grant the domain service account that runs PSU, elevated access both on-prem or in our 365 tenant so that my or the service desk team can run the tools as that service account. Any company that is required to meet Sox compliance hopefully understands this.

Issue 2: Login method. As part of our requirement to be Sox Compliant, our current application configuration is required to be configured to use OIDC authentication. This authentication method does not capture credentials so there is no pscredential object to take advantage of.

Issue 3: Secret management. This is the part where I’m hoping to be corrected on how secrets are used. Again, I’ve read through the secrets documentation and even watched Adam’s demo video. From what I recall, there was a point in the video where Adam said the secrets were stored in the logged in user’s context (depending on which storing method you chose). However, this is not the case with my testing. I created three different secrets each using the three currently available storage methods as my test SD account. All three secrets were available to my both my personal admin account and other test SD account. These results make sense because, if the PSU service is running as the service account, then the service account is responsible for storing the secret. For the option of Windows Credential Manager and PowerShell SecretStore, this is done under the service account’s context and so, anyone with access to that service account can access those secrets (which is all users logged into and running the PSU web app). Furthermore, I am also able to take the PowerShell SecretStore vault password out of the settings.json file and run the PowerShell command Get-Secret to see all those secrets, assuming I’m logged into that server as that domain service account (which all my engineering team will have the ability to do). To Summarize issue # 3, me nor my team should have access to other user’s secrets and other teams to ours.

Issue 4: No current workaround.

Forms Method: Unfortunately, this recommendation proposed by few is quickly thrown out the window as our company requires the solution to be Sox compliant. While the forms login solution would allow me to capture the username and password to create a pscredential object, this is not secure and doesn’t meet the OIDC requirement.

Impersonation allowed if logged: I’ve also seen a few say that impersonation is acceptable by them since you can log events with information about the user who ran the script/job and then cross reference that date/time with an event that the service account created in let’s say Exchange. I assume said users’ companies do not require Sox compliance. This is not an acceptable solution as infosec nor the auditors will be spending hours trying to cross refence two or more logs to see who did what when. Any solution to correctly those two separate events into a single pane of glass would take some special development.

Again, I’m hoping that I’ve overlooked a workaround or do not properly understand how this solution should be used and that someone will come along and set me straight.

1 Like

So, a few things here…

For Dashboards, you can use Get-Credential to have them put in a username and password. Link

For jobs, you can prompt for a credential parameter. link

For Dashboards, I’m currently implementing a solution that requires the Helpdesk to type in their normal AD username/password in order to run commands against AD. This is so all the logs on the DC show who has made the change and are automatically uploaded/audited by Azure Sentinel.

This means to get into the front door (login) it can use OIDC and thus MFA. The Azure side might require some more thinking about.

More on that, in Azure have you explored the possibility of using their standard accounts and leverage Azure PIM Eligible assignments?

Thanks for the suggestions.

For the get-credential option, I think this might be where I need to head. I did see the waiting for feedback/respond to feedback feature that jobs have which may coincide with the get-credential cmd you’re talking about. I haven’t tested out that solution yet.

For the credential parameter param([PSCredential]$Credential), I did see the documentation about that and tested it. As far as running the script as a form within the web app, that just creates a single field just like the “run as” field and lets them choose from stored secrets (see issue #3) so that didn’t seem like an option.

For consolidating all their elevated accounts (even cloud only) into their one and only domain/cloud synced account then using a PIM solution, I would love nothing more and believe that’s just as secure. However, at the moment that just isn’t an option. We’re a pretty large company and that conversation would realistically end up being about 6+ months of deliberation between various entities just to discuss items such as security acceptance, cost for a PIM solution, implementation of the solution, etc.

Okay so I think this is the best solution I have so far. Thanks @Jori for the recommendation. Now it’s important to note that there are limitations of the “respond to feedback” job feature as well as a smart way and not so smart way to implement credential gathering via this method.

Script 1 - New-PSCredential.ps1

    # NOTE: At no point is the provided username or password stored into a variable/memory - it is directly piped into an argument for the ps credential object
    [pscredential]$Credential = New-Object System.Management.Automation.PSCredential ("$(Read-Host -Prompt "Username")", $(ConvertTo-SecureString -String "$(Read-Host -Prompt "Password")" -AsPlainText -Force))
    return $Credential

Script 2 - Demo_FormScript.ps1

[pscredential]$Credential = .\New-PSCredential.ps1
Do-Something -Credential $Credential

Thoughts on security
This method is probably the most secure in the sense that all code obtaining user input and creating the pscredential object is stored in a single file and not explicitly written across various scripts. As commented, the user input username and password is directly piped in and not necessarily stored in a variable or memory (for all you tin foil hat peeps out there). The downside of this (and I would like to get @adam to maybe consider as a future feature), is that you can’t prompt for a secure string. When you prompt for the user’s password and then they click the blue respond to feedback button, their password will be typed into the text field in plain text for anyone standing over their shoulder to see or possibly other running processes to capture/cache? Also, depending on how strict your InfoSec security team is, they may not approve of this method of credential gathering. I mean, even the simple PSScriptAnalyzer flags the ConvertTo-SecureString -AsPlainText as a bad practice.

Why not Get-Credential?
Running this command within a script does not work with the “respond to feedback” job feature. The experience is that a single text box pops up called credential. You can only enter a single string of text and that obviously doesn’t translate to a pscredential object with a username and password property. In fact, you even get an error when you submit your response.

Respond to feedback quirk
This is probably talked about elsewhere, but I found that when you have multiple prompts or feedback required by the user in a job, after you submit a response, the next response doesn’t properly show when you click on the blue “respond to feedback” button - you must refresh your page. example, if you’re prompted for a username and submit something, you must refresh your page before hitting the blue button again to respond to a password prompt. If you do not, you’ll just see the previous prompt (username) again.

It seems like in your situation you should not allow for jobs to be ran from the admin interface then? Create a dashboard that can invoke the job and gather credentials…

Note, do not use $Credential as the parameter, or anything like $Cred. Make it distinct (thus why I chose $usecred)

Result:

so some years ago we built a solution for managing identities which required SOX compliance and to do this properly requires a LOT of work. In the end we built our own solution which was tailored to our requirements, the source code as audited and it took forever to make changes and get them approved.

I work for another organisation know and we use PSU to manage various things, but I would not be 100% sure it would meet those compliance requirements.

I would contact @adam directly and discuss your requirements with him.

Isn’t the $User variable or $Userinfo variable populated using OIDC?

Good question and I get where your head is currently at. I first went down the route of impersonation but capturing user information in logs for audit purposes. But like I said in my previous comment, we can’t really impersonate so I abandoned this option for now.

To answer your question though, user information is available in the job but seemingly, only while running the job. That means every script you write must include code to capture the information and pass it onto something else. I didn’t like the idea of myself or another engineer having to actively include that code with every script to ensure logging was captured correctly. It just introduces the human aspect which leaves room for error. If I or another person writing the script forgets to write that code or does it wrong, then logs don’t get captured correctly. Sure, we could do CI pipeline to ensure correct coding is followed but that’s starting to get crazy. I prefer a passive logging mechanism.

Once again, looks like your recommendation might be the route I need to take. I did anticipate that all scripts would eventually be the back end for every front end UI (dashboard) but I was hoping that I’d have the option to let other users run the script from the admin portal directly for some scenarios.

When using Get-Credential, I’m getting this white single field credential box before getting the traditional username/password credential popup. Any idea why? I’m able to dismiss this w/o issue but I don’t really want to see this.


-OnSubmit {
    $ElvCreds = Get-Credential
}

I’m not seeing that , but in my implementation I am also using the -Title and -Message parameter.