windows core run command with elevated privileges - windows

There are a few options for standard user to run as Administrator (or any another user), however, even when logged as Administrator, some functions requires to run 'elevated'.
On a windows gui, just right click a .exe and select run as Administrator or even elevate 'cmd' or 'powershell'.
How can you get elevated privileges on Windows core?

Generally, to programmatically invoke an executable with elevation (Run as Administrator) on Windows, use the Start-Process cmdlet with -Verb RunAs.
This applies equally to pwsh.exe, the PowerShell Core executable, so that in the simplest case you can write:
# Open a new console window with PowerShell Core running with admin privileges.
Start-Process -Verb RunAs pwsh
If you wanted to wrap that in a convenience function that is also more robust and cross-edition on Windows (also works in Windows PowerShell):
Note: See the bottom section for a more sophisticated function, downloadable from a Gist, which notably also allows passing commands to execute in the elevated PowerShell session.
function Enter-AdminPSSession {
Start-Process -Verb RunAs (Get-Process -Id $PID).Path
}
# Optionally also define a short alias name:
# Note: 'psa' is a nonstandard alias name; a more conformant name would be
# the somewhat clunky 'etasn'
# ('et' for 'Enter', 'a' for admin, and 'sn'` for session), analogous
# to built-in 'etsn' alias referring to 'Enter-PSSession'
Set-Alias psa Enter-AdminPSSession
If you want the function to also be cross-platform (to also work on Unix-like platforms):
function Enter-AdminPSSession {
if ($env:OS -eq 'Windows_NT') {
Start-Process -Verb RunAs (Get-Process -Id $PID).Path
} else {
sudo (Get-Process -Id $PID).Path
}
}
Important: Due to the cmdlets / utilities involved,
on Windows, the new session invariably opens in a new console window.
The fact that the new session is an admin session is reflected in its window's title (prefix Administrator: )
on Unix (Linux, macOS), the new session invariably opens in the same console (terminal) window.
On Unix there is no obvious indicator that an admin session has been entered; running whoami is a quick way to test for that (returns root in an admin session); a better solution would be to modify the prompt function to reflect an admin session in the prompt string, as the prepackage solution discussed next does.
If you additionally want the ability to run commands in the new session and optionally auto-close it, much more work is needed:
You can download function Enter-AdminPSSession from this Gist, which:
enables passing commands to execute via a script block ({ ... })
keeps the session open by default, so that command output can be inspected, but you can opt-out with -Exit or -ExitOnSuccess (close the session only if no error occurred).
tries to reflect overall success of the commands passed via $LASTEXITCODE (even for PowerShell-native commands this variable is normally not set); 0 indicates success.
ensures that the calling session's current location (working directory) is also the elevated session's current location.
allows you to opt out of loading the profiles, with -NoProfile
prefixes the prompt string in interactive elevated sessions with [admin] , on all platforms.
Assuming you have looked at the linked Gist's source code to ensure that it is safe (which I can personally assure you of, but you should always check), you can install Enter-AdminPSSession directly as follows:
irm https://gist.github.com/mklement0/f726dee9f0d3d444bf58cb81fda57884/raw/Enter-AdminPSSession.ps1 | iex
Example calls (which assume that Set-Alias psa Enter-AdminPSSession has been called):
Enter an interactive elevated session:
psa
Windows: Enter an elevated session without loading profiles and set the all-users execution policy, then exit if that succeeded.
psa -NoProfile -ExitOnSuccess { Set-ExecutionPolicy -Scope LocalMachine RemoteSigned }
Unix: Gets the content of file /etc/sudoers (which can only be read with administrative privileges), then exits:
psa -Exit { Get-Content /etc/sudoers }

Related

Unable to capture output from CMD

Trying to open CMD in elevated mode and running powershell script.
powershell -Command "Start-Process cmd \"/k "PowerShell -NoProfile -ExecutionPolicy Bypass -file ""C:\WIM_Creation.ps1 ""hello"" V7.0E V9.0A""\" -Verb RunAs"
Note: Problem is in order to open CMD in elevated mode from current CMD window it redirect to new window in elevated mode and run the command. due to which output is not getting captured in primary CMD window. HENCE NOT GETTING OUTPUT CAPTURED IN JENKINS.
Need to open elevated CMD in current CMD.
An elevated process launched from a non-elevated one invariably runs in a new window.
A non-elevated process that launches an elevated one cannot capture its output.
The workaround is to make the elevated process itself capture its output, by redirecting it to a file.
Unless the program running in the elevated process itself happens to support that, you must call it via a shell, i.e. launch the shell elevated, and pass it a command line that invokes the target executable with a shell redirection (>)
To make your code work:
You need to add -Wait to your Start-Process -Verb RunAs call to ensure that it waits for the elevated process to terminate.
This precludes using cmd /k in automated execution, as it would create a cmd.exe session that stays open until closed by the user.
While you could use cmd /c, there's no reason to create another cmd.exe process - just call the nested powershell instance directly.
*>$env:TEMP\out.txt is used to capture the elevated powershell instance's output in a temporary file, whose content is then output by the outer (non-elevated) instance.
Note:
The file's content is only output after the elevated process has exited, so you won't get realtime feedback; while implementing the latter is possible, it would significantly complicate the solution.
Using fixed file name out.txt creates the potential for name collisions; ruling those out would require more work.
powershell -Command "Start-Process -Verb RunAs -Wait powershell '-NoProfile -ExecutionPolicy Bypass -file C:\WIM_Creation.ps1 \"hello\" V7.0E V9.0A *>$env:TEMP\out.txt'; Get-Content $env:TEMP\out.txt; Remove-Item $env:TEMP\out.txt"
Note that I've made some corrections to the use of " quotes in your original command, based on what I think you're trying to do.

get administrator privileges in PowerShell (Windows Terminal) from without running it as an administrator

Even if you are a authoritative user and you need to do something that requires extended privileges, I have to run the terminal again by right clicking it as choosing "Run as Administrator" unlike in Linux and other operating systems where we can take help of "su" or "sudo".
My question is : Is there any way to get the same terminal window as a administrator one?
To programmatically start an elevated new PowerShell session (with administrative privileges) on Windows - invariably in a new window - from an existing session, use:
Note:
The command below invariably opens the elevated PowerShell instance in an - invariably new - regular console window (conhost.exe) - see next section for use from Windows Terminal.
Start-Process -Verb RunAs (Get-Process -Id $PID).Path
The above works in both PowerShell editions and uses the same executable that is running the current session; you can take a shortcut if you know the executable name and can assume it to be the first in $env:PATH when invoked by name only; for Windows PowerShell:
Start-Process -Verb RunAs powershell
and for PowerShell (Core) 7+:
Start-Process -Verb RunAs pwsh
See this answer for convenience functions, including for cross-platform use and the ability to pass commands to execute in the elevated session.
To open the elevated session in Windows Terminal (also invariably in a new window):
# As above, 'powershell.exe' or 'pwsh.exe' may do as the argument.
# See below for Windows Terminal profile-related options.
Start-Process -Verb RunAs wt.exe ('"{0}"' -f (Get-Process -Id $PID).Path)
Note:
If the desired PowerShell executable is the your Windows Terminal's default profile, you can omit the argument after wt.exe
# Launch the elevated session with the shell configured as
# the default profile.
Start-Process -Verb RunAs wt.exe
If you want to target a specific Windows Terminal profile, pass its name (or GUID) case-exactly to the -p (--profile) parameter; e.g.:
# Launch the elevated session with the "Windows PowerShell" profile.
Start-Process -Verb RunAs wt.exe '-p "Windows PowerShell"'

Powershell command no longer working - only running as an administrator

I'm not really good at PS so I decided to ask for some advice.
I have a VBA script that uses Get-DhcpServerv4Lease in a PS command to query the DHCP server (as a domain admin user) for a given Scope and arrange the data returned into Excel.
strCommand = "%SystemRoot%\system32\WindowsPowerShell\v1.0\Powershell.exe start-job -credential <domain>\" & TheAdminUser & " -ScriptBlock{Get-DhcpServerv4Lease -ComputerName '<DHCP server>' -ScopeId " & TheScope & "} | wait-job | receive-job"
Set WshShell = CreateObject("WScript.Shell")
Set WshShellExec = WshShell.Exec(strCommand)
strOutput = WshShellExec.StdOut.ReadAll
The script was working perfectly before, now for some unknown reason it's no longer functioning.
Tried to manually run the command in PS and realized it is now only working if I run PS as administrator (works even as local admin), else it returns the following error:
[localhost] An error occurred while starting the background process. Error reported: The directory name is invalid.
+ CategoryInfo : OpenError: (localhost:String) [], PSRemotingTransportException
+ FullyQualifiedErrorId : -2147467259,PSSessionStateBroken
Any advice what might be the problem or where could I start looking for a solution?
Update:
In the meantime I found a different workaround that fixes the original code I used.
Adding [environment]::CurrentDirectory='C:\Windows\System32\WindowsPowerShell\v1.0'; makes it run again without the error.
strCommand = "%SystemRoot%\system32\WindowsPowerShell\v1.0\Powershell.exe "[environment]::CurrentDirectory='C:\Windows\System32\WindowsPowerShell\v1.0';start-job -credential <domain>\" & TheAdminUser & " -ScriptBlock{Get-DhcpServerv4Lease -ComputerName '<DHCP server>' -ScopeId " & TheScope & "} | wait-job | receive-job"
The answers were very useful, maybe alternative workarounds for others, and got me closer to understand Powershell better.
Any advice what might be the problem
The problem is a bug in the Start-Job cmdlet that affects both Windows PowerShell (v5.1, the latest and final version) and PowerShell (Core) v6+ (albeit with different symptoms, still as of PowerShell Core 7.2.0-preview.8 - see GitHub issue #7172).
In Windows PowerShell, the background process uses a (fixed) working directory in the calling user's home-directory tree (the Documents folder), which the user whose credentials are passed to -Credential is not allowed to access, causing the error you saw - and there's no way to specify a different working directory.
Start-Job -Credential does work if your session is elevated, i.e. running with admin privileges, in which case the target user's Documents folder is switched to. Given that the standard runas.exe utility can invoke commands as a different user even from non-elevated sessions, there should be no need for this requirement, however.
Also - as you have discovered yourself - there is a workaround:
If you explicitly set the process-level working directory (which is distinct from PowerShell's) to one the target user is permitted to access, Start-Job -Credential works; for instance, you can use C:\ or $env:SYSTEMROOT\Windows32 (the latter is what runas.exe uses); a quick example (replace otheruser with the username of interest):
[Environment]::CurrentDirectory = 'C:\' # set process-level working dir.
Start-Job -Credential (Get-Credential otheruser) { whoami } |
Receive-Job -Wait -AutoRemoveJob
PowerShell (Core) now makes the background process inherit the caller's working directory (which the caller could set to a directory accessible by the target user) and also has a -WorkingDirectory parameter, but neither approach solves the problem with -Credential as of PowerShell Core 7.2.0-preview - even if you run with elevation (and the above workaround doesn't help either).
Based on the update to your question, it seems the workaround solved your problem, implying that you do not require the operation running with the domain user identity to be elevated; the following may still be of interest for use cases where elevation is required.
If you need to run the operation that uses a different user identity with elevation (with admin privileges):
Launching an elevated (run as admin) process is something Start-Job -Credential fundamentally cannot provide.
The only (PowerShell-native) way to launch an elevated process is via Start-Process -Verb RunAs.
Note: Using Start-Process means that the launched process' output cannot be captured directly by the caller, and instead requires sending the output to files via the --RedirectStandardOutput and -RedirectStandardError parameters, which the caller - assuming termination of the process is waited for - can later read.
Therefore, try the following:
strCommand = "%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -c Start-Process -Wait -Verb RunAs powershell.exe \""-c Get-DhcpServerv4Lease -ComputerName '<DHCP server>' -ScopeId " & TheScope & "\"""
Note:
Another call to powershell.exe is required, launched with elevation (-Verb RunAs), synchronously (-Wait), which then performs the Get-DhcpServerv4Lease call in the foreground.
Because Start-Process -Verb RunAs invariably launches the process in a new window, you may want to hide that window too, by adding -WindowStyle Hidden to the Start-Process call. Conversely, if you do want to see that window, you may want to hide the intermediate window that launches the elevated one, using VBA features.
Note: I've added -c (-Command) to the powershell.exe calls for conceptual clarity; while this parameter is implied in powershell.exe (Windows PowerShell), in pwsh.exe, the PowerShell (Core) equivalent, the default is now -f (-File).
Also note the need to \-escape the embedded " chars. (escaped for VBA as ""), so that the PowerShell CLI retains them as part of the command to execute after command-line argument parsing.
As with your original attempt, this will prompt for a password, and if the calling user doesn't have administrative privileges in principle, an administrator's username will have to be entered too. Note that this prompt cannot be prevented (unless you turn UAC off, which is ill-advised).
The admin username to use for elevation cannot be passed, because -Verb RunAs is mutually exclusive with -Credential. The logic of -Verb RunAs is such that if the current user is an admin user (in principle), it is invariably used for the elevated session, and you're only presented with a Yes/No confirmation dialog. Thus, if you need a different admin user, such as a domain admin, this won't work - see below. (Only if the calling user is not an admin user does the UAC prompt ask for a username and password explicitly).
If you need to run the elevated session with a given admin user account:
Unfortunately, this requires an even more deeply nested command, with additional pitfalls:
In essence, you need to first call Start-Process -Credential to create an (of necessity) non-elevated session with the target user, which then allows you to call Start-Process -Verb RunAs to create an elevated session for that user.
Caveat: This requires you to answer two prompts: first, you need to enter the password to start the non-elevated session for the admin user, and then you need to answer the Yes/No UAC prompt to confirm the intent to start an elevated session for that user.
A Set-Location C:\ command is incorporated, to ensure that the working directory is valid for the target user (in the initial non-elevated session).
Using Start-Process -Wait in order to wait for termination of a process started with a different user (-Credential) inexplicably fails due lack of permissions when invoked from a non-elevated session; the workaround is to use (Start-Process -PassThru ...).WaitForExit().
To simplify quoting, only '...' quoting (escaped as ''...'' in the nested call) is used - therefore, the commands themselves mustn't contain ' chars.
This leads to the following monstrosity.
' Assumes that the following variables are defined:
' TheAdminUser, TheComputerName, TheScope
strCommand = "%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -c Set-Location C:\; (Start-Process -WindowStyle Hidden -PassThru -Credential " & TheAdminUser & " powershell.exe ' -c Start-Process -Wait -Verb RunAs powershell.exe '' -c Get-DhcpServerv4Lease -ComputerName " & TheComputerName & " -ScopeId " & TheScope & " '' ').WaitForExit()"
Note: For troubleshooting, precede the -c argument(s) with -noexit to keep the PowerShell session(s) open.
Alternative, with prior setup:
As Joel Coehoorn points out, one way to allow a non-admin user to execute a preconfigured operation - only - with administrative privileges is to set up a scheduled task that runs with admin credentials and performs the desired operation, which non-admin users can then invoke on demand.
This would obviate the need for a password altogether, but be sure that the operation is truly safe for non-admin users to perform.
Note: Having a scheduled task run by a non-admin user / from a non-elevated process can fail under certain circumstances - though it does work in the scenario at hand, according to Joel; as he notes in reference to this Server Fault post:
I think part of the problem was trying to run as SYSTEM rather than a specific privileged user in elevated mode. It also talks about contexts like SCCM and startup, where certain registry keys are not available, and the powershell code to invoke the task may also have changed.
You should be able to use Start-Process -RunAs for the powershell.exe command and it will elevate. Note that this will trigger UAC if the vBA process isn't already elevated.
The kicker is that if you are trying to self-elevate from a different process, Start-Process is a PowerShell cmdlet so you will need to basically run PowerShell to run another elevated PowerShell session. The command will look something like this:
powershell.exe -Command "Start-Process -Wait -Verb RunAs powershell.exe '-Command ""YOUR ELEVATED CODE HERE""'"
You can test this with the following command in Command Prompt that will output "hello", wait for a key-press, then exit:
powershell.exe -Command "Start-Process -Wait -Verb RunAs powershell.exe '-Command ""echo hello; cmd /c pause""'"
Note that this is how you would invoke the command from the command line, whether PowerShell or CMD. You may need to tweak the escape sequences if calling from another language.
You should also make use of the -Command parameter when invoking powershell.exe or pwsh from somewhere else and want to exit the session once the command is complete, or the -File parameter for the same but it's a script.
You also need to make use of the -Wait parameter when you call Start-Process or else it won't block. This is antithesis to the general executable invocation pattern that non-GUI programs usually don't need the -Wait parameter to block until the process exits.

How to sudo on powershell on Windows

Whenever I need to run a powershell script it complains of security, if I add powershell.exe -nologo -executionpolicy bypass -File .\install.ps1 I still get permission denied unauthorizedAccessException. I just want to run this install script, what is the sudo equivalent to type on the powershell on windows?
If you are using Chocolatey (a package manager), you can install a package named sudo.
Then you can use sudo like Linux 😋
Note: If you're looking to add general-purpose, prepackaged sudo-like functionality to PowerShell, consider the
Enter-AdminPSSession (psa) function from this Gist, discussed in the bottom section of this answer.
If you are running from PowerShell already, then use Start-Process -Verb RunAs as follows:
Start-Process -Verb RunAs powershell.exe -Args "-executionpolicy bypass -command Set-Location \`"$PWD\`"; .\install.ps1"
Note:
The script invariably runs in a new window.
Since the new window's working directory is invariably $env:windir\System32, a Set-Location call that switches to the caller's working directory ($PWD) is prepended.
Note that in PowerShell (Core) 7+ (pwsh.exe) this is no longer necessary, because the caller's current location is inherited.
Executing Set-Location necessitates the use of -Command instead of -File.
A general caveat is that -Command can change the way arguments passed to your script are interpreted (there are none in your case), because they are interpreted the same way they would be if you passed the arguments from within PowerShell, whereas -File treats them as literals.
If you're calling from outside of PowerShell, typically from cmd.exe/ a batch file, you need to wrap the above in an outer call to powershell.exe, which complicates things in terms of quoting, unfortunately:
powershell.exe -command "Start-Process -Verb RunAs powershell.exe -Args '-executionpolicy bypass -command', \"Set-Location `\"$PWD`\"; .\install.ps1\""
Interactively, of course, you can:
Right-click the PowerShell shortcut (in your taskbar or Start Menu, or on your Desktop), select Run as Administrator to open a PowerShell window that runs with admin privileges, and run .\install.ps1 from there.
Alternatively, from an existing PowerShell window, you can open a run-as-admin window with Start-Process -Verb RunAs powershell.exe, as in AdminOfThings' answer.
You can utilize the Start-Process command and then use parameter -Verb runas to elevate. This works great for starting an elevated process.
I created a sudo function like this and added it to my powershell profile:
function sudo {
Start-Process #args -verb runas
}
Example: Open notepad as Admin to edit hosts file
sudo notepad C:\Windows\System32\drivers\etc\hosts
If you want to elevate a Powershell command, you can create a simple function like this:
function Start-ElevatedPS {
param([ScriptBlock]$code)
Start-Process -FilePath powershell.exe -Verb RunAs -ArgumentList $code
}
Then, call the function and pass command wrapped in {} (script block)
Example: Elevate to create a symbolic link
Start-ElevatedPS { New-Item -ItemType SymbolicLink -Name mySymlink.ps1 -Target C:\myTarget.ps1 }
You can start PowerShell with the Run as Administrator option:
Start-Process powershell -Verb runAs
As of today (October 2021), winget install gerardog.gsudo did the trick (on windows 10 home edition). Edit: Tested on Windows 11 as well (April 2022)
after that, you can do this:
gsudo notepad C:\windows\system32\something-editable-by-admin-only.txt
To test if it's working, or in your case:
gsudo powershell.exe install.ps1
You will be prompted by windows` UAC to elevate your priveleges by gsudo, and you can read the source code here: https://github.com/gerardog/gsudo
If you have a corporate policy that blocks scripts execution, then yes. ByPass does not change your profile (user context) state. That is not the design (use case) for any of those switches regarding Execution Policies.
There is not a direct comparison of sudo in Windows, this has nothing to do with PowerShell. You are either admin in a session / app or you are not. If you are installing software, that means you must be admin. If you are doing global system-wide changes, that means you must be admin.
There are folks who have strived to implement scripts, wrapper functions and or modules to mimic sudo …
Module from the MS PowerShell gallery.
Sudo 0.9.3
Use functionality similar to sudo in PowerShell
From GitHub
Sudo for PowerShell
Sudo for PowerShell Installation From PowerShell, create a $profile if
you don't have one:
if (!(test-path $profile)) { new-item -path $profile -itemtype file -force }
Open the profile in notepad:
notepad.exe $profile
Add the following line and save the file:
. /path/to/sudo.ps1
sudo will be available in all new PowerShell windows Usage
sudo application [arguments ...]
...but that does not change what Windows expects when dealing with security boundaries.
See also this Q&A
Sudo !! equivalent in PowerShell
$^ is a variable that expands to the last executed Powershell command.
You can run a command as another user using runas, so the following
works:
runas /user:domain\administrator $^
To shorten that up a bit, you can do some magic with aliases. Take a
look at this Technet article for more info.
EDIT: One caveat - $^ only executes the first command in a pipeline or
multi-command line. If you need to redo an entire command that is
peppered with pipes or semicolons, use Invoke-History instead (which
defaults to the last full command in its entirety).

Can't exec NSIS installation application from tcl script

Running tcl 8.4.13, I have a tcl script to execute my NSIS installation application (duly signed by microsoft) that used to work but now fails (windows 7 and 10), maybe because of windows security update or something? The same tcl script works fine when the target is a local/renamed copy of notepad.exe. The NSIS application works fine when run from from the command line.
The tcl script looks like the following, run via tclkit-win32 tclmnu.tcl, where tclmnu.tcl looks like this:
#! /bin/sh -x
# \
exec wish "$0" "$#"
#
package require Tk
#set runcmd notepad_local_copy.exe
set runcmd my_nsis_app.exe # this doesn't work
# this works with the notepad_local_copy.exe (above) but not my_nsis_app.exe
set catchcode [ catch { exec ${runcmd} } result ]
# also tried this, doesn't work either
#set catchcode [ catch { exec "runas /usr:administrator: ${runcmd}" } result ]
tk_messageBox -type ok -icon error -message "DEBUG: catchcode=${catchcode}"
# catchcode is 0 when runas=notepad_local_copy.exe, 1 when it's my_nsis_app.exe
Well, I solved it by adding cmd /c, as in
set catchcode [ catch { cmd /c exec ${runcmd} } result ]
I don't know why it needs that, nor why it seemed to work before and not now.
I don't know anything about TCL but I'm assuming that exec calls CreateProcess. When UAC was added to Vista it seems Microsoft deemed CreateProcess too low-level to be able to perform elevation.
Dealing with Administrator and standard user’s context:
CreateProcess and CreateProcessWithLogonW do not have new flags to launch the child process as elevated. Internally, CreateProcess() checks whether the target application requires elevation by looking for a manifest, determining if it is an installer, or if it has an app compat shim. If CreateProcess() determines the target application requires elevation, it simply fails with ERROR_ELEVATION_REQUIRED(740). It will not contact the AIS to perform the elevation prompt or run the app. If CreateProcess() determines the application doesn’t require elevation, it will spawn it as a new process.
ShellExecute[Ex] however is able to contact the UAC service and display the UAC elevation dialog:
To programmatically launch a child process as an elevated process, two things must occur: first, the executable of the child process needs to be identified as needing elevation, and second, the parent process needs to use ShellExecute() or ShellExecuteEx().
It seems there are multiple ways to get TCL to call ShellExecute:
eval exec [auto_execok start] {""} [list [file nativename $filename]] ;# http://wiki.tcl.tk/2809
or
twapi::shell_execute -path filename.exe ;# http://www.magicsplat.com/blog/how-do-i-exec-thee/
You can also request elevation of executables even if they are not marked as requiring elevation:
twapi::shell_execute -path filename.exe -verb runas
(The runas verb is not the same thing as runas.exe)
NSIS scripts use the RequestExecutionLevel attribute to mark the executable with the desired UAC elevation level.
Using cmd /c is a bit of a hack and will probably display a console window for a brief period of time. cmd /c first tries CreateProcess and then falls back to ShellExecuteEx if elevation was required. I'm not sure if this is documented behavior.

Resources