Skip to content
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

Different window stations assumed as aliases #6

Closed
diversenok opened this issue Mar 7, 2018 · 9 comments
Closed

Different window stations assumed as aliases #6

diversenok opened this issue Mar 7, 2018 · 9 comments
Labels

Comments

@diversenok
Copy link
Contributor

diversenok commented Mar 7, 2018

OS: Windows 7 x64
WinObjEx64: 1.5.2

There are two window stations that are named WinSta0 that are situated in different locations:

The one, in which the interactive user works:
\Sessions\1\Windows\WindowStations\WinSta0

And the other, for services:
\Windows\WindowStations\WinSta0

These are two different window stations but WinObjEx64 shows them as if they are represented by the same object. As a result, it is not possible to view information and change security for the second window station and its desktops.

@hfiref0x
Copy link
Owner

hfiref0x commented Mar 7, 2018

Can you illustrate this?

Nevermind this initial reply :)

The reason why it happens is because OpenWindowStation takes Winstation name as parameter. They are the same in this case. Thank you for reporting this issue.

@hfiref0x hfiref0x added the bug label Mar 7, 2018
@diversenok
Copy link
Contributor Author

diversenok commented Mar 7, 2018

Here's the problem: on my computer, these properties windows contain absolutely the same information for both WinSta0. The process list, the security descriptor and so on. If I change the security in the one properties window it will automatically change in the other.

See a screen recording of this problem.

@hfiref0x
Copy link
Owner

hfiref0x commented Mar 7, 2018

I'm afraid there will be no real workaround as OpenWindowStation API doesn't have ability to specify object path. So it will always open current session id winsta. We can try however NtUserOpenWindowStation (which have OBJECT_ATTRIBUTES as parameter) but it is unexported until Windows 10 RS1 and undocumented at all.

@diversenok
Copy link
Contributor Author

I've monitored API calls and that's what I've got. In both cases, OpenWindowStationW assumes the directory from the current session, so the call tree looks like this (NtUserOpenWindowStation or whatever opens the resultant handle is missed):

API log

That's why it shows the same information in the properties window. However, using a debugger I was able to change "\Sessions\2\Windows\WindowStations" to "\Windows\WindowStations" on the fly and it worked fine (I was able to access and change the security of zero session's WinSta0). So it is possible even on Windows 7. The question is how to achieve this properly...

@hfiref0x
Copy link
Owner

hfiref0x commented Mar 8, 2018

OpenWindowStation expects that lpszWinSta belongs to current Session. This is stated even on MSDN. Internally as you see in your API call log OpenWindowStation prepares directory path to current session directory object then it opens directory with NtOpenDirectoryObject. Next this handle used as RootDirectory in OBJECT_ATTRIBUTES parameter that passed to NtUserOpenWindowStation.

OBJECT_ATTRIBUTES ->
RootDirectory = Handle of current session Winsta directory (e.g. \Sessions\1\Windows\WindowStations)
ObjectName = pointer to UNICODE_STRING where UNICODE_STRING.Buffer = lpszWinSta
Attributes = OBJ_CASE_INSENSITIVE and (if OpenWindowStation fInherit param set) |= OBJ_INHERIT

then it calls NtUserOpenWindowStation

It goes to Win32k, here after preliminary sanity checks ObOpenObjectByName called from _OpenWindowStation internal routine. Referenced handle returned to user mode as result of API call.

What will be if we attempt to directly open WinSta object from different session? As you said when you changed object directory path OpenWindowStation returned correct handle to requested object.
This mean that you basically can reimplement OpenWindowStation by calling NtUserOpenWindowStation with your params (changing OBJECT_ATTRIBUTES RootDirectory to what is needed). That will work (at least Windows 7 and Windows 8.1 this working well).

What is the side effect of this which stops me from implementing this as a fix.

You can't close returned WinStation handle if the object you opened not belong to your session.

Why this happens.

CloseWindowStation is a syscall NtUserCloseWindowStation which goes to win32k.

This system service basically does the following (Win7/Win8.1 at least):

First it validates hWinSta parameter. It refences opaque undocumented win32k WINSTATION object by calling ObReferenceObjectByHandle. Then it does the following check - SessionId from this object and current process SessionId must be the same. If they are different STATUS_INVALID_HANDLE will be returned and execution returns back from service.

This mean that you cannot use CloseWindowStation for WinSta handles if they are not belong to current session. There is no system workaround except manual hack via direct object dereference from driver code. So this is limitation by OS design.

From current system Win32 API design point of view we should not be able to open different session WinStation objects (leaving undocumented backdoor for system use with NtUserOpenWindowStation behavior).

Yes currently WinObjEx64 think WinSta from different sessions are the same because of OpenWindowStation implementation. So additional object path check before calling this API is required as fix.

hfiref0x added a commit that referenced this issue Mar 8, 2018
Addressing issue #6
@hfiref0x
Copy link
Owner

hfiref0x commented Mar 8, 2018

See commit f5e8d75 in dev_alpha branch.
There is a code to directly call NtUserOpenWindowStation thus bypassing OpenWindowStation limitation, you can use it if you replace https://github.com/hfiref0x/WinObjEx64/blob/dev_alpha/Source/WinObjEx64/sup.c#L3109 with commented variant https://github.com/hfiref0x/WinObjEx64/blob/dev_alpha/Source/WinObjEx64/sup.c#L3130 and recompile exe (configuration Release).

However it will have all the above described problems with handle leak so commented code won't be pushed to the master branch.

hfiref0x added a commit that referenced this issue Mar 8, 2018
Addressing issue #6
@hfiref0x hfiref0x closed this as completed Mar 8, 2018
@diversenok
Copy link
Contributor Author

Wow, that's impressive :)
However, I have some additions:

There is no system workaround except manual hack via direct object dereference from driver code. So this is limitation by OS design.

And what about NtClose instead of CloseWindowStation? It definitely can close this non-typical handle.

However it will have all the above described problems with handle leak so commented code won't be pushed to the master branch.

Or, if the trick with NtClose is not applicable (for some reasons unknown to me), what about just opening only one instance of this handle with MAXIMUM_ALLOWED access for all purposes related to another session's window station instead of opening and closing these handles on the fly? In my opinion, one additional handle for the entire duration of the program is not actually a leak.

@hfiref0x
Copy link
Owner

hfiref0x commented Mar 8, 2018

Value returned by NtUserOpenWindowStation is a typical handle, not pseudo-handle, not pointer to object and it is managed by Object Manager as any other typical handle. So yes, normally NtClose should work. However, Microsoft implemented special routines to work with Winstation/Desktops/Composition objects handles. It must be done on purpose. OS wants to do additional checks before calling ObpCloseHandle for example. This handling is done via object type procedure. In case of WindowStation it is ExWindowStationObjectType->OkayToCloseProcedure (ExpWin32OkayToCloseProcedure). This procedure may block closing handle if required. It is common procedure for several object types so first it determine which type of object it should handle and then use Win32k callout (PsInvokeWin32Callout) passing type and requested operation index to Win32k callback. Win32k handles this in W32CalloutDispatch routine. Here you can find OkayToCloseWindowStation routine for example. It does several checks over handle bits and EPROCESS values and also may block handle closure. Same btw goes to ExWindowStationObjectType->OpenProcedure it also triggers win32 callout WindowStationOpenProcedure. Everything here is completely undocumented and subject of change between Windows versions. How does this unusual way of work (NtUserOpenWindowStation+NtClose) will affect system is something I don't really want to investigate, especially in case of Windows 10 where things are changing rapidly. However, you can use version from dev_alpha branch, replace supOpenWindowStationFromContext with commented code and replace all CloseWindowStation calls with NtClose. At your own risk of course.

@anotherfinemess84
Copy link

Hi, quick question. If \Sessions\2\Windows\WindowStations\WinSta0 handle in within a process (wow64) is a driver required to open that handle and use it to read the process rather than opening my own handle? Previously this has required using csrss or something else (driver) but it seems possible from usermode.

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants