├── Release └── rpau.zip ├── Screenshots ├── Syntax_1a.png ├── Syntax_1b.png └── Syntax_2.png ├── README.md └── Source └── rpau.pb /Release/rpau.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgeraghty/RunProcessAsUser-ScreenConnect/HEAD/Release/rpau.zip -------------------------------------------------------------------------------- /Screenshots/Syntax_1a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgeraghty/RunProcessAsUser-ScreenConnect/HEAD/Screenshots/Syntax_1a.png -------------------------------------------------------------------------------- /Screenshots/Syntax_1b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgeraghty/RunProcessAsUser-ScreenConnect/HEAD/Screenshots/Syntax_1b.png -------------------------------------------------------------------------------- /Screenshots/Syntax_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgeraghty/RunProcessAsUser-ScreenConnect/HEAD/Screenshots/Syntax_2.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RunProcessAsUser-ScreenConnect 2 | Built as an accessory to ConnectWise ScreenConnect Remote Administration Software. Allows commands via the web interface to NT AUTHORITY\SYSTEM Command Prompt, to be run in the Active Console Session. (Windows Guests Only) 3 | 4 | Background/Goal: 5 | On the Host page of the web interface, input sent to the "Commands" Tab of any Windows Guest, is executed on the machine via the ScreenConnect service, which results in the command being executed as the SID "NT Authority\SYSTEM". This is not a problem, however I wanted to also be able to send commands to the Guest and have them be executed within the interactive user session (Console Session) of the machine, as the user who is currently logged in to that session, whoever that might be at that point. I found that from time to time, I needed to execute certain commands as the user to get the desired result, but was not able to without connecting into the session and taking physical control of the user's session / using the toolbox. 6 | 7 | 8 | Description: 9 | The console program I wrote to accomplish this is called "rpau.exe" (Run Process As User). It was written in PureBasic 5.41, and accomplishes this goal with very small footprint on the system (19kb). I placed a copy of it in the C:\Windows directory (PATH) to be able to run it from the Command Tab by simply calling it by name: 10 | 11 | C:\Windows\system32>rpau 12 | 13 | Error: No Options / Parameters Given. 14 | 15 | [Run Process As User] v0.32.131 16 | 17 | Syntax1: rpau.exe --hidden "[ProgramName]" "[CmdLineArgs]" "[WorkingDirectory]" 18 | : You can optionally include "--hidden" to visibly hide the executed 19 | : program. Enclose parameters in double-quotes as shown. 20 | : The only required parameter is [ProgramName]. However, if you want 21 | : to specify [WorkingDirectory] without specifying [CmdLineArgs], 22 | : [CmdLineArgs] must still be passed as: "". (Empty String). 23 | 24 | Example: rpau.exe "cmd.exe" "/c ipconfig > test.txt" "C:\ProgramData" 25 | 26 | 27 | Syntax2: rpau.exe --username 28 | : Returns the Username of the account in the Console Session. No 29 | : Additional Parameters Expected. 30 | 31 | Example: rpau.exe --username 32 | 33 | There are currently 2 "modes" it works in. The program can only be run from the SYSTEM account, and will do nothing except exit if launched as a normal user. (Security/Redundant Use Case) 34 | - Syntax 1 is for running a process as the user in their session, with the option of suppressing the visibility of any windows with --hidden. ('--hidden' option is for running things like batch scripts, etc. without visually interrupting the user, or running the risk of stealing their window focus.) It is basically a command-line interface to the CreateProcess() API (ProgramName, Arguements, WorkingDirectory can be specified), while impersonating the logged on user in an environment block. 35 | - Syntax 2 returns printed information to the Command Tab, specifically the username of the account which is logged in to the console session. ScreenConnect already provides this information, but it's just there for convenience. 36 | 37 | (See Screenshots Folder for Examples) 38 | 39 | 40 | Limitations/ToDo: 41 | - Currently, does not support Terminal-Services style configurations (More than one concurrent console session). I plan to add an option to enumerate the WTS Sessions, and to select which one specifically to direct the commands to, but haven't had time to yet. 42 | - Plan to add other options to perform other actions as the user. -------------------------------------------------------------------------------- /Source/rpau.pb: -------------------------------------------------------------------------------- 1 | Prototype protoWTSGetActiveConsoleSessionId() : Global WTSGetActiveConsoleSessionId.protoWTSGetActiveConsoleSessionId ; Prototype for WTSGetActiveConsoleSessionId() @ kernel32.dll 2 | Prototype.b protoWTSQueryUserToken(SessionId, phToken) : Global WTSQueryUserToken.protoWTSQueryUserToken ; Prototype for WTSQueryUserToken() @ wtsapi32.dll 3 | Prototype.b protoCreateEnvironmentBlock(lpEnvironment, hToken, bInherit.b) : Global CreateEnvironmentBlock.protoCreateEnvironmentBlock ; Prototype for CreateEnvironmentBlock() @ userenv.dll 4 | Prototype.b protoDestroyEnvironmentBlock(lpEnvironment) : Global DestroyEnvironmentBlock.protoDestroyEnvironmentBlock ; Prototype for DestroyEnvironmentBlock() @ userenv.dll 5 | 6 | Global ArgC.i = CountProgramParameters() ; Total Arguement Count 7 | Global sParamC.i = 0 ; Count of Arguements that are not --options (String Values) 8 | Global NewList Params.s() ; String List to hold Parameters / Arguements. 9 | Global RunProgramHidden.i = #False ; Default Option To Run As User With All Windows Hidden. 10 | Global ReturnUserName$ = "" ; Store the username, if it is recorded. 11 | 12 | Procedure RPAU_(lpApplicationName.s, lpCommandLine.s = #Null$, lpCurrentDirectory.s = #Null$, Hide.b = #False, Mode$ = "NORMAL") ; Run Process As User in Session 13 | PrintN("") 14 | kernel32 = OpenLibrary(#PB_Any, "kernel32.dll") ; Open Requred DLLs, Assign Functions 15 | If IsLibrary(kernel32) 16 | WTSGetActiveConsoleSessionId = GetFunction(kernel32, "WTSGetActiveConsoleSessionId") 17 | Else 18 | PrintN("Error: Failed to access 'kernel32.dll'.") 19 | EndIf 20 | wtsapi32 = OpenLibrary(#PB_Any, "wtsapi32.dll") 21 | If IsLibrary(wtsapi32) 22 | WTSQueryUserToken = GetFunction(wtsapi32, "WTSQueryUserToken") 23 | Else 24 | PrintN("Error: Failed to access 'wtsapi32.dll'.") 25 | EndIf 26 | userenv = OpenLibrary(#PB_Any, "userenv.dll") 27 | If IsLibrary(userenv) 28 | CreateEnvironmentBlock = GetFunction(userenv, "CreateEnvironmentBlock") 29 | DestroyEnvironmentBlock = GetFunction(userenv, "DestroyEnvironmentBlock") 30 | Else 31 | PrintN("Error: Failed to access 'userenv.dll'.") 32 | EndIf 33 | Result.b = #False ; Success 34 | dwSessionId = WTSGetActiveConsoleSessionId() ; Get SessionID of User 35 | If dwSessionId 36 | If WTSQueryUserToken(dwSessionId, @TokenHandle) ; Get User Token 37 | #SecurityImpersonation = 2 38 | #TokenPrimary = 1 39 | If DuplicateTokenEx_(TokenHandle, #MAXIMUM_ALLOWED, #Null, #SecurityImpersonation, #TokenPrimary, @ImpersonateToken) ; Duplicate Token 40 | If ImpersonateLoggedOnUser_(ImpersonateToken) ; Impersonate User 41 | If CreateEnvironmentBlock(@pEnvironment, ImpersonateToken, #False) ; Create Environment Block As User 42 | Select Mode$ 43 | Case "NORMAL" 44 | #CREATE_UNICODE_ENVIRONMENT = $400 45 | dwCreationFlag = #NORMAL_PRIORITY_CLASS | #CREATE_NEW_CONSOLE | #CREATE_UNICODE_ENVIRONMENT 46 | si.STARTUPINFO 47 | ZeroMemory_(@si, SizeOf(STARTUPINFO)) 48 | si\cb = SizeOf(STARTUPINFO) 49 | si\lpDesktop = @"WinSta0\Default" 50 | si\dwFlags = #STARTF_USESHOWWINDOW 51 | Select Hide ; Option to Run Program Hidden 52 | Case #True 53 | si\wShowWindow = #SW_HIDE 54 | Case #False 55 | si\wShowWindow = #SW_SHOWNORMAL 56 | EndSelect 57 | pi.PROCESS_INFORMATION ; Struct to catch resulting Process Information 58 | ZeroMemory_(@pi, SizeOf(PROCESS_INFORMATION)) 59 | Result = CreateProcessAsUser_(ImpersonateToken, lpApplicationName, lpCommandLine, #Null, #Null, #False, dwCreationFlag, pEnvironment, lpCurrentDirectory.s, @si, @pi) ; Create Process As User 60 | Case "USERNAME" 61 | ReturnUserName$ = UserName() 62 | EndSelect 63 | SetLastError_(0) 64 | DestroyEnvironmentBlock(pEnvironment) ; End Environment Block As User 65 | Else 66 | PrintN("Error: Failed to create Environment Block.") 67 | EndIf 68 | RevertToSelf_() 69 | Else 70 | PrintN("Error: Failed to impersonate Logged On User.") 71 | EndIf 72 | CloseHandle_(ImpersonateToken) 73 | Else 74 | PrintN("Error: Failed to duplicate User Token.") 75 | EndIf 76 | CloseHandle_(TokenHandle) 77 | Else 78 | PrintN("Error: Failed to query User Token.") 79 | EndIf 80 | Else 81 | PrintN("Error: Failed to get Active Console Session ID.") 82 | EndIf 83 | Select Mode$ 84 | Case "NORMAL" 85 | Select Result ; Check Results 86 | Case #True 87 | PrintN("The Program Was Executed In Session: "+Str(dwSessionId)) 88 | Default 89 | PrintN("The Program Failed To Execute. Please Check Syntax of Commandline Text.") 90 | EndSelect 91 | Case "USERNAME" 92 | PrintN("Console Session["+Str(dwSessionId)+"] UserName: " + ReturnUserName$) 93 | EndSelect 94 | If IsLibrary(userenv) : CloseLibrary(userenv) : EndIf ; Close DLLs 95 | If IsLibrary(wtsapi32) : CloseLibrary(wtsapi32) : EndIf 96 | If IsLibrary(kernel32) : CloseLibrary(kernel32) : EndIf 97 | EndProcedure 98 | 99 | Procedure PrintVersion() 100 | PrintN("") 101 | PrintN(" [Run Process As User] v0."+Str(#PB_Editor_BuildCount)+"."+Str(#PB_Editor_CompileCount)) 102 | PrintN("") 103 | EndProcedure 104 | 105 | Procedure PrintHelp() 106 | PrintVersion() 107 | PrintN("Syntax1: rpau.exe --hidden "+#DQUOTE$+""+#DQUOTE$+" "+#DQUOTE$+""+#DQUOTE$+" "+#DQUOTE$+""+#DQUOTE$) 108 | PrintN(" : You can optionally include "+#DQUOTE$+"--hidden"+#DQUOTE$+" to visibly hide the executed") 109 | PrintN(" : program. Enclose parameters in double-quotes as shown.") 110 | PrintN(" : The only required parameter is . However, if you want") 111 | PrintN(" : to specify without specifying ,") 112 | PrintN(" : must still be passed as: "+#DQUOTE$+#DQUOTE$+". (Empty String).") 113 | PrintN("") 114 | PrintN("Example: rpau.exe "+#DQUOTE$+"cmd.exe"+#DQUOTE$+" "+#DQUOTE$+"/c ipconfig > test.txt"+#DQUOTE$+" "+#DQUOTE$+"C:\ProgramData"+#DQUOTE$) 115 | PrintN("") 116 | PrintN("") 117 | PrintN("Syntax2: rpau.exe --username") 118 | PrintN(" : Returns the Username of the account in the Console Session. No ") 119 | PrintN(" : Additional Parameters Expected.") 120 | PrintN("") 121 | PrintN("Example: rpau.exe --username") 122 | PrintN("") 123 | EndProcedure 124 | 125 | Procedure Main() 126 | OpenConsole() 127 | PrintN("") 128 | If ArgC >= 1 129 | For I = 1 To ArgC 130 | CurrentParam$ = ProgramParameter() 131 | Select Left(CurrentParam$, 2) 132 | Case "--" 133 | Select UCase(Right(CurrentParam$, Len(CurrentParam$) - 2)) 134 | Case "HIDDEN" 135 | PrintN("'--hidden' Switch Detected, Program Output Will Be Suppressed...") 136 | RunProgramHidden = #True 137 | Case "USERNAME" 138 | RPAU_(#Null$, #Null$, #Null$, #False, "USERNAME") 139 | End 140 | Default 141 | PrintN("Error: Unknown Switch: '"+CurrentParam$+"'. Aborting...") 142 | PrintHelp() 143 | End 144 | EndSelect 145 | Default 146 | sParamC + 1 147 | AddElement(Params()) 148 | If CurrentParam$ = "" : CurrentParam$ = #Null$ : EndIf 149 | Params() = CurrentParam$ 150 | EndSelect 151 | Next 152 | FirstElement(Params()) 153 | Param1$ = "" : Param2$ = "" : Param3$ = "" 154 | Select sParamC 155 | Case 0 156 | PrintN("Error: No Parameters Given.") 157 | PrintHelp() 158 | End 159 | Case 1 160 | Param1$ = Params() 161 | PrintN("") 162 | PrintN("Attempting To Run: " + #DQUOTE$ + Param1$ + #DQUOTE$) 163 | RPAU_(Param1$, #Null$, #Null$, RunProgramHidden, "NORMAL") 164 | Case 2 165 | Param1$ = Params() 166 | NextElement(Params()) 167 | Param2$ = Params() 168 | PrintN("") 169 | PrintN("Attempting To Run: " + #DQUOTE$ + Param1$ + " " + Param2$ + #DQUOTE$) 170 | RPAU_(Param1$, Param2$, #Null$, RunProgramHidden, "NORMAL") 171 | Case 3 172 | Param1$ = Params() 173 | NextElement(Params()) 174 | Param2$ = Params() 175 | NextElement(Params()) 176 | Param3$ = Params() 177 | PrintN("") 178 | PrintN("Attempting To Run: " + #DQUOTE$ + Param1$ + " " + Param2$ + #DQUOTE$ + " in Directory: " + #DQUOTE$ + Param3$ + #DQUOTE$) 179 | RPAU_(Param1$, Param2$, Param3$, RunProgramHidden, "NORMAL") 180 | Default 181 | PrintN("Error: Too Many Parameters, Cannot Be Greater Than 3") 182 | PrintHelp() 183 | End 184 | EndSelect 185 | Else 186 | PrintN("Error: No Options / Parameters Given.") 187 | PrintHelp() 188 | End 189 | EndIf 190 | EndProcedure 191 | 192 | Select UserName() 193 | Case "SYSTEM" 194 | Main() 195 | Default 196 | End 197 | EndSelect 198 | ; IDE Options = PureBasic 5.41 LTS (Windows - x86) 199 | ; ExecutableFormat = Console 200 | ; CursorPosition = 100 201 | ; Folding = w 202 | ; EnableThread 203 | ; Executable = rpau.exe 204 | ; DisableDebugger 205 | ; EnableCompileCount = 132 206 | ; EnableBuildCount = 33 --------------------------------------------------------------------------------