Windows expect command equivalent - powershell

What is the equivalent to linux's expect in windows? Is there a way for a script to send a password when prompted to by a previous command?
runas /user:admin net user username /add
Trying echo XYZ | runas ... I got 1326 Logon failure: unknown user name or bad password. The command works without the piped echo.

There is nothing built into PowerShell. However there is a module that should be able to do this called Await - https://msconfiggallery.cloudapp.net/packages/Await/
There is a PowerShell Summit session on how to use Await here - https://www.youtube.com/watch?v=tKyAVm7bXcQ

There is no direct equivalent to expect in Windows that I know of, although you can doubtless write one without too much difficulty. Fortunately, for the special case of supplying credentials in PowerShell, there's a better way, one which doesn't even store passwords in plaintext in the script. This answer to Batch scripting, Powershell, and not triggering the UAC in Windows* starts by saving the user password somewhere as a secure string**:
$pass = Read-Host -AsSecureString
ConvertFrom-SecureString $pass | Out-File pass.txt
Then running the program as administrator with the stored password this way:
function Invoke-ElevatedCommand([String]$FileName, [String]$Arguments) {
$pass = Import-SecureString (Get-Content pass.txt)
$startinfo = New-Object System.Diagnostics.ProcessStartInfo
$startinfo.UserName = "administrator"
$startinfo.Password = $pass
$startinfo.FileName = $FileName
$startinfo.Arguments = $Arguments
[System.Diagnostics.Process]::Start($startinfo)
}
Invoke-ElevatedCommand "net" "user username /add" # Or whatever you need
* Note that this is almost a dupe, but not quite, since the accepted answer is not particularly relevant to this question. Linked answer lightly adapted for the purpose, and upvoted accordingly.
** Note further that, while pass.txt is not plaintext, it's not safe to leave lying around where just anyone can grab it. Stick some restrictive NTFS permissions on it, maybe EFS, etc.

Sorry, I don't know how linux's expect works... However, at this post there is a Batch-file solution for a problem similar to yours and titled "Automatically respond to runas from batch file":
#if (#CodeSection == #Batch) #then
#echo off
start "" runas /user:testuser c:/path/to/my/program.exe
CScript //nologo //E:JScript "%~F0"
goto :EOF
#end
WScript.CreateObject("WScript.Shell").SendKeys("password{ENTER}");

Related

Why would one do this when storing a secure string?

Would this in some way make it more secure than just a secure string and storing it in a credential? Why would someone do this?
$secVal = read-host -AsSecureString -prompt "Enter xstoreuser password"
$strVal = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secVal)
$dasPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($strVal)
and then later
psftp -l somelogin -pw $dasPassword -P 22 -b .\ftp.txt somehost
The technique you're showing is not about making the command more secure - on the contrary:
You need to do extra work to recover the plain-text password from the [securestring] instance, because in order to pass a password to an external program such as psftp.exe you need an insecure plain-text representation, because external programs know nothing about .NET secure strings.
How secure is SecureString? tells you about .NET secure strings and their limitations in general.
As an aside: The command for extracting the plain-text password can be simplified a little via an aux. [pscredential] instance:
# Returns the plain-text content of secure string $secVal:
(New-Object pscredential somelogin, $secVal).GetNetworkCredential().Password
The only secure approach is to avoid plain-text passwords altogether and use a PKI approach (based on public and private keys) instead, if supported by the target program.
To demonstrate that the plain-text approach is insecure (even without the use of an intermediate variable to store the unencrypted password):
# Create a [pscredential] instance with a secure-string password:
# Note: To make this demo self-contained, I'm converting *from* plain
# text in order to construct the credential object.
# Note that you should never do that in production code.
# The need to specify -Force to do it is a reminder that it
# normally shouldn't be done.
$cred = New-Object pscredential someuser,
(ConvertTo-SecureString -AsPlainText -Force 'actualPassword')
# Open a new hidden window that passes the password *as plain text* to
# `cmd /c echo` (and then waits for a keypress, to keep the process alive).
$ps = Start-Process -PassThru -WindowStyle Hidden cmd "/c echo $($cred.GetNetworkCredential().Password) & pause"
# Now inspect the process' command line using CIM (WMI):
(Get-CimInstance Win32_Process -Filter "ProcessID = $($ps.ID)").CommandLine
# Kill (stop) the sample process.
$ps | Stop-Process
The above yields "C:\WINDOWS\system32\cmd.exe" /c echo actualPassword & pause, demonstrating that the plain-text password can indeed be obtained by other processes.
You can also use GUI methods for inspecting a running process' command line: Task Manager (Taskmgr.exe) or, without potential truncation, SysInternals' Process Explorer (procexp.exe), which, however, must be installed on demand - see this ServerFault answer.

Prevent PowerShell script from being read

I have the below PowerShell script (myscript.ps1) in which I ask for username and password. Depending on the username and password it copies a file to a certain destination.
$credentials = Get-Credential
if ($credentials.Username -eq "user1" -And $credentials.GetNetworkCredential().password -eq "pass1")
{ Copy-Item "test1.pdf" "\test\test1.pdf"; }
else
{ Copy-Item "test2.pdf" "\test\test2.pdf"; }
Requirement: I want to make this file protected so no one can edit it and see the username and password.
PS2EXE
I found a solution found here which converts the PowerShell script to an .exe file. When I originally run the script using PowerShell a dialog box appears allowing me to enter the username and password:
After the .exe is generated and when I run it the credentials dialog box no longer appears. Instead, the console appears saying "Credential:"
I don't know why? I want the credentials form to still appear when running the exe. Any thoughts please?
Q: Why does the EXE prompt with "Credential"?
This isn't an answer to the real question, and is based on guessing/supposition about PS2EXE, but I hope it is useful to clear up some confusion.
Having looked briefly at the PS2EXE page linked above, it seems that this utility encodes the script in Base64 and bundles it with a lightweight (?) custom PowerShell host. When run, I suppose the EXE starts the host, decodes the script and runs it.
The problem is that the Get-Credential cmdlet is running within a PS host that probably can't interact with the desktop. That is, it can't put up the GUI prompt for credentials. It therefore needs to prompt for the Credential property on the command line, explaining why you see that behaviour.
Workaround with Read-Host?
Instead of trying to use Get-Credential to prompt for username and password, you could embrace what PS2EXE seems to be doing and just use Read-Host:
$UserName = Read-Host "Enter username"
$Password = Read-Host "Enter password" -AsSecureString
$Credentials = New-Object System.Management.Automation.PSCredential $UserName,$Password
if ($credentials.Username -eq "user1" -And $credentials.GetNetworkCredential().password -eq "pass1")
{ ... }
Using -AsSecureString will hide the password on the screen. The $Password variable will be of type System.Security.SecureString, which can be used to create a PSCredential object as shown.
You'd need to test this, but it seems that you're able to read from the shell but not from a GUI prompt.
And just to be clear: none of this is anywhere near best-practice security. If you need authentication/authorization for these activities, step back and look at the problem again.
Workaround with two scripts?
It seems that PS2EXE doesn't support -AsSecureString in the same way that normal PowerShell does, i.e. it doesn't hide the characters. A possible workaround for this would be to collect the username and password from the user in one script and then pass them to a PS2EXE-converted script for processing.
Launch-MyScript.ps1:
$Credentials = Get-Credential
& MyScript.exe $Credentials.Username $Credentials.Password
MyScript.exe (coverted with PS2EXE):
param($Username,$Password)
$Credentials = New-Object System.Management.Automation.PSCredential $Username,$Password
if ($Credentials.Username -eq "user1" -and
$Credentials.GetNetworkCredential().password -eq "pass1")
{
...
}
The user runs Launch-MyScript.ps1 and completes the password prompt. Then the EXE is run automatically with the username and password passed in as arguments. Note that, as shown above, the password is a Secure String. Test this; I'm not using PS2EXE so it's a theoretical solution at the moment.
If you can't pass $Password along the pipeline as a Secure String object, you can convert it to text with ConvertFrom-SecureString in the first script, then conver it back with ConvertTo-SecureString in the second one.
According to this article http://windowsitpro.com/powershell/protect-your-powershell-scripts you should first set ur execution policy to AllSigned by Set-ExecutionPolicy AllSigned, then create a certificate using makecert cmdlet.
Then u can sign single script using Set-AuthenticodeSignature cmdlet or use .pfx File to Sign a Script which appears even safer.
Hope it helps a bit.

Powershell Automation - Passing Password after executing a exe file

I working on automating the specific task using powershell and getting error while passing the password. Below is my task. Below are the tasks I need to automate.
Execute exe file (For ex: export.exe)
It will prompt for password twice (Enter your password and Reenter your password)
After Entering our password Twice, It will ask for confirmarion Yes or No: (I need to give 'Yes' or No to continue )
I tried automating the above first two steps. First I get the stored password in a file using the below command
$password = get-content C:\cred.txt | convertto-securestring
Then I tried executing the below commands in script
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $pass
Invoke-Expression "& `"C:\Program Files\XX\XX\bin\export.exe`""
But I dont know how to use the password from the file twice to continue the script. I am new to powershell. Please help me.
I don't think that your application will accept PowerShell's PSCredential object. More likely, you just have to pass plaintext password twice and then Yes.
Try this (assuming that export.exe is console application):
'Password', 'Password', 'Yes' | & 'C:\Program Files\XX\XX\bin\export.exe'
This will send 3 strings, separated by the newline (Enter) to the export.exe's stdin.

Correctly set the current user as the new owner in PowerShell [duplicate]

How do I get the current username in Windows PowerShell?
I found it:
$env:UserName
There is also:
$env:UserDomain
$env:ComputerName
On Windows, you can:
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name
I thought it would be valuable to summarize and compare the given answers.
If you want to access the environment variable:
(easier/shorter/memorable option)
[Environment]::UserName -- #ThomasBratt
$env:username -- #Eoin
whoami -- #galaktor
If you want to access the Windows access token:
(more dependable option)
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name -- #MarkSeemann
If you want the name of the logged in user
(rather than the name of the user running the PowerShell instance)
$(Get-WMIObject -class Win32_ComputerSystem | select username).username -- #TwonOfAn on this other forum
Comparison
#Kevin Panko's comment on #Mark Seemann's answer deals with choosing one of the categories over the other:
[The Windows access token approach] is the most secure answer, because $env:USERNAME can be altered by the user, but this will not be fooled by doing that.
In short, the environment variable option is more succinct, and the Windows access token option is more dependable.
I've had to use #Mark Seemann's Windows access token approach in a PowerShell script that I was running from a C# application with impersonation.
The C# application is run with my user account, and it runs the PowerShell script as a service account. Because of a limitation of the way I'm running the PowerShell script from C#, the PowerShell instance uses my user account's environment variables, even though it is run as the service account user.
In this setup, the environment variable options return my account name, and the Windows access token option returns the service account name (which is what I wanted), and the logged in user option returns my account name.
Testing
Also, if you want to compare the options yourself, here is a script you can use to run a script as another user. You need to use the Get-Credential cmdlet to get a credential object, and then run this script with the script to run as another user as argument 1, and the credential object as argument 2.
Usage:
$cred = Get-Credential UserTo.RunAs
Run-AsUser.ps1 "whoami; pause" $cred
Run-AsUser.ps1 "[System.Security.Principal.WindowsIdentity]::GetCurrent().Name; pause" $cred
Contents of Run-AsUser.ps1 script:
param(
[Parameter(Mandatory=$true)]
[string]$script,
[Parameter(Mandatory=$true)]
[System.Management.Automation.PsCredential]$cred
)
Start-Process -Credential $cred -FilePath 'powershell.exe' -ArgumentList 'noprofile','-Command',"$script"
(you may need a hyphen before noprofile, like so)
Start-Process -Credential $cred -FilePath 'powershell.exe' -ArgumentList '-noprofile','-Command',"$script"
$env:username is the easiest way
I'd like to throw in the whoami command, which basically is a nice alias for doing %USERDOMAIN%\%USERNAME% as proposed in other answers.
Write-Host "current user:"
Write-Host $(whoami)
[Environment]::UserName returns just the user name. E.g. bob
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name returns the user name, prefixed by its domain where appropriate. E.g. SOMEWHERENICE\bob
Now that PowerShell Core (aka v6) has been released, and people may want to write cross-platform scripts, many of the answers here will not work on anything other than Windows.
[Environment]::UserName appears to be the best way of getting the current username on all platforms supported by PowerShell Core if you don't want to add platform detection and special casing to your code.
I have used $env:username in the past, but a colleague pointed out it's an environment variable and can be changed by the user and therefore, if you really want to get the current user's username, you shouldn't trust it.
I'd upvote Mark Seemann's answer:
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name
But I'm not allowed to. With Mark's answer, if you need just the username, you may have to parse it out since on my system, it returns hostname\username and on domain joined machines with domain accounts it will return domain\username.
I would not use whoami.exe since it's not present on all versions of Windows, and it's a call out to another binary and may give some security teams fits.
Just building on the work of others here:
[String] ${stUserDomain},[String] ${stUserAccount} = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name.split("\")
$username=( ( Get-WMIObject -class Win32_ComputerSystem | Select-Object -ExpandProperty username ) -split '\\' )[1]
$username
The second username is for display only purposes only if you copy and paste it.
I didn't see any Add-Type based examples. Here is one using the GetUserName directly from advapi32.dll.
$sig = #'
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool GetUserName(System.Text.StringBuilder sb, ref Int32 length);
'#
Add-Type -MemberDefinition $sig -Namespace Advapi32 -Name Util
$size = 64
$str = New-Object System.Text.StringBuilder -ArgumentList $size
[Advapi32.util]::GetUserName($str, [ref]$size) |Out-Null
$str.ToString()
Sometimes the Username attribute has no data in Win32_ComputerSystem even though there's a user signed in. What works for me is to use quser and parse the output. It's not perfect, but it works. E.g.:
$quserdata = #()
$quserdata = quser
$userid = ($quserdata[1] -split ' ')[1]
$userid
Note: if this is run as the user who is logged in, quser adds '>' symbol to the output. Then you need to get rid of that symbol, but mostly this is needed for code run as system or another account than the one that is logged in.
If you're used to batch, you can call
$user=$(cmd.exe /c echo %username%)
This basically steals the output from what you would get if you had a batch file with just "echo %username%".
I find easiest to use: cd $home\Desktop\
will take you to current user desktop
In my case, I needed to retrieve the username to enable the script to change the path, ie. c:\users\%username%. I needed to start the script by changing the path to the users desktop. I was able to do this, with help from above and elsewhere, by using the get-location applet.
You may have another, or even better way to do it, but this worked for me:
$Path = Get-Location
Set-Location $Path\Desktop
In my case, I needed to retrieve the username to enable the script to change the path, ie. c:\users\%username%\. I needed to start the script by changing the path to the users desktop. I was able to do this, with help from above and elsewhere, by using the get-location applet.
You may have another, or even better way to do it, but this worked for me:
$Path = Get-Location
Set-Location $Path\Desktop

Entering a password for DomainCredential in Rename-Computer in Powershell

I am trying to use a Powershell script to rename computers based on their serial numbers.
So far I have -
Set-ExecutionPolicy -Unrestricted
$name = (Get-WmiObject win32_bios).SerialNumber.Trim()
Rename-Computer -NewName $name -DomainCredential \
I don't want this to prompt because I have a few thousand system to image and I would like this to just do the rename silently and then the MDT 2012 Update 1 will do the reboot.
I am a n00b when it comes to powershell and to scripting and I have spent the better part of a week trying to figure this out. I can get the rename to work locally with no problems but I am hoping I can get some help doing it silently.
I guess my question is, how do I put a password in my powershell script so I don't have to enter it at the workstation?
I'm a little late, but couldn't you do: OSDComputerName=%serialnumber% in your task sequence rules since you're using MDT?
When the task sequence runs it should just set the computer name to that serial number. If you had a prefix or suffix like OSDComputerName=WS%SerialNumber%
You can use the Get-WmiObject command let for the same and pass a password stored in a variable like below
(Get-WmiObject win32_computersystem).Rename( $NewName,$passwd,'domain\username')
to store the password in variable, you can do something like
$key = 1..32 | ForEach-Object { Get-Random -Maximum 256 }
$passwd = Read-Host "Enter password" -AsSecureString
$encpwd = ConvertFrom-SecureString $passwd -Key $key
$encpwd
This examples are taken from this post how to pass credentials to rename command?
go through this post; it explains both secure and non-secure way of storing the password in great detail.