2

Let's say an application is already running with elevated privileges. Is it possible for this application to show a UAC prompt and get its result (successfully confirmed or cancelled)?

Background story: I have an application that requires Administrator privileges but runs in a restricted user account, so an UAC prompt is shown at its start, the user enters Administrator credentials to confirm it and everything works fine. However, for some critical actions I'd like to verify that the current user is (still) allowed to do that.

For example, the original user left the workstation without locking his Windows account (yes, the world's not perfect...) and another user open that already running application and accesses some sensitive settings. You can compare this to an online-shop, where an already logged in user has to provide his credentials again if he wants to change his delivery address.

I understand that I could create a custom prompt, ask for admin account credentials and check if they're valid, but I don't want to touch those credentials at all. Neither do I want to introduce additional application-specific credentials. The UAC prompt would be a nice and native solution to re-verify the user has admin privileges.

Basically something like this:

if VerifyAdminWithUacPrompt then
begin
  //critical stuff
end;

A Delphi example would be perfect, but I'm also happy about general ideas how to accomplish this.

CodeX
  • 717
  • 7
  • 23
  • AFAIK you cannot force another UAC prompt for an already elevated process. I'm afraid you need to do what you suggest in the first part of the last paragraph of your question. But interesting question anyway. – Jabberwocky Mar 15 '19 at 14:58
  • 1
    However this SO post might be interesting: https://stackoverflow.com/a/31844696/898348, especially [this link](https://learn.microsoft.com/fr-ch/windows/desktop/api/wincred/nf-wincred-creduipromptforcredentialsw) or also [this](https://learn.microsoft.com/fr-ch/windows/desktop/SecBP/asking-the-user-for-credentials) – Jabberwocky Mar 15 '19 at 15:04
  • 3
    One way might be to put the implementation of your sensitive elements into a separate process called by your (non elevated) main process, and for that second process to exit when it is done. That way the UAC screen is delayed until the elevated status is required. – Dsm Mar 15 '19 at 15:09
  • @Dsm apparently he wants to have the UAC prompt already upon startup of the program and then later a second time under certain conditions – Jabberwocky Mar 15 '19 at 15:17
  • 2
    You'd need to create a non elevated process, and then have it start the other process – David Heffernan Mar 15 '19 at 15:27
  • The sensitive stuff is part of the application. So "start the other process" would mean to run the application a second time. I don't want to quit the first instance and having two instances running at the same time is confusing. – CodeX Mar 15 '19 at 15:36
  • Creating a non-elevated process that tries to run some other elevated process, immediately closes it, returns the result and closes itself might be a way. A rather complicated way. Since I don't actually want to elevate anything, I really hope someone has an idea how to just show the UAC prompt and get its result. – CodeX Mar 15 '19 at 15:40
  • We are not saying run the same process twice, we are saying separate your functionality into two (different) processes, one of which runs at elevated level and one not. If you are not prepared to do that then you need to handle your own security internally, which means creating your own screen. – Dsm Mar 15 '19 at 17:27
  • @Dsm You and David suggest different things. David suggests to run a non-elevated process from my elevated main process to start the other process. You however suggest to have a non-elevated main process in the first place. The application is an administrative tool where almost all actions require elevated privileges. It doesn't makes sense to run it non-elevated and show a UAC prompt for every single action, therefore the whole application is running elevated. – CodeX Mar 15 '19 at 21:15
  • 1
    @CodeX in that case, you will have to make your main process run elevated as you want, but then have it [spawn a non-elevated process when needed](https://devblogs.microsoft.com/oldnewthing/?p=2643). That unelevated process can then spawn a new elevated process and exit. UAC will prompt the user before letting that final process run, and if successful then the process can do the actual admin work, or use an IPC mechanism to send a signal back to your main process so it can perform the admin work. – Remy Lebeau Mar 15 '19 at 21:29
  • @CodeX besides, even if you could invoke the UAC prompt directly and act on its result, that would just be a target for reverse engineers to simply bypass the prompt and gain permission to perform the work unhindered. So, you do need a multi-stage approach to ensure the validation you want can't be skipped. – Remy Lebeau Mar 15 '19 at 21:53
  • What's the security difference compared to calling CredUIPromptForCredentials and verifying the credentials yourself? If the UAC prompt can be bypassed, so can this be bypassed as well. If the executable or the memory of an elevated process is modified, we're in a completely different story. – CodeX Mar 15 '19 at 22:23
  • Remy, your suggestion is basically what I've written in my second comment: Elevated main process (1) calls non-elevated process (2) that calls some other process (3) requesting elevated privileges. (2) can return the result as an exit code, so (1) can do its thing. (3) is basically irrelevant, right? So (3) could be cmd.exe so it starts fast and preferably exits itself automatically? – CodeX Mar 15 '19 at 22:34

1 Answers1

2

Your app does not need to invoke a new UAC prompt, since UAC is already running your app elevated. The app just needs to ask the user for credentials. Windows has APIs for that very purpose: CredUIPromptForCredentials() and CredUIPromptForWindowsCredentials():

The CredUIPromptForCredentials function creates and displays a configurable dialog box that accepts credentials information from a user.

The CredUIPromptForWindowsCredentials function creates and displays a configurable dialog box that allows users to supply credential information by using any credential provider installed on the local computer.

See Asking the User for Credentials on MSDN for more details:

Your application may need to prompt the user for user name and password information to avoid storing an administrator password or to verify that the token holds the appropriate privileges.

However, simply prompting for credentials may train users to supply those to any random, unidentified dialog box that appears on the screen. The following procedure is recommended to reduce that training effect.

To properly acquire user credentials

  • Inform the user, by using a message that is clearly part of your application, that they will see a dialog box that requests their user name and password. You can also use the CREDUI_INFO structure on the call to CredUIPromptForCredentials to convey identifying data or a message.

  • Call CredUIPromptForCredentials. Note that the maximum number of characters specified for user name and password information includes the terminating null character.

  • Call CredUIParseUserName and CredUIConfirmCredentials to verify that you obtained appropriate credentials.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    IMO the docs are kinda joke: _"Inform the user, by using a message that is clearly part of your application, that they will see a dialog box that requests their user name and password. "_ -- as if any malicious software would have a hard time to fake such a dialog box. – zett42 Mar 15 '19 at 19:54
  • @zett42 Well, that is why the API also allows you to put your identifying information directly in the prompt dialog itself. – Remy Lebeau Mar 15 '19 at 20:01
  • For liability reasons I don't want to have access to the credentials at all, as I've mentioned in my question (regardless of whether custom or native prompt). UAC seemed suitable, because it natively and securely verifies whether the user is an admin or not. Showing an UAC prompt and getting its result would be perfect. – CodeX Mar 15 '19 at 21:09
  • 1
    @CodeX Sorry, but the only way to invoke the UAC prompt is to launch a new elevated process, as explained in other comments, or to instantiate an elevated COM object using the COM Elevation Moniker. Both would have to be done from an unelevated context in order for the UAC prompt to kick in. Otherwise, this is your next best option. Just because you ask for credentials does not mean you have to keep them, or even look at them. Prompt and discard the credentials after you validate them, not that difficult. – Remy Lebeau Mar 15 '19 at 21:22
  • I fully understand that. It's just that by definition you shouldn't enter your credentials anywhere where they don't belong. Windows credentials are for Windows only (logging on, UAC prompts and maybe network shares). I wouldn't enter my Windows credentials into any third-party app if it asks me to. From my point of view, credentials should be considered compromised if they are entered somewhere where they don't belong. If my desired solution isn't possible, I'll have to find a completely different way. I was just hoping, there's some possibility I'm not aware of. – CodeX Mar 15 '19 at 21:38
  • I think that the ideal solution would be to change the application to use "COM Elevation Moniker" that is mentioned in the SO link you included. The OP would put all of the "sensitive" functionality in a COM class that runs elevated. This class is created when the application starts so the elevation question is there up front. If the user leaves the machine idle then the class gets recreated so the application is never elevated, only the class. – Graymatter Mar 16 '19 at 01:06
  • @Graymatter that has the issue of when to idle timeout the object and destroy it, which plays into the OP's concern about the user walking away from the computer and another user using the app without entering credentials. The COM object would have to be destroyed immediately after use and re-created for each new operation, thus re-prompting for login each time. But that would certainly be a viable alternative to using sub-processes, provided the main process itself is no longer elevated, allowing the COM Elevation Moniker to prompt the user for login each time the COM object is created. – Remy Lebeau Mar 16 '19 at 01:16
  • Why would the COM object need to be destroyed immediately? It could be kept around for a while and only destroyed on some idle timer. – Graymatter Mar 16 '19 at 03:46
  • @Graymatter because the whole point of the OP's exercise is to avoid the situation where an admin user walks away from the app without locking it, and another user walks up and uses the app to do sensitive things without permission. If the app relies just on an idle timeout, the 2nd user could get in before the timeout elapses. At the time a sensitive operation is performed, a new COM object would be needed to invoke a new login prompt. I suppose the object could be created at app startup and held on to, and then recreated only on sensitive operations. – Remy Lebeau Mar 16 '19 at 04:47
  • In that case it would make sense to ask every time and not elevate the application at all. The only other way would be to have 2 com objects. You elevate the one for normal operations and elevate the second one for any "special case". The benefit of the COM object is that you can control when the elevation is requested. That control would let the OP pick and choose when to ask. – Graymatter Mar 16 '19 at 05:49