The winlogon secure desktop is really just another desktop on winsta0, and I came across a utility - RemoteUnlock.exe - which injects itself as a thread in the console winlogon process of a remote machine and switches from the secure winlogon desktop to the logged on user’s winsta0\default desktop.
This allows you to skip the SAS process requiring the account password of the currently logged on user, and just shows the desktop, allowing full console interaction as the logged on user.
This doesn't really have very many practical uses, but the concept is great and I thought I would share the utility I came across plus some additional thoughts on the topic.
The utility and source code:
I've previously toyed with creating a command shell on a remote winlogon desktop, which can be done with the following command:
- psexec /s \\%computer% cmd /c c:\windows\temp\psexec /accepteula /x /d /s cmd
After another discussion regarding secure passwords with PowerShell, I was curious just what might be possible from the Winlogon desktop.
One thing led to another, and after trying unsuccessfully using a PowerShell script run from a command shell on the Winlogon desktop (using psexec) calling GetProcessWindowStation/UnlockWindowStation (an undocumented API, presumably unlocking a window station) and OpenDesktop/SwitchDesktop , I came across a reference to UnlockWindowStation which mentioned that this would only work when running as part of winlogon.exe, explaining why the PowerShell script did nothing.
I then successfully tested remoteunlock.exe running from one XPSP2 workstation against another. RemoteUnlock uses switchdesktop which doesn’t actually unlock the desktop, I was going to recompile and try unlockwindowstation, but I don’t have Visual Studio. It would also be interesting to modify this to work with TS winlogon processes, rather than only the interactive console (I presume you could similarly ‘unlock’ an in-use TS session).
As an aside, below is the PowerShell script calling APIs through VB.Net embedded code, which doesn’t work in this case but it's still a valid example of how to call APIs from PowerShell (which I essentially just copied from http://monadblog.blogspot.com/2005_12_01_archive.html):
$provider = new-object Microsoft.VisualBasic.VBCodeProvider
$params = new-object System.CodeDom.Compiler.CompilerParameters
$params.GenerateInMemory = $True
$refs = "System.dll","Microsoft.VisualBasic.dll"
# VB.NET EXAMPLE
$txtCode = @'
Declare Auto Function GetProcWinStation Lib “user32.dll” Alias "GetProcessWindowStation" () As Integer
Declare Auto Function UnlockWinStation Lib “user32.dll” Alias "UnlockWindowStation" (ByVal WinSta As Integer) As Integer
Declare Auto Function OpenWinStation Lib “user32.dll” Alias "OpenWindowStation" (ByVal lpszWinSta As String, ByVal fInherit as Boolean, ByVal ACCESS_MASK as Integer) As Integer
Declare Auto Function OpenDesktop Lib “user32.dll” Alias "OpenDesktop" (ByVal lpszDesktop As String, ByVal dwFlags as Integer, ByVal fInherit as Boolean, ByVal ACCESS_MASK as Integer) As Integer
Declare Auto Function SwitchDesktop Lib “user32.dll” Alias "SwitchDesktop" (ByVal hDesktop As Integer) As Integer
main = GetProcWinStation()
' main = OpenWinStation("winsta0\\desktop", True, 895)
main = OpenDesktop("Default", 0, True, 256)
$results = $provider.CompileAssemblyFromSource($params, $txtCode)
$mAssembly = $results.CompiledAssembly
$i = $mAssembly.CreateInstance("FindProcessWinStation")
$r = $i.main()
Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.