psexec trying to exec script error - powershell

I am learning how to interact with PowerShell and PsTools, and I have a problem with psexec.
I got a ps1 script named test.ps1 and inside it I have Get-Service which gives me the all services in my computer. Now I am going into PowerShell and go to c:\pstools. Then I type
psexec.exe C:\test\test.ps1
and it fails and returns me this error:
%1 is not a valid Win32 application
What could be the problem?

PsExec launches an executable. You need to specify the executable for PowerShell and associated arguments:
psexec.exe -accepteula -nobanner -s -h -d powershell.exe -ExecutionPolicy Bypass -NoProfile -NoLogo -File "C:\test\test.ps1"

The immediate answer to your question is:
psexec requires a (binary) executable as its first argument and cannot execute scripts directly.
Therefore, you must pass the Windows PowerShell executable name to psexec and in turn pass the desired script to the latter as an argument, via the -File parameter:
psexec powershell -File C:\test\test.ps1
That said, this particular use of psexec is pointless, as it would execute the script locally, as the current user, in which case use of psexec is a needless complication:
If you already know that, and the psexec command at hand is just a simplified example, never mind.
Otherwise, read on below.
The ps in psexec and PsTools has nothing to with PowerShell; PsTools is a collection of CLIs for managing Windows machines remotely, including processes, a common abbreviation of which is "ps", inspired by the standard ps Unix utility, which in turn inspired the initial tool in the collection, pslist; the primary purpose of psexec is to invoke arbitrary command lines on remote machines[1]
.
To invoke a PowerShell script locally:
From inside PowerShell itself, simply invoke the file path directly:
PS> c:\test\test.ps1
PS> & "c:\test\test.ps1", if the file path is / must be quoted or is provided via a variable or expression.
From outside of PowerShell, such as cmd.exe ("Command Prompt") or bash, you must invoke the PowerShell executable explicitly and pass it the script file path via the -File parameter:
Windows PowerShell: C:\> powershell -file c:\test\test.ps1
PowerShell Core: C:\> pwsh -file c:\test\test.ps1
In other words: the PowerShell's executable name is
powershell.exe for Windows PowerShell,
vs. pwsh for the cross-platform PowerShell Core edition (with extension .exe on Windows).
If you do need remote execution:
Pass \\-prefixed machine name(s) or IP address(es) to psexec; e.g., the following command executes the hostname utility on machine somemachine:
psexec \\somemachine hostname
There is no benefit to using psexec without targeting a different machine.[1]
However, psexec is normally not needed, because PowerShell has built-in support for remoting (i.e., the ability to execute commands on other machines; remoting requires setup, however - run Get-Help about_Remote_FAQ for more information); e.g., the equivalent of the above command is:
Invoke-Command -ComputerName somemachine { hostname }
[1] As TheIncorrigible1 points out, psexec can also be used for local execution as the system account (NT AUTHORITY\SYSTEM, the account that represents the computer as a whole) with the -s option.
Additionally, you can also run locally as another user, using the -u parameter - which, however, the standard runas utility can do as well (the latter doesn't offer passing the target user's password as a parameter for security reasons, but does offer to securely save a password for later reuse).
Run psexec -h for help.

Related

Is there a way to execute powershell commands remotely on a domain user from the DC?

Let's say for example, I have a domain controller and a client that is joined to the domain.
If I wanted to remotely lock out the client I would supposedly run
Invoke-Command -ComputerName [workstation name] -ScriptBlock {rundll32.exe user32.dll, LockWorkStation}
However this does not work. I'm assuming this is because the Invoke-Command cmdlet runs the code in the scriptblock but returns anything back to the local terminal. What I'm trying to accomplish is to have the code or powershell script run locally on the remote computer.
My question is first of all if this is the correct approach and second why the command I'm running does not work.
Download PsExec from https://learn.microsoft.com/en-us/sysinternals/downloads/psexec and run following command.& "C:\PSTools\PsExec.exe" -s -i \\COMPNAME rundll32.exe user32.dll,LockWorkStation
As per my comment when using PSExec... So, stuff like this ---
PsExec.exe \\ -d -u \Administrator -i cmd.exe /c "C:\windows\system32\rundll32.exe user32.dll, LockWorkStation"
Or using PowerShell with quer.exe ...
(it's a tool in every modern Windows version)
quser | Microsoft Docs
...in a PowerShell remoting script, like described here:
How To Log Off Windows Users Remotely With PowerShell
Again the work is being done by quser.exe, not PowerShell specifically. PowerShell is just being used to run quser.exe remotely. You could do the same, by copying PSExec to the remote host and do a similar operation.

Elevate / Sudo on PowerShell

I have found a few PowerShell elevate / sudo functions, but none of them seem to work well (in a "as intuitively and seamlessly as on every Unix and Linux distribution" way). They are mostly redundant as they don't work well. If someone has a seamlessly working elevate / sudo on PowerShell they'll know it.
The problems with the functions that I've seen are:
• They only work with external scripts by calling another instance of powershell.exe. i.e. If you want to do something as simple as sudo gci or sudo Get-ChildItem that will generate an error as the methods don't seem to like calling aliases or Cmdlet (for some reason!).
• You cannot seamlessly elevate the existing console session up to Administrator, and this seems to require that an elevate / sudo function opens a completely new console (seems cumbersome to have to open a new console for nothing!)?
Does anyone have a reliable elevate / sudo that they use? I don't expect it to be perfect, if there are good technical reasons why things like the above do not work (maybe to do with limitations of the PowerShell host itself not being capable enough) then that's fine, but it would be good to know how far we can get with a functional elevate / sudo within PowerShell. It's often a shame that, although PowerShell is massively more advanced than bash (and it's object manipulation capabilities blow away Python and Perl imo also), sometimes it seems like some of the most simple capabilities in Unix-land, like sudo, blow away what is possible in PowerShell - I'd love to see those gaps filled so that PowerShell can be shown to be every bit as capable as Unix (and more so!!) for a change.
Nothing native in the box of course, so, an apples/oranges comparison when talking sudo stuff with Windows.
Security boundaries/functionalities are just different, as well all know, and the sudo equivalent in Windows (and thus PowerShell) is RunAs and that will pop Windows UAC, no getting around that, without turning UAC off (don't do this) or setting up an AppCompat shim.
So, when you say functions, are you saying you have already tired these:
Find-Module -Name '*sudo*' |
Select Name, Version, Type, Description
# Results
<#
Name Version Type Description
---- ------- ---- -----------
Sudo 2.1.0 Module Use functionality similar to sudo in PowerShell. GitHub: https://github.com/pldmgg/Sudo
PSSudo 1.4.0 Module Function for executing programs with adminstrative privileges
#>
This type of question comes up a lot here and has been answered several times. So, are you saying, you tried the below?
How to sudo on powershell on Windows
Start-Process -Verb RunAs powershell.exe -Args "-executionpolicy bypass -command Set-Location \`"$PWD\`"; .\install.ps1"
Sudo !! equivalent in PowerShell
runas /user:domain\administrator $^
Is there any 'sudo' command for Windows?
doskey sudo= runas /user:Administrator "cmd /k cd \"%cd%\" & $*"
runas /noprofile /user:Administrator cmd
See also:
Support sudo #3232
5 Windows Alternatives to the Linux sudo Command
gsudo is a sudo for Windows that behaves like Unix sudo (elevates a command or your cmd/ps shell in your current console windows). It works in Powershell, but with limitations: The elevated memory space can't share objects with the non-elevated one, so variables can't be shared, and some kind of marshalling of objects must be done. Currently gsudo does the most naive, but at least honest, marshalling: just strings can be passed to and from. You can pass a string literal with the command that needs to be elevated to gsudo. Then gsudo returns a string that can be captured, not powershell objects.
# Commands without () or quotes
PS C:\> gsudo Remove-Item ProtectedFile.txt
or
PS C:\> gsudo 'Remove-Item ProtectedFile.txt'
# On strings enclosed in single quotation marks ('), escape " with \"
$hash = gsudo '(Get-FileHash \"C:\My Secret.txt\").Hash'
# For variable substitutions, use double-quoted strings with single-quotation marks inside
$hash = gsudo "(Get-FileHash '$file' -Algorithm $algorithm).Hash"
# or escape " with \""
$hash = gsudo "(Get-FileHash \""$file\"" -Algorithm $algorithm).Hash"
# Test gsudo success (optional):
if ($LastExitCode -eq 999 ) {
'gsudo failed to elevate!'
} elseif ($LastExitCode) {
'Command failed!'
} else { 'Success!' }
Or, you can just call gsudo to elevate your current shell, in Powershell:
PS C:\> gsudo
(Accept UAC popup)
PS (ADMIN) C:\> Remove-Item ProtectedFile.txt
PS (ADMIN) C:\> exit
PS C:\>

Need to unblock remote ps script run using psexec

As part of our automatic build and deploy using TFS, I need to execute a powershell script on a target server. The following is the (simplified for clarity) command run on the build server by the TFS Build Agent PreBuild step, in the (pre-build.ps1) script...
C:\Builds\<snip>\psexec.exe -accepteula -s -i \\WSRMO632WEB powershell.exe \\TFS-BAGENT-POC\<snip>\PreBuild-AppPool.ps1 -name AppPool-DEV -usr User -pw pass
If I run the powershell part of the command on the WSRMO632WEB box in a command window, I get the warning...
Security warning
Run only scripts that you trust. While scripts from the internet can be useful,
this script can potentially harm your computer. If you trust this script,
use the Unblock-File cmdlet to allow the script to run without this warning message.
Do you want to run \\TFS-BAGENT-POC\<snip>\PreBuild-AppPool.ps1?
[D] Do not run [R] Run once [S] Suspend [?] Help (default is "D"):
If I choose R, the script runs and performs correctly.
My problem is that I cannot get the syntax correct to incorporate the Unblock-File cmdlet.
I'm currently thinking that I'm going to have to use multiple psexec commands, one to copy the file from the build server, one to unblock it and a third to finally run it.
Surely it should be easier than that, but I can't find a suitable example and can't get the syntax right.
Any suggestions, please?
You can use Powershell's -command to first do an Unblock-File, then run it as a script.
C:\Builds\<snip>\psexec.exe -accepteula -s -i \\WSRMO632WEB powershell.exe "-command \"$file='\\TFS-BAGENT-POC\<snip>\PreBuild-AppPool.ps1'; $file; Unblock-File $file; & $file\"" -name AppPool-DEV -usr User -pw pass
Quotes are necessary so that the full command string will be passed to Powershell. Add backslashes to escape themselves as necessary.
UPDATE: You can also try feeding the required command into standard input.
echo r | C:\Builds\<snip>\psexec.exe -accepteula -s -i \\WSRMO632WEB powershell.exe \\TFS-BAGENT-POC\<snip>\PreBuild-AppPool.ps1 -name AppPool-DEV -usr User -pw pass
This way Powershell will run, get the "R" for "Run once" and run the script, without any changes to the script or calling command.

Running CMD command in PowerShell

I am having a bunch of issues with getting a PowerShell command to run. All it is doing is running a command that would be run in a CMD prompt window.
Here is the command:
"C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386\CmRcViewer.exe" PCNAME
I have tried the following with no success (I have tried many iterations of this to try and get one that works. Syntax is probably all screwed up):
$TEXT = $textbox.Text #$textbox is where the user enters the PC name.
$CMDCOMMAND = "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386\CmRcViewer.exe"
Start-Process '"$CMDCOMMAND" $TEXT'
#iex -Command ('"C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386\CmRcViewer.exe"' $TEXT)
The command will just open SCCM remote connection window to the computer the user specifies in the text box.
Try this:
& "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386\CmRcViewer.exe" PCNAME
To PowerShell a string "..." is just a string and PowerShell evaluates it by echoing it to the screen. To get PowerShell to execute the command whose name is in a string, you use the call operator &.
To run or convert batch files externally from PowerShell (particularly if you wish to sign all your scheduled task scripts with a certificate) I simply create a PowerShell script, e.g. deletefolders.ps1.
Input the following into the script:
cmd.exe /c "rd /s /q C:\#TEMP\test1"
cmd.exe /c "rd /s /q C:\#TEMP\test2"
cmd.exe /c "rd /s /q C:\#TEMP\test3"
*Each command needs to be put on a new line calling cmd.exe again.
This script can now be signed and run from PowerShell outputting the commands to command prompt / cmd directly.
It is a much safer way than running batch files!
One solution would be to pipe your command from PowerShell to CMD. Running the following command will pipe the notepad.exe command over to CMD, which will then open the Notepad application.
PS C:\> "notepad.exe" | cmd
Once the command has run in CMD, you will be returned to a PowerShell prompt, and can continue running your PowerShell script.
Edits
CMD's Startup Message is Shown
As mklement0 points out, this method shows CMD's startup message. If you were to copy the output using the method above into another terminal, the startup message will be copied along with it.
For those who may need this info:
I figured out that you can pretty much run a command that's in your PATH from a PS script, and it should work.
Sometimes you may have to pre-launch this command with cmd.exe /c
Examples
Calling git from a PS script
I had to repackage a git client wrapped in Chocolatey (for those who may not know, it's a package manager for Windows) which massively uses PS scripts.
I found out that, once git is in the PATH, commands like
$ca_bundle = git config --get http.sslCAInfo
will store the location of git crt file in $ca_bundle variable.
Looking for an App
Another example that is a combination of the present SO post and this SO post is the use of where command
$java_exe = cmd.exe /c where java
will store the location of java.exe file in $java_exe variable.
You must use the Invoke-Command cmdlet to launch this external program. Normally it works without an effort.
If you need more than one command you should use the Invoke-Expression cmdlet with the -scriptblock option.

Batch files, Powershell Scripts, PSExec and System user

I'm trying to put in place some monitoring for Windows Task Scheduler, I have a Powershell script that runs the following:
$serverName = hostname
$schedule = new-object -com("Schedule.Service")
$schedule.connect($serverName)
$tasks = $schedule.getfolder("\").gettasks(0)
$tasks |select name, lasttaskresult, lastruntime
This returns a list of scheduled tasks on the server it is run on, the last task result and last run time. The purpose for this is to return a dataset to our monitoring solution (Geneos) which we can use for alerting.
We have a large Win2008 estate, so I want the script centralised allowing any of the Geneos probes to call it and return a dataset for their host. To do this I wrapped the powershell in a .bat file which does the following:
\\fileserverhk\psexec.exe -accepteula -u admin -p "pwd" powershell.exe cpi \\fileserverhk\scripts\TaskSchedulerMonitor.ps1 -Destination C:\Monitor\TaskSchedulerMonitor.ps1
\\fileserverhk\psexec.exe -accepteula -u admin -p "pwd" powershell.exe -ExecutionPolicy Bypass -File C:\Monitor\TaskSchedulerMonitor.ps1
The First step copies the .ps1 file locally to get around Powershell not trusting UNC paths and the second part runs the script.
If I run the .bat file manually from a test server it executes fine (this is logged in under an admin account). However, when I fire the .bat file via Geneos (which runs under the SYSTEM account) I get:
Access is denied.
PsExec could not start powershell.exe:
So basically my question is, how do I get PsExec to switch user when it is run under the SYSTEM account? Even though PsExec has the credentials set for another account, there is obviously something preventing it from changing when run under system.
I read to try running it with the -h switch but I get the below error:
The handle is invalid.
Connecting to local system...
Starting PsExec service on local system...
Connecting with PsExec service on <server>...
Starting powershell.exe on <server>...
Error communicating with PsExec service on <server>:
In addition to the above error, I end up with the PSExec and powershell processes hung on the remote machine. The interesting part is I can see the PSExec and PSEXEC.SVC running under SYSTEM and the powershell running under admin, so it's almost there, but something isn't quite right there.
We managed to get there using a powershell wrapper on the Windows schtasks command (link here). Schtasks can be run under the SYSTEM account and will return all the necessary task information, so we no longer needed to faff about with permissions, and no more clear text passwords on the environment (bonus).
We wrapped:
schtasks.exe Query /FO CSV
in a powershell script, and used PS to format the output into the csv style expected by Geneos.