ramensoftware / windhawk

The customization marketplace for Windows programs: https://windhawk.net/
https://windhawk.net
GNU General Public License v3.0
1.07k stars 28 forks source link

Windhawk main window not opening on non-admin account #108

Closed Bert-Proesmans closed 9 months ago

Bert-Proesmans commented 9 months ago

Relevant context

My system has an admin account, and my own non-admin account. I use my own account for day-to-day work.
Mods work as expected.

Expected results

Double clicking the tray icon launches the main window (vscodium) of windhawk and I can do limited things like working on mods and reading logs.

Actual results

Double clicking the tray icon doesn't open the windhawk window and reports an access error to the (I presume) working directories of the mod IDE. See the screenshot below.

image

Workaround

I have to exit Windhawk (requires elevation) and launch Windhawk again with admin privileges.

[..] In Windhawk v1.0, I added an option to exit Windhawk without admin rights by holding Shift, right clicking on the tray icon and choosing to force exit. It can be used in cases like this. [..] Originally posted by @m417z in https://github.com/ramensoftware/windhawk/issues/22#issuecomment-1364973126

Holding shift does not change anything to the right click menu, nor changes the functionality of closing windhawk the user process (tray icon).

Other info

After adding my local user to the group "Network Configuration Operators", Windows is trigger happy to elevate into additional priviliges. But the typical administrator permissions (like SeTcbPrivilege) aren't there and will cause user confusion and/or software confusion.

m417z commented 9 months ago

Running the Windhawk UI in "readonly mode" isn't supported, not by Windhawk, and it seems that not by VSCode, too. But Windhawk should've elevated the process, so it looks like there's a bug.

Previously, Windhawk was asking for elevation every time the UI was opened. Later, for convenience, I added a scheduled task that can be used by the tray icon process to run the UI elevated without the elevation prompt. Perhaps with the scheduled task, it somehow launches unelevated. You can disable it in Windhawk's settings. Once it's disabled, does it make a difference?

I remember testing this scenario with a limited user, but perhaps there's a configuration I didn't try or a new bug.

Holding shift does not change anything to the right click menu, nor changes the functionality of closing windhawk the user process (tray icon).

I removed this functionality. It was added as an emergency measure, and was replaced by the safe mode button which can be accessed from the toolbox.

Bert-Proesmans commented 9 months ago

I added a scheduled task that can be used by the tray icon process to run the UI elevated without the elevation prompt. Perhaps with the scheduled task, it somehow launches unelevated. You can disable it in Windhawk's settings. Once it's disabled, does it make a difference?

The task is installed and runs without issue. The issue is the configuration;

Use the following user account; [SID of builtin\USERS] This runs the task with process owner set to the current user, in this case the interactive one. My user account is not a local administrator. It elevates correctly because the process token is protected from my interactive session, but the elevation doesn't grant additional privileges.
The task would need to be executed as a builtin account with permissions to write to ProgramData or with another administrator account, none are ideal. Going down that route would also require adding an EVERYONE ALLOW rule to the task file ACL, which is against MS best practise.

A graceful degradation of required privileges would be one possible robust solution. I'm gonna look into that.

EDIT; Removed related discussion link, for it was not related to privileges.

m417z commented 9 months ago

It elevates correctly because the process token is protected from my interactive session, but the elevation doesn't grant additional privileges.

Frankly I'm not an expert in Windows tokens and various possible configurations around it, but sounds like you have a rather unique configuration. What I'm familiar with is that a user can be admin or limited, and if it's limited and a process requires elevation, the user can run it as another, admin user (and a password might be required).

From what I understand, in your case a process "elevates correctly", which means it thinks admin rights are granted, but that's not the case. Is that right? If so, what's the correct solution for Windhawk in this case?

A graceful degradation of required privileges would be one possible robust solution.

Do you mean being able to run correctly with limited access rights? That would require a lot of work, either making VSCode work in this mode, or making an alternative execution mode (different app, different UI, etc.) just for that case.

Bert-Proesmans commented 9 months ago

What I'm familiar with is that a user can be admin or limited, and if it's limited and a process requires elevation, the user can run it as another, admin user (and a password might be required).

Correct. I run everything under my own user, which is limited. The admin account on my computer is for one-off uses.

From what I understand, in your case a process "elevates correctly", which means it thinks admin rights are granted, but that's not the case. Is that right?

The task configuration as it is now will start the task with a unique combination of the priviliges of the useraccount starting the task and the builtin group 'users'. These priviliges also include all privileges normally guarded by elevation, in my case none (see screenshot, right prompt is an elevated window for my limited user) because my user is not part of the administrator group.
So this task setup only works as expected if the currently interactive user, which starts the windhawk task by double clicking the task icon, is a member of the administrators group. The task scheduler does not present me with a consent window where I can put the credentials of my admin user.

image

"Elevation" does "not grant" additional privileges for a user. It unhides already granted privileges. Interactive logon is basically a filter on privileges and the consent windows removes that filter for a specific process tree.

If so, what's the correct solution for Windhawk in this case?

Honestly, the IDE should not need a workspace in ProgramData which is only writeable by special user groups.
I read your blogpost about injection and I do understand that the injection process needs elevation. The mod installations could or could not require special permissions (i'm on the fence on that one but install probably should require administrator privileges, toggling on/off should be possible without administrator privileges).

So it's hard to properly "fix" this. Here are a few ideas;

  1. Execute task as builtin account SYSTEM (aka LOCALSYSTEM)
  2. Drop the task and run windhawk in self-elevation mode. i think this requires manifest changes or win-compatibility mode needs to kick in (for example, add the word setup to your exe name and get auto elevated)?
  3. Drop the task and trigger UAC during process start (Microsoft makes this way harder than needs to be, check ShellExecuteA and runas command)
  4. Create a special Windhawk group and adjust ACLs, kinda like docker for windows does
  5. Craft special process token (very advanced) for running Windhawk
  6. Have a hooking flow that has limited functionality within the user session/Have the IDE working directory in LocalAppData instead of ProgramData
  7. Extract all system administration functionality into the windhawk service and do IPC
m417z commented 9 months ago

an elevated window for my limited user

That's what I find unusual. I thought that there's no such thing.

I created a new limited user to test it, and here's what I see:

https://github.com/ramensoftware/windhawk/assets/4129781/eaa64d85-ecc4-4fd4-aeda-59adecd63f2f

The same prompt appears when clicking on Windhawk's tray icon.

Honestly, the IDE should not need a workspace in ProgramData which is only writeable by special user groups.

I agree that the better architecture would be to run VSCode unelevated, and execute privileged tasks via an elevated helper process when that's needed, but it's not trivial and I didn't implement it. Many things that can be improved, limited time.

I do understand that the injection process needs elevation.

It's only required to inject code into elevated process. When installing Windhawk, you can also choose to extract a portable version which can run unelevated. Then it can only customize unelevated processes, of course.

toggling on/off should be possible without administrator privileges

I think it depends on the usage, even the safe mode not requiring elevation can be undesirable for some cases (let's say I used Windhawk to create a mod that adds constraints to limited users). I kept it because the alternative is not having an escape hatch in case of a problem. I may make it configurable in the future if there's demand for it.

Drop the task and trigger UAC during process start (Microsoft makes this way harder than needs to be, check ShellExecuteA and runas command)

Windhawk already checks whether it runs as admin, and if not, tries to elevate with ShellExecute("runas"). Not sure why it doesn't have the desired effect in your case.

Perhaps with the scheduled task, it somehow launches unelevated. You can disable it in Windhawk's settings. Once it's disabled, does it make a difference?

Could you check that? Does it make a difference whether or not the scheduled task is disabled?

Workaround I have to exit Windhawk (requires elevation) and launch Windhawk again with admin privileges.

BTW a simpler workaround for you might be to just run the UI elevated with the following command:

"C:\Program Files\Windhawk\windhawk.exe" -run-ui
Bert-Proesmans commented 9 months ago

Perhaps with the scheduled task, it somehow launches unelevated. You can disable it in Windhawk's settings. Once it's disabled, does it make a difference?

Could you check that? Does it make a difference whether or not the scheduled task is disabled? Windhawk already checks whether it runs as admin, and if not, tries to elevate with ShellExecute("runas"). Not sure why it doesn't have the desired effect in your case.

My bad, I misinterpreted this suggestion and that caused us to desync.
Toggling "Require UAC elevation for running Windhawk" to true, the consent prompt launches as expected after attempting to open the Windhawk UI. I was confused earlier thinking that the expected behaviour would occur in the toggle state false.

This makes the workaround easier for not being able to open the windhawk ui from a limited account.

I'm gonna check out which functionality is available on a portable mode install.

m417z commented 9 months ago

Toggling "Require UAC elevation for running Windhawk" to true, the consent prompt launches as expected after attempting to open the Windhawk UI.

Great, that narrows the problem. Two quick questions:

Bert-Proesmans commented 9 months ago

Great, that narrows the problem

There is for me, with the UAC required toggle in the settings, no more workaround necessary to open the main window. I suppose I should close the issue, unless you need more info.

What's your Windhawk version?

Windhawk v1.3.1 beta

Open Task Scheduler, [..], post the whole command

C:\Program Files\Windhawk\windhawk.exe -run-ui-as-admin

m417z commented 9 months ago

I suppose I should close the issue, unless you need more info.

I'm not sure why the issue only happens with the scheduled task, and if it's a simple fix, I'd like to get it fixed. It seems to work properly with my test with a limited account.

Windhawk v1.3.1 beta C:\Program Files\Windhawk\windhawk.exe -run-ui-as-admin

That's what I expected. Running with the -run-ui-as-admin flag does the following (pseudo code):

if (!runningAsAdmin()) {
  runSelfAsAdmin("-run-ui");
  exit();
}
runVsCode();

Running with the -run-ui flag just runs VSCode.

So either the runningAsAdmin part of the runSelfAsAdmin part doesn't work when ran from a scheduled task on your computer.

If you'd like to debug that, I can create a tiny tool for you to compile and test. If not, it's tricky for me to investigate it without a way to reproduce it locally. In this case the issue can be closed.

Bert-Proesmans commented 9 months ago

https://github.com/ramensoftware/windhawk/blob/0f673d740ee337acc1edd422e072208fbb72aa18/src/windhawk/app/functions.cpp#L72-L97

That's a correct test for the Administrator group. So that flows into.

https://github.com/ramensoftware/windhawk/blob/0f673d740ee337acc1edd422e072208fbb72aa18/src/windhawk/app/app.cpp#L442-L452

Which calls for elevation, but doesn't show the consent window. This is because the process is already flagged as elevated, as dictated by the task configuration in the task scheduler.

PS C:\Windows\system32> (Get-ScheduledTask -TaskName WindhawkRunUITask).Principal

DisplayName         :
GroupId             : Users
Id                  : Author
LogonType           : Group
RunLevel            : Highest <-- Indicates elevated
UserId              :
ProcessTokenSidType : Default
RequiredPrivilege   :
PSComputerName      :

So the roundtrip through the consent flow is transparent and the proces is continuing on with current user privileges. The required permissions for the ProgramData folder are.

PS C:\Users\Bert> (get-acl c:\ProgramData\Windhawk).Access

FileSystemRights  : 268435456
AccessControlType : Allow
IdentityReference : CREATOR OWNER
IsInherited       : False
InheritanceFlags  : ContainerInherit, ObjectInherit
PropagationFlags  : InheritOnly

FileSystemRights  : FullControl
AccessControlType : Allow
IdentityReference : NT AUTHORITY\SYSTEM
IsInherited       : False
InheritanceFlags  : ContainerInherit, ObjectInherit
PropagationFlags  : None

FileSystemRights  : FullControl
AccessControlType : Allow
IdentityReference : BUILTIN\Administrators
IsInherited       : False
InheritanceFlags  : ContainerInherit, ObjectInherit
PropagationFlags  : None

FileSystemRights  : ReadAndExecute, Synchronize
AccessControlType : Allow
IdentityReference : BUILTIN\Users
IsInherited       : False
InheritanceFlags  : ContainerInherit, ObjectInherit
PropagationFlags  : None

Nothing strange here.

image

So my limited user only has permission to read/execute on ProgramData\Windhawk*. For me there is no issue here, other than the fact that Windhawk UI stores it's working directory at that location.

I cannot say what is the difference between this situation and the properties of your limited user. You maybe have given your limited user permissions on the windhawk programdata folder?
You haven't explicitly stated what is wrong with your limited user, so I assume here that you want to understand/solve the warning message "Unable to write program user data". Otherwise we're not talking about the same thing and should refocus the issue on windhawk UI needing write-access to ProgramData.


As an aside; I do not know anything about the task scheduler API and noticed TASK_RUN_AS_SELF, but cannot tell if this is relevant or not.

https://github.com/ramensoftware/windhawk/blob/0f673d740ee337acc1edd422e072208fbb72aa18/src/windhawk/app/ui_control.cpp#L259-L264

m417z commented 9 months ago

This is because the process is already flagged as elevated, as dictated by the task configuration in the task scheduler.

I don't think that "Highest" means elevated. I think it means - highest possible for the current user. Indeed, when I run the task from the limited user, the process that is spawned isn't marked as elevated by System Informer:

https://github.com/ramensoftware/windhawk/assets/4129781/446173f0-61ca-4198-baac-0ff04667b415

The relevant frame:

image

The required permissions for the ProgramData folder are.

I see exactly the same.

image

For me it's only "Users":

image

You maybe have given your limited user permissions on the windhawk programdata folder?

I just created a limited user in the most standard way possible.

You haven't explicitly stated what is wrong with your limited user, so I assume here that you want to understand/solve the warning message "Unable to write program user data". Otherwise we're not talking about the same thing and should refocus the issue on windhawk UI needing write-access to ProgramData.

The issue that I'm willing to solve is: Instead of you seeing the "Unable to write program user data" warning message, I'd like you to get the elevation prompt as I'm getting with my quick test with a limited user, as can be seen in the video in this comment.

As an aside; I do not know anything about the task scheduler API and noticed TASK_RUN_AS_SELF, but cannot tell if this is relevant or not.

Without this flag, the task is started for all users, which is not what I want.

Bert-Proesmans commented 9 months ago

For me it's only "Users"

Indeed there is the difference. I've removed all groups (except "users") from my limited account and now experience the same flow as you described.
Being enlisted in administrator groups changes how the consent flow works. I did not know that, nor expected that because memberships and elevation shouldn't be, and logically isn't, tightly coupled.

I haven't recorded my screen because there is nothing new or different on my computer in the tested configuration. I'm going to keep using those groups (because they make other tasks less tedious) and the settings toggle inside Windhawk.

I don't think that "Highest" means elevated

It does, according to the docs and my experience, have only two switches; not-elevated and elevated. Important to note this is specifically targetting elevation, not other process security attributes like isolation/virtualization/integrity which do have levels.

m417z commented 9 months ago

Being enlisted in administrator groups changes how the consent flow works.

Since this is a non-standard configuration which is not common, and since there's a workaround, I guess I'll keep things as they are. I'm not sure there's a single correct fix for this. I could try and check for the required permissions to files, registry and such, but it feels like a bit on an overkill, and makes the whole logic more complicated and difficult to reason about.