Secure Desktop Prompting : Always Run As Administrator

I'm a PC, and Windows 8 will be partially my idea.


User Account Control

The main goal of the UAC(User Account Control), is to limit application software down to standard privileges, until an administrator authorizes an elevation of privileges. Applications trusted by the user/admin may be granted administrative privileges, while malwares are prevented from executing on the desktop.

This article describes how to grant semi-permanent authorization, to certain programs specified by the administrator, on a secure desktop. The main user interface looks, sounds, and acts like the real thing, only better! It's a bold statement, but that's how I roll.

Update 12/01/09-12/24/09

This articles source files have been updated with the following changes.

  1. The .NET Process component is now used, so that the SecureString data type protects the password until it's transfered and disposed of.
  2. It validates credentials and starts applications from a "Sub Main", so it loads a little faster now.
  3. The Administrator password is set on the fly, so that whatever password is typed, changes that users password, and authorizes the program with it. Previous authorizations become inactive when passwords are changed.
  4. The background imagery mirrors the particular operating system that is it being displayed on, so that the shields are appropriately colored.

Outline

There are four main elements to achieving this.

  1. A kiosk-like desktop that's protected from input and execution.
  2. Proper declaration and call of the CreatProcess API, so that processes can be launched on the new desktop. This was real tricky to get right while upgrading the old style declarations.
  3. A declaration and proper call to the CreateProcessWithLogon API.
  4. An encryption routine, to obscure your password, user name, and path to the executable shortcut.

Key features

  1. It's simpler to use common programs that have already been granted privileges once. A malware cannot launch these applications with your granted permission either. Only the user knows where this application is located, and/or it's link, and what it opens. This feature will encourage users NOT to turn off the UAC completely! It is best to use a Standard or split token account anyways, but now you can "white list" certain programs as the true administrator. It's the best of both worlds, because the remaining UAC prompts will be more suspicious. Read more about the pros/cons of usability versus security, of the UAC when it was first designed.Windows Team Blog
  2. The ability to launch a small group of programs while on the secure desktop is a unique feature that microsoft should integrate into the UAC on Windows 8. As of now, windows allows you to input a password safely there, but then returns you back to the jungle. A safe place to elevate and use high level programs, would avoid snooping programs that wait until after you have granted elevation, and then automate the user interface. For example the task manager, displays the elevation button until you elevate. A malware can then spring into action, after being dormant.
  3. It's compatible with 2k/XP/Vista/7, {32-64 bit}. Why not offer backwards compatibility to existing XP administrators? No worries, got your back. This is an immediate security upgrade to the wide open "Run As" window, which allows keyloggers to snatch your password as shown below.

This flaw exists in Vista and Windows 7 as well. Yikes!

XP users should run their PC with a standard account. Vista/7 users should run a split token account, or in other words a user that is in the administrators group, thus avoiding password phishing. In the past and still today, alot of people don't use a standard account, because you can't do much of anything without getting access denied messages, and many programs actually need higher privileges to operate as designed.
It is a sound security idea, but it's highly unusable in its stock form. Now you can have your cake, and eat it too. You can safely use a restricted account to protect the system against software attacks, but not be restricted by it, when using it normally.
In addition passwords cannot be sniffed on a secure desktop object, as opposed to the "Run As" window which is completely naked on the default desktop, as shown above.

Setup(Updated)

Administrator activation is now seamless with first time use.

Vista and Windows 7
The manifest file included, requires the user to elevate the program to set it up on Vista and Windows 7. If the manifest file is deleted after setup, the program will run with *{reduced functionality}.

2k and XP
On 2K/XP systems, you will have to log into an existing administrator account to setup the program. After the setup is complete Standard accounts will be able to run it, but only with reduced functionality.

* When running with reduced functionality, the program will not be able to save files in restricted folders like, "Program Files", or "System32", etc. It also will not be able to change passwords on the fly, but it will still be able to make use of an existing password to authorize shortcuts.

Usages

The sample application and source files included in this article, can be used in five ways, four of which are distinctive.
The most secure way to make permanent authorizations, is to have the program run at the welcome screen(boot), as I've outlined in my On Screen Keys article last month. That way your password would be safe at the welcome screen. However at the moment there are virtually zero high-level hacks against this kind of program on a secure desktop object like that of an ATM machine, so you can feel very secure while using it.

  1. Always Run As Administrator
    Run the program and it will enter a secure desktop, where you can enter your password, user name, location, and path to the file that you want to,"Always run as administrator". Click OK > Yes. The program then becomes an administrative shortcut, that will always open the specified path with administrative privileges. It will also create a regular shortcut, for you to place on the desktop, taskbar, start menu, or desired location, that way you can quickly access the shortcut at any time.
  2. Run On Secure Desktop
    Run the program and enter a secure desktop, where you can provide the password, to run almost any application in this secure environment, without fear of keyloggers etc. You don't have to "always" run as administrator, either. Just close the app, and no settings will be saved or remembered.
  3. Run On Secure Administrator Desktop
    Run the program and enter a secure desktop, where you can provide the password. Then open explorer.exe, usually located here:
    C:\Windows\explorer.exe
    This will show the taskbar, and start menu of the administrator. The desktop will have been given a unique number, that you should remember for this session.{see illustration below} Now if you leave the administrator desktop by closing any open applications, you may navigate back at any time, by right clicking the secure button, and choosing what desktop you want to go to.
  4. Run On Default Desktop
    Run the program and enter a secure desktop, where you can provide the password, and then click the Desktop button to exit the secure environment. Now the program will run on the regular desktop, keeping your secured data. You can then minimize the application to the taskbar icon, so that it can be used as an administrative launch deck, by Right-clicking > Open.

You can even make a new verb that starts up this program by right-clicking an executable file, similar to the "Run as... verbs".

Create a text file with notepad, and paste the following contents. Then save the text(.txt) file as a registry(.reg) file. Now double click it, to enable the new verb in the registry.

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\exefile\shell\runasadmin]
"HasLUAShield"=""
@="Run as administrator always"

[HKEY_CLASSES_ROOT\exefile\shell\runasadmin\command]
@="C:\\Security\\UAC.exe %*"

In the regedit code above, you would need to change the path to your security program.

3. In method 3, the unique desktop name is found when you mouse over the "File origin:" as show here.

To navigate to a particular desktop, right click the Secure/Desktop button, and specify it's name.

Secure Desktop

Default Desktop w/Tray Icon

Secure Desktop Prompting : Always Run As Administrator

Creating Processes

The two API used to create a process in this article are, CreateProcess, and CreateProcessWithLogon. The CreateProcess API will be used to launch a new process on a specified desktop. The CreateProcessWithLogon will be used to launch a new process with administrative elevation, by using the users name and password.

[VB0575.JPG]

In VB.NET CreateProcess looks something like this.

'Up top!
Imports System.Runtime.InteropServices
 Const INFINITE As Int32 = -1
 Private Structure SECURITY_ATTRIBUTES
     Public nLength As Int32
     Public lpSecurityDescriptor As Int32
     Public bInheritHandle As Int32
 End Structure
 Private Structure PROCESS_INFORMATION
     Public hProcess As Int32
     Public hThread As Int32
     Public dwProcessId As Int32
     Public dwThreadId As Int32
 End Structure
 Private Structure STARTUPINFO
     Public cbSize As Int32
     Public lpReserved As String
     Public lpDesktop As String
     Public lpTitle As String
     Public dwX As Int32
     Public dwY As Int32
     Public dwXSize As Int32
     Public dwYSize As Int32
     Public dwXCountChars As Int32
     Public dwYCountChars As Int32
     Public dwFillAttribute As Int32
     Public dwFlags As Int32
     Public wShowWindow As Int32
     Public cbReserved2 As Int32
     Public lpReserved2 As Byte
     Public hStdInput As Int32
     Public hStdOutput As Int32
     Public hStdError As Int32
 End Structure

 Private Declare Function apiCreateProcess Lib _
 "kernel32" Alias "CreateProcessA" _
 (ByVal lAppName As String, _
 ByVal lCmLine As String, _
 ByVal lProcessAtt As Int32, _
 ByVal lpThreadAtt As Int32, _
 ByVal bInheritHandles As Boolean, _
 ByVal dwCreationFlags As Int32, _
 ByVal lEnvironment As String, _
 ByVal lCurDir As String, _
 ByRef lStartInfo As STARTUPINFO, _
 ByRef lProcessInfo As PROCESS_INFORMATION) As Int32

 Private Declare Function apiWaitForSingleObject Lib _
 "kernel32" Alias "WaitForSingleObject" _
 (ByVal hHandle As Int32, _
 ByVal dwMilliseconds As Int32) As Int32

 Private Declare Function apiCloseHandle Lib _
 "kernel32" Alias "CloseHandle" _
 (ByVal hObject As Int32) As Int32

 Public Function StartProcess(ByVal sPath As String) As Int32
     On Error Resume Next
     Dim pInfo As New PROCESS_INFORMATION
     Dim psi As New STARTUPINFO
     psi.lpTitle = "Default" 'Desktop name!!!
     psi.lpDesktop = "Default"
     psi.cbSize = Marshal.SizeOf(psi)

     StartProcess = apiCreateProcess _
      (sPath, Nothing, Nothing, Nothing, False, Nothing, Nothing, Nothing, psi, pInfo)
      If StartProcess <> 0 Then 'If process started
         ' Wait until the process has completed
         apiWaitForSingleObject(pInfo.hProcess, INFINITE)
         apiCloseHandle(pInfo.hProcess)
         apiCloseHandle(pInfo.hThread)
     End If
 End Function
[VB675.JPG]

In VB6 CreateProcess looks a little different.

Option Explicit
Const INFINITE As Long = -1
Private Type PROCESS_INFORMATION
   hProcess As Long
   hThread As Long
   dwProcessId As Long
   dwThreadId As Long
End Type
Private Type STARTUPINFOW
   cbSize As Long
   lpReserved As Long
   lpDesktop As Long
   lpTitle As Long
   dwX As Long
   dwY As Long
   dwXSize As Long
   dwYSize As Long
   dwXCountChars As Long
   dwYCountChars As Long
   dwFillAttribute As Long
   dwFlags As Long
   wShowWindow As Integer
   cbReserved2 As Integer
   lpReserved2 As Long
   hStdInput As Long
   hStdOutput As Long
   hStdError As Long
End Type

Private Declare Function apiCreateProcess Lib _
"kernel32" Alias "CreateProcessW" _
(ByVal lpApplicationName As Long, _
ByVal lpCommandLine As Long, _
ByRef lpProcessAttributes As Long, _
ByRef lpThreadAttributes As Long, _
ByVal bInheritHandles As Long, _
ByVal dwCreationFlags As Long, _
ByRef lpEnvironment As Long, _
ByVal lpCurrentDirectory As Long, _
ByRef lpStartupInfo As STARTUPINFOW, _
ByRef lpProcessInformation As PROCESS_INFORMATION) As Long

Private Declare Function apiWaitForSingleObject Lib _
"kernel32" Alias "WaitForSingleObject" _
(ByVal hHandle As Long, _
ByVal dwMilliseconds As Long) As Long

Private Declare Function apiCloseHandle Lib _
"kernel32" Alias "CloseHandle" _
(ByVal hObject As Long) As Long


Public Function StartProcess(ByVal sPath As String) As Long
   On Error Resume Next
   Dim psi As STARTUPINFOW
   Dim pInfo As PROCESS_INFORMATION
   psi.cbSize = Len(psi)
   psi.lpTitle = StrPtr("Default") 'desktop name!!!
   psi.lpDesktop = StrPtr("Default")
   StartProcess = apiCreateProcess _
   (StrPtr(sPath), ByVal 0, ByVal 0, ByVal 0, 1, 0, ByVal 0, ByVal 0, psi, pInfo)
   If StartProcess <> 0 Then
      'Wait until the process has completed
      Call apiWaitForSingleObject(pInfo.hProcess, INFINITE)
      apiCloseHandle (pInfo.hProcess)
      apiCloseHandle (pInfo.hThread)
   End If
End Function
[VB0575.JPG]

Both API were tricky to declare without proper documentation, but CreateProcessWithLogon was the hardest.

UPDATE- Since .NET makes use of the SecureString data type, it's recommended. Here is a quick example.

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        txtPassword.UseSystemPasswordChar = True
        txtUser.Text = Environment.UserName
        txtFilePath.Text = Application.StartupPath & "\" & Process.GetCurrentProcess.ProcessName & ".exe"
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim ss As New Security.SecureString 'Create secure string for password transfer
        Dim ret As Int32 = 0
        For i As Int32 = 1 To txtPassword.Text.Length
            ss.AppendChar(Chr(Asc(txtPassword.Text.Substring(i - 1, 1))))
        Next
        ret = StartProcessAs(txtFilePath.Text, txtUser.Text, ss)  'Try and start shortcut as administrator
        ss.Dispose() 'erase password string
        If ret = 0 Then
            MessageBox.Show("The process image could not be started with those credentials" & vbCrLf & "or elevation privileges.", "Failure", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification) : Exit Sub
            Exit Sub
        End If
        Me.Close()
    End Sub

    Public Function StartProcessAs(ByVal fPath As String, ByVal user As String, ByVal password As Security.SecureString) As Int32
        On Error Resume Next
        Dim p As New Process, psi As New ProcessStartInfo
        psi.UseShellExecute = False 'need this for passwords
        psi.LoadUserProfile = False 'ehh?  might want/ not. Loads registry of user
        psi.UserName = user 'User name usually Administrator, unless policy changed
        psi.Password = password 'Admins secured password being transfered
        psi.WorkingDirectory = fPath.Replace(IO.Path.GetFileName(fPath), "") 'What path to file
        psi.FileName = IO.Path.GetFileNameWithoutExtension(fPath) 'Set file name
        p.StartInfo = psi 'Attach start up information
        p.Start() 'Start the darn thing already
        StartProcessAs = p.Id 'Use process id as the return value
        password.Dispose() 'Erase temp password securely
    End Function

The CreateProcessWithLogon API can be used as a backup plan if the Process component fails.

Const LOGON_WITH_PROFILE As Int32 = 1
Const CREATE_NEW_CONSOLE As Int32 = 16
Const CREATE_NEW_PROCESS_GROUP As Int32 = 512
Const CREATE_DEFAULT_ERROR_MODE As Int32 = 67108864
Private Structure PROCESS_INFORMATION
    Public hProcess As Int32
    Public hThread As Int32
    Public dwProcessId As Int32
    Public dwThreadId As Int32
End Structure
Private Structure STARTUPINFO
    Public cb As Int32
    Public lpReserved As Int32
    Public lpDesktop As Int32
    Public lpTitle As Int32
    Public dwX As Int32
    Public dwY As Int32
    Public dwXSize As Int32
    Public dwYSize As Int32
    Public dwXCountChars As Int32
    Public dwYCountChars As Int32
    Public dwFillAttribute As Int32
    Public dwFlags As Int32
    Public wShowWindow As Short
    Public cbReserved2 As Short
    Public lpReserved2 As Byte
    Public hStdInput As Int32
    Public hStdOutput As Int32
    Public hStdError As Int32
End Structure

Private Declare Auto Function apiCreateProcessWithLogon Lib _
"advapi32" Alias "CreateProcessWithLogonW" _
(ByVal lUsername As String, _
ByVal lDomain As String, _
ByVal lPassword As String, _
ByVal dwLgnFlags As Int32, _
ByVal lAppName As String, _
ByVal lCommandLine As String, _
ByVal dwCreationFlags As Int32, _
ByVal lEnv As Int32, _
ByVal lCurDir As String, _
ByRef lStartInf As STARTUPINFO, _
ByRef lProcInfo As PROCESS_INFORMATION) As Int32

Private Declare Function apiCloseHandle Lib "kernel32" _
Alias "CloseHandle" _
(ByVal hObject As Int32) As Int32
[VB675.JPG]

It was a little more cooperative in VB6.

Private Const LOGON_WITH_PROFILE As Long = 1
Private Const CREATE_DEFAULT_ERROR_MODE As Long = &H4000000
Private Const CREATE_NEW_CONSOLE As Long = &H10
Private Const CREATE_NEW_PROCESS_GROUP As Long = &H200
Private Type PROCESS_INFORMATION
    hProcess As Long
    hThread As Long
    dwProcessId As Long
    dwThreadId As Long
End Type
Private Type STARTUPINFO
    cb As Long
    lpReserved As Long
    lpDesktop As Long
    lpTitle As Long
    dwX As Long
    dwY As Long
    dwXSize As Long
    dwYSize As Long
    dwXCountChars As Long
    dwYCountChars As Long
    dwFillAttribute As Long
    dwFlags As Long
    wShowWindow As Integer
    cbReserved2 As Integer
    lpReserved2 As Byte
    hStdInput As Long
    hStdOutput As Long
    hStdError As Long
End Type

Private Declare Function apiCreateProcessWithLogon Lib _
"advapi32" Alias "CreateProcessWithLogonW" _
(ByVal lpUsername As Long, _
ByVal lpDomain As Long, _
ByVal lpPassword As Long, _
ByVal dwLogonFlags As Long, _
ByVal lpApplicationName As Long, _
ByVal lpCommandLine As Long, _
ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As Long, _
ByVal lpCurrentDirectory As Long, _
ByRef lpStartupInfo As STARTUPINFO, _
ByRef lpProcessInfo As PROCESS_INFORMATION) As Long

Private Declare Function apiCloseHandle Lib _
"kernel32" Alias "CloseHandle" _
(ByVal hObject As Long) As Long

Private Function StartProcessLoggedAs _
(ByVal fPath As String, _
ByVal user As String, _
ByVal password As String) As Long
    On Error Resume Next

    Dim StartInfo As STARTUPINFO
    Dim ProcessInfo As PROCESS_INFORMATION
    Dim lpDomain As String
    Dim lpCommandLine As String
    Dim lpCurrentDirectory As String
    lpUsername = user
    lpDomain = ""
    lpPassword = password
    'use the same as lpApplicationName
    lpCommandLine = vbNullString
    'use standard directory
    lpCurrentDirectory = vbNullString
    'initialize structure
    StartInfo.cb = LenB(StartInfo)
    StartInfo.dwFlags = 0
    StartProcessLoggedAs = _
    apiCreateProcessWithLogon _
    (StrPtr(user), StrPtr(lpDomain), StrPtr(password), _
    LOGON_WITH_PROFILE, StrPtr(fPath), _
    StrPtr(lpCommandLine), CREATE_DEFAULT_ERROR_MODE Or _
    CREATE_NEW_CONSOLE Or CREATE_NEW_PROCESS_GROUP, ByVal 0, _
    StrPtr(lpCurrentDirectory), StartInfo, ProcessInfo)
    
    'close the handle to the main thread/process, since we don't use it
    Call apiCloseHandle(ProcessInfo.hThread)
    Call apiCloseHandle(ProcessInfo.hProcess)
End Function

Well, actually there is one more API used to create processes in this sample, but it only applies to the "Winlogon" desktop. This is the systems desktop, that is running before you log into windows at the welcome screen. If you run this sample at login, the WinExec API is used to launch processes with administrative rights, without need for a password. A registry value called "StartExe" points to the executable file, that is to be started at boot time, and is located here:

Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Accessibility\ATs\osk

The switch to turn it on and off, is triggered by changing the value of "Configuration" to "osk" located here:

Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Accessibility\

Secure Desktop Prompting : Always Run As Administrator

Secure Desktop Creation

I made two modules for Desktop related API, that can be used for other applications. One for VB6, and the other for VB.NET. Since it's pretty action packed, I figured I'd just go over the difficult part.

Enumerating Desktops

This was another unexpected hitch to overcome. I found plenty of examples online in VB6 , but unfortunately they just plain didn't work. Without a basic foundation to start from, it wasn't easy to figure out in .NET. Although once I did, I was able to get the VB6 example working finally. It probably did work once upon a time, but somewhere along the way, software changed this API was left in a semi-broken abandoned state until now.

[VB0575.JPG]
Private Declare Function apiCopyMemory Lib _
"kernel32" Alias "RtlMoveMemory" _
(ByVal pDst As String, _
ByVal pSrc As String, _
ByVal ByteLen As Int32) As Int32

Private Declare Function apiGetProcessWindowStation Lib _
"user32" Alias "GetProcessWindowStation" () As Int32

Private Declare Function apiEnumDesktops Lib _
"user32" Alias "EnumDesktopsA" _
(ByVal hSta As Int32, _
ByVal lEnumFunc As EnumDesktopsDelegate, _
ByVal lParam As Int32) As Int32

Private Delegate Function EnumDesktopsDelegate _
(ByVal desktop As String, _
ByVal lParam As Int32) As Boolean

Private Declare Function apiStrLen Lib _
"kernel32" Alias "lstrlenA" _
(ByVal lString As String) As Int32

Public lDesktops As String

Public Function GetDesktops() As String
    apiEnumDesktops _
    (apiGetProcessWindowStation, AddressOf EnumDesktopProc, 0)
    GetDesktops = lDesktops
End Function

Private Function EnumDesktopProc _
(ByVal lDesktop As String, _
ByVal lParam As Int32) As Boolean
    On Error Resume Next

    Dim Buffer As String = Space(apiStrLen(lDesktop))
    apiCopyMemory(Buffer, lDesktop, Len(Buffer))

    Form1.ListBox1.Items.Add(Buffer) 'Update forms listbox

    lDesktops &= Buffer & vbCrLf
    EnumDesktopProc = True
End Function
[VB675.JPG]

To do this in VB6, I had to pass the parameters ByVal in the CopyMemory API, and magically it all came to life.

Option Explicit

Private Declare Function apiCopyMemory Lib _
"kernel32" Alias "RtlMoveMemory" _
(ByRef pDst As String, _
ByRef pSrc As Long, _
ByVal ByteLen As Long) As Long

Private Declare Function apiEnumDesktops Lib _
"user32" Alias "EnumDesktopsA" _
(ByVal hSta As Long, _
ByVal lEnumFunc As Long, _
ByVal lParam As Long) As Long

Private Declare Function apiGetProcessWindowStation Lib _
"user32" Alias "GetProcessWindowStation" () As Long

Private Declare Function apiStrLen Lib _
"kernel32" Alias "lstrlenA" _
(ByVal lString As Long) As Long

Public lDesktops As String

Public Function GetDesktops() As String
    Call apiEnumDesktops _
    (apiGetProcessWindowStation, AddressOf EnumDesktopProc, 0)
    GetDesktops = lDesktops
End Function

Private Function EnumDesktopProc _
(ByVal lDesktop As Long, _
ByVal lParam As Long) As Long
    On Error Resume Next
    
    Dim Buffer As String
    Buffer = Space(apiStrLen(lDesktop)) 'Create buffer
    'Call copy memory correctly with ByVal in ByRef params/Uhgg
    Call apiCopyMemory(ByVal Buffer, ByVal lDesktop, Len(Buffer))
    
    Form1.List1.AddItem (Buffer) 'Update list on form
    
    lDesktops = lDesktops & Buffer & vbCrLf 'Append string of desktops
    EnumDesktopProc = 1 'Return complete
End Function

Secure Desktop Prompting : Always Run As Administrator

Self Elevation

[shield32.PNG]

If you find that your program needs to elevate for a particular operation, but not that frequently, then you probably want to self elevate like microsoft programs do. Here is one possible approach using the verb of your executable to ask for elevation.

[VB0575.JPG]
Imports System.Runtime.InteropServices'UP TOP

Const TOKEN_QUERY As Int32 = 8
Public Structure TOKEN_ELEVATION
    Public TokenIsElevated As Int32
End Structure

Public Enum TOKEN_INFORMATION_CLASS
    TokenUser = 1
    TokenGroups = 2
    TokenPrivileges = 3
    TokenOwner = 4
    TokenPrimaryGroup = 5
    TokenDefaultDacl = 6
    TokenSource = 7
    TokenType = 8
    TokenImpersonationLevel = 9
    TokenStatistics = 10
    TokenRestrictedSids = 11
    TokenSessionId = 12
    TokenGroupsAndPrivileges = 13
    TokenSessionReference = 14
    TokenSandBoxInert = 15
    TokenAuditPolicy = 16
    TokenOrigin = 17
    TokenElevationType = 18
    TokenLinkedToken = 19
    TokenElevation = 20
    TokenHasRestrictions = 21
    TokenAccessInformation = 22
    TokenVirtualizationAllowed = 23
    TokenVirtualizationEnabled = 24
    TokenIntegrityLevel = 25
    TokenUIAccess = 26
    TokenMandatoryPolicy = 27
    TokenLogonSid = 28
    MaxTokenInfoClass = 29
End Enum

Private Declare Function apiOpenProcessToken Lib _
"advapi32" Alias "OpenProcessToken" _
(ByVal ProcessHandle As Int32, _
ByVal DesiredAccess As Int32, _
ByRef TokenHandle As Int32) As Boolean

Private Declare Function apiGetTokenInformation Lib _
"advapi32" Alias "GetTokenInformation" _
(ByVal TokenHandle As Int32, _
ByVal TokenInformationClass As TOKEN_INFORMATION_CLASS, _
ByVal TokenInformation As Int32, _
ByVal TokenInformationLength As Int32, _
ByRef ReturnLength As Int32) As Boolean

Private Declare Function apiCloseHandle Lib _
"kernel32" Alias "CloseHandle" _
(ByVal hObject As Int32) As Boolean

Private Declare Function apiShellExecute Lib _
"shell32" Alias "ShellExecuteA" _
(ByVal hwnd As Int32, _
ByVal lOperation As String, _
ByVal lFile As String, _
ByVal lParam As String, _
ByVal lDir As String, _
ByVal nShow As Int32) As Int32

Private Sub ElevateSelf()
    On Error Resume Next
    'vista or above and not elevated 
    If IsVistaAbove() = True AndAlso IsElevated() = False Then
        'do file verb to run another instance
        ' of this application as administrator
        DoVerb(Application.StartupPath, _
        Process.GetCurrentProcess.ProcessName & ".exe", _
        "run as administrator")
        Environment.Exit(0) '
    End If
End Sub

Private Function DoVerb _
  (ByVal dirName As String, _
  ByVal filName As String, _
  ByVal sVerb As String) As Int32
    On Error Resume Next

    'If blank assume this current directory
    If dirName = "" Then dirName = Application.StartupPath

    'Create a new vbscript file and name it the verb
    FileOpen(1, dirName & "\" & sVerb.Replace("&", "") & ".vbs", _
    OpenMode.Output, OpenAccess.Write, OpenShare.Shared)

    'Print out the scripts contents
    PrintLine(1, _
    "On Error Resume Next")
    PrintLine(1, _
    "Set objShell = CreateObject(" & Chr(34) & _
    "Shell.Application" & Chr(34) & ")")
    PrintLine(1, _
    "Set objFolder = objShell.Namespace(" & _
    Chr(34) & dirName & Chr(34) & ")")
    PrintLine(1, _
    "Set objFolderItem = objFolder.ParseName(" & _
    Chr(34) & filName & Chr(34) & ")")
    PrintLine(1, _
    "Set objVerbs = objFolderItem.verbs")
    PrintLine(1, _
    "For Each objVerb In objVerbs")
    PrintLine(1, _
    "If LCase(RePlace(objVerb.Name, " & _
    Chr(34) & Chr(38) & Chr(34) & ", " & Chr(34) & _
    Chr(34) & ")) = " & Chr(34) & _
    LCase(Replace(sVerb, "&", "")) & Chr(34) & " Then")
    PrintLine(1, "objVerb.DoIt")
    PrintLine(1, "End If")
    PrintLine(1, "Next")
    FileClose(1)

    'Give the script a moment to exist
    For i As Int32 = 1 To 30
        Threading.Thread.Sleep(100)
        If IO.File.Exists _
        (dirName & "\" & sVerb.Replace("&", "") & ".vbs") = True Then
            Threading.Thread.Sleep(200)
            Exit For
        End If
    Next

    'If it was not created then abort function
    If IO.File.Exists _
    (dirName & "\" & sVerb.Replace("&", "") & ".vbs") = False Then
        Exit Function
    End If

    'Open script with ShellExecute in the directory
    DoVerb = apiShellExecute _
    (0, "open", dirName & "\" & sVerb.Replace("&", "") & ".vbs", _
    vbNullString, dirName, 1)

    Threading.Thread.Sleep(400)

    If IO.File.Exists _
    (dirName & "\" & sVerb.Replace("&", "") & ".vbs") = True Then
        Kill(dirName & "\" & sVerb.Replace("&", "") & ".vbs")
    End If
End Function

Public Function IsVistaAbove() As Boolean
    Return (System.Environment.OSVersion.Version.Major >= 6)
End Function

Public Function IsElevated() As Boolean
    Dim bRetVal As Boolean = False
    Dim hToken As Int32 = 0
    Dim hProcess As Int32 = Process.GetCurrentProcess.Handle.ToInt32
    If hProcess = 0 Then Return False
    If apiOpenProcessToken(hProcess, TOKEN_QUERY, hToken) = False Then Return False
    Try
        Dim te As TOKEN_ELEVATION
        te.TokenIsElevated = 0
        Dim dwReturnLength As Int32 = 0
        Dim teSize As Int32 = Marshal.SizeOf(te)
        Dim tePtr As IntPtr = Marshal.AllocHGlobal(teSize)
        Try
            Marshal.StructureToPtr(te, tePtr, True)
            bRetVal = apiGetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenElevation, tePtr.ToInt32, teSize, dwReturnLength)
            If bRetVal = True AndAlso teSize = dwReturnLength Then
                te = DirectCast(Marshal.PtrToStructure(tePtr, GetType(TOKEN_ELEVATION)), TOKEN_ELEVATION)
            Else
                'error
            End If
        Finally
            Marshal.FreeHGlobal(tePtr)
        End Try
        Return (te.TokenIsElevated <> 0)
    Finally
        apiCloseHandle(hToken)
    End Try
End Function


Secure Desktop Prompting : Always Run As Administrator

Encrypt Strings

You'll probably want to implement your own custom encryption technique, however I've provided a basic one that is fairly powerful.

[VB0575.JPG]
Imports System.Text 'UP TOP
Imports System.Collections.Specialized
Imports System.Security.Cryptography
Imports system.Runtime.InteropServices

'Change to a unique value ahead of time!!!
Const lscryptoKey As String = "CryptKey" '!!!
Private lVector() As Byte = {239, 8, 55, 34, 0, 144, 224, 62}

Private Function sDecrypt(ByVal sQuery As String) As String
    Dim lBuffer() As Byte
    Dim lCryptoClass As New TripleDESCryptoServiceProvider
    Dim lCryptoProvider As New MD5CryptoServiceProvider
    sDecrypt = ""
    Try
        lBuffer = Convert.FromBase64String(sQuery)
        lCryptoClass.Key = lCryptoProvider.ComputeHash _
        (ASCIIEncoding.ASCII.GetBytes(lscryptoKey))
        lCryptoClass.IV = lVector
        Return Encoding.ASCII.GetString _
        (lCryptoClass.CreateDecryptor().TransformFinalBlock _
        (lBuffer, 0, lBuffer.Length()))
    Catch ex As Exception

    Finally
        lCryptoClass.Clear()
        lCryptoProvider.Clear()
        lCryptoClass = Nothing
        lCryptoProvider = Nothing
    End Try
End Function

Private Function sEncrypt(ByVal sInput As String) As String
    Dim lCryptoClass As New TripleDESCryptoServiceProvider
    Dim lCryptoProvider As New MD5CryptoServiceProvider
    Dim lBuffer() As Byte
    sEncrypt = ""
    Try
        lBuffer = System.Text.Encoding.ASCII.GetBytes(sInput)
        lCryptoClass.Key = lCryptoProvider.ComputeHash _
        (ASCIIEncoding.ASCII.GetBytes(lscryptoKey))
        lCryptoClass.IV = lVector
        sInput = Convert.ToBase64String _
        (lCryptoClass.CreateEncryptor().TransformFinalBlock _
        (lBuffer, 0, lBuffer.Length()))
        sEncrypt = sInput
    Catch ex As CryptographicException

    Catch ex As FormatException

    Catch ex As Exception

    Finally
        lCryptoClass.Clear()
        lCryptoProvider.Clear()
        lCryptoClass = Nothing
        lCryptoProvider = Nothing
    End Try
End Function

[VB675.JPG]

I'm sorry VB6'ers, but you'll have to find your own encryption routines to hide passwords from malware.

Secure Desktop Prompting : Always Run As Administrator

Security concerns

This application demonstrates how easily it would be for a malware to use your standard account, to prompt and elevate privileges forever. This is in stark contrast to the information at the following link: TechNet

Quote: It is worthwhile to note that malware can paint over the interactive desktop and present an imitation of the secure desktop, but when the setting is set to prompt for approval the malware does not gain elevation should the user be tricked into clicking Continue on the imitation. If the setting is set to prompt for credentials, malware imitating the credential prompt may be able to gather the credentials from the user. >Note that this does also does not gain malware elevated privilege and that the system has other protections that mitigate malware from automated driving of user interface even with a harvested password.

There is no need to automate an interface with the CreateProcessWithLogon API. There also seems to be a typo at this line:
-{Note that this does also does not...}
This may or may not be a typo, or copy/pasto mistake, but it's curious that it would just so happen to end up directly on the word at which microsoft admits that malware can spoof a UAC dialog, and harvest(phish)the users passwords.

It gets even more jumbled with the following non-relevant information...

Quote: While malware could present an imitation of the secure desktop, this issue cannot occur unless a user previously installed the malware. Because processes requiring an administrator access token cannot silently install when UAC is enabled, the user must explicitly provide consent by clicking Continue or by providing administrator credentials.

The point is that this process does not need administrator access. Moreover, there are several ways to paint over the desktop that do not require admin privileges. I'm not sure what this guy is talking about.

So, I went and did some more research about the origins of the UAC. I came across an interesting msdn blog. MSDN blog

So now when the user mouses over the elevation UI attempting to cancel it since the malicious software could brazenly announce itself as "I'm gonna own your PC.exe", what's really happening is that the hot spot of the mouse is invisibly over the "Allow" button. Click! Not what you thought would happen. This type of attack is also blocked on the Secure Desktop. This obviously begs the question of why we don't provide this type of protection to ALL applications and windows running on the operating system. ... in a nutshell since the normal User Desktop is meant for running all types of applications - most of which do not need exceptional privilege to run normally - Windowsb" provides a platform where these applications can do whatever is needed to work, including drawing their UI on the screen. ... But like any tool, these can be misused by software with malicious intent, including unknown malware that may be on your PC. In a perfect world we would love....

To make a safer place for administrative programs to execute?

It appears that people knew about CreateProcessWithLogonW back then..

You're mistaken -- UAC cannot stop a process that knows the administrator password from acting as an adminstrator. Once it knows the password, a process running in a standard privilege account can use CreateProcessWithLogonW() (the same API that is used to implement "Run As"), to run a subprocess with administrator privileges, which will not be subject to UAC. -Anonymous?

CreateProcessWithLogonW() is subject to the UAC functionality and will create the new process with a "UAC" access token. -Jenn

Nope -- we've blocked programmatic elevation at the API level (and at the network level). Actually, it is programmatically impossible to use a password to elevate now (short of running as SYSTEM). If there end up being any holes here, that's a bug and we'd definitely like to hear about it :) -Jonathan Schwartz

Well that's interesting, because it doesn't appear to be programmatically impossible. That API, can be declared in at least two different ways that work. Maybe he was speaking about Sun Microsystems, and it's interaction at the API level.

We are aware that there are currently some drivers who handle the desktop switch better than others. We have teams in Windows working to get the drivers to handle this better across the board. Our goal is to have this switch look very fast and seamless by the time we ship. -Jim

I wonder what happened.
This application switches very fast comparatively.


Conclusion

Microsoft should seriously consider using a variation of this kind of approach, to allow user/admin customization of the UAC.
It's simple to use without compromising security significantly, because Windows already has the credentials saved.

The upshot of a microsoft program like this one, would be that there would end up being less UAC prompts for common tasks as permitted by the user/admin, and the remaining prompts will face more scrutiny as a possible spoof.

The quick fix for microsoft, is to disallow standard user accounts if the administrator account is active, or at least warn them that the standard account is at a high risk of being spoofed. In most cases the admin or parent would be the ones to insert the credentials, so they deserve to know that the UAC is not fully trustable.

If this saves the life of one child, and the torment of a parent, is it not worth it?

With this articles sample, a malware would have to be already installed, and looking for the obscured file name, info, and location on disk. Then it would have to decrypt the obscured configuration file, to obtain the administrators password, which would be fairly difficult.

I suppose I should also mention the obvious, that changing the Administrators password renders all authorizations useless. That could be used as a helpful toggle, where you could authorize the session at any time by flipping your password back and forth, using the "net user" command line in code. Prompted and elevated ofcourse.

In the rare case that there is a breach somehow, it's the admin that has already made a choice to permanently run as admin, which is consistent with the current UAC philosophy of admin approval. Although I think that there are many things that can be done to increase security beyond this simple example. It's seems more fool-proof than windows is spoof-proof, as it is now.

{Disclaimer}
In conclusion, the most vulnerable users, are standard users on a computer that has the administrator account enabled.
It's highly recommended that Vista and Windows 7 PCs use a split token user/admin account, if the Administrator account is going to be enabled. Then you avoid ever seeing a password window that could be spoofed, and the remaining UAC prompts, would be by programs that you have not made permanent, and are proportionately more likely to be a malware, that will be stopped. A standard account is recommended for windows 2k/XP, but the "Run As" window must be avoided, and this application used instead.

Administrators can optionaly launch sensitive applications in an even more secure desktop environment, instead of running them on the default desktop. Microsoft, just doesn't have this yet, but it makes complete sense, that in the future admin approved applications should be encouraged to run on a secure-ish desktop.

Q. After completing this relatively easy program I had to ask myself why microsoft condones excessive prompting when it can be spoofed.
A. David Cross, a product unit manager at Microsoft, stated during the RSA Conference 2008 that UAC was in fact designed to "annoy users," and force independent software vendors to make their programs more secure so that UAC prompts would not be triggered.

If that continues to be the circular reasoning and intent, then I am annoyed with the "one size fits all" approach,
and Windows 8 will not have been my idea.



About the Author

Shane Findley

Developer of applications for use in number theory.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds