1

ShellExecute has a parameter that allows to request a UAC permission to run a process with elevated rights: runas

Launches an application as Administrator. User Account Control (UAC) will prompt the user for consent to run the application elevated or enter the credentials of an administrator account used to run the application.

But is it possible to do the contrary?
I have an application that is run with elevated rights by default and there are urls which user can open. But if I use ShellExecute, a default browser will be opened. Some malware can be registered as a default browser.


using CreateProcessAsUser to launch a url - this question seems to be on the same problem. It generally indicates, that CreateProcessAsUser can't be used to open a URL by itself.

Sergey Kolesnik
  • 3,009
  • 1
  • 8
  • 28
  • 4
    See [How can I launch an unelevated process from my elevated process and vice versa?](https://devblogs.microsoft.com/oldnewthing/20131118-00/?p=2643), and [How can I launch an unelevated process from my elevated process, redux](https://devblogs.microsoft.com/oldnewthing/20190425-00/?p=102443) – Remy Lebeau Nov 01 '21 at 14:36
  • @RemyLebeau the second links states that the approach is not sueted for opening a default browser – Sergey Kolesnik Nov 01 '21 at 14:43
  • sure, if you go through the shell to launch the browser. But if you knew ahead of time the EXE path/command for the desired browser, you could use `CreateProcess()` instead. – Remy Lebeau Nov 01 '21 at 14:47
  • @SergeyKolesnik: ShellExecute has quite a few different functions; it's a swiss army knife in that sense. It can execute programs, open documents, and open URL's. You seem to mix the functions a bit when you write "I have an application" and "there are urls". – MSalters Nov 01 '21 at 14:51
  • @RemyLebeau I cannot predict which browser a user has. I'd like to run a default one and secure the call from running a malware. Of cource, I could also find out a path to a default browser and then call `CreateProcess()`, but I was hoping for a solution with less manipulations... It **doesn't have to be** possible without workarounds though. – Sergey Kolesnik Nov 01 '21 at 14:52
  • @MSalters a default browser will be opened with `ShellExecute` called with an url. – Sergey Kolesnik Nov 01 '21 at 14:53
  • @SergeyKolesnik: Of course. And a default application will be opened when `ShellExecute` is called with a document file name. So what do you mean by "I have an application" ? `ShellExecute` can't read your mind; if you give it just a URL it will need to fill in the other details. – MSalters Nov 01 '21 at 14:56
  • @MSalters Well, I don't need "other details". I need a default browser to open an url. That's it. A convenient way was to use `ShellExecute(NULL, NULL, url, NULL, NULL, SW_SHOWDEFAULT)`. – Sergey Kolesnik Nov 01 '21 at 15:00
  • 1
    But the first link does exactly `ShellExecute` via `IShellDispatch2`. it even says "Open an unelevated Web page in the user’s default Web browser." in the usages example at the bottom. – dewaffled Nov 01 '21 at 15:26
  • @dewaffled yes. I might also accept it's method as an answer – Sergey Kolesnik Nov 01 '21 at 15:28

1 Answers1

1

My personal inclination would be to call FindExecutable or AssocQueryString, then use CreateProcessAsUser to open the browser with the un-elevated account/credentials.

This is a little more work than using ShellExecute, but not a whole lot--and it makes your intent 100% clear and explicit.

Using COM from C++ is enough work that it's almost certainly more work to use IShellDispatch2, and (at least to me) that seems likely to do quite a bit more to hide the real intent of the code.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • `FindExecutable` is useful when you actually know which executable to look for. So given the amount of code to determine which executable to look for, it is as much work as calling `IShellDispatch`. But still a valid answer. Btw, the intent is clear from a function `OpenURL`. The question here is about the way to implement it. – Sergey Kolesnik Nov 01 '21 at 16:12
  • @SergeyKolesnik: The whole point of `FindExecutable` is to find the executable when you don't know what executable to look for. For example, you can pass it `c:\\file.txt`, and (assuming the user hasn't changed the default text editor) it will give you the path to Notepad.exe, which you can then use to open the text file. But if the user has changed that association, it'll give you the path to whatever editor they prefer instead. – Jerry Coffin Nov 01 '21 at 16:15
  • Didn't know that! Thanks – Sergey Kolesnik Nov 01 '21 at 16:16
  • What if the current user has admin rights though? – Sergey Kolesnik Nov 01 '21 at 16:21
  • @SergeyKolesnik: That's why you use CreateProcessAsUser--create the process with an account that has exactly the restrictions you want to apply. – Jerry Coffin Nov 01 '21 at 16:40
  • I can't possibly be sure if such an account exists. Even if I create one during an installation, I will have to check each time if that account wasn't modified to have admin rights... I think it deserves a question of its own. – Sergey Kolesnik Nov 01 '21 at 16:43
  • @SergeyKolesnik: I suppose if that's really a major concern, you would do a NetUserAdd, run the program, then NetUserDel when you're done using that account, so you create it with the appropriate credentials each time. Admittedly, that starts to get a bit more complex though. If no such account exists, it's not clear (at least to me) that the COM route would work correctly either though. – Jerry Coffin Nov 01 '21 at 20:03
  • Could you please modify your answer with a code example to open `cmd.exe` without elevated rights? I get a token via `OpenProcessToken` with `TOKEN_QUERY`, `TOKEN_DUPLICATE`, and `TOKEN_ASSIGN_PRIMARY`, then pass it to `CreateProcessAsUserA`, but the resulting console is opened in admin mode – Sergey Kolesnik Nov 05 '21 at 14:12