Running Windows Powershell from PHP unaware of Current Profile - php

I have a few functions and variables defined for all machine users like so:
Set-Content $Profile.AllUsersCurrentHost (Get-Content path/to/myprofile.ps1)
Let's call one of my defined functions Do-Stuff.
This works very well. And anytime Powershell console is called up, and you type "Do-Stuff"+ENTER, it works.
Now I have tried to call this function from PHP a few ways, and I'm having a couple of problems. Consider this:
$res = shell_exec( 'Powershell Do-Stuff');
print_r($res);
What I get is this error:
Do-Stuff: The term 'Do-Stuff' is not recognized as the name of a
cmdlet, function, script file, or operable program....
I've tried also:
$res = shell_exec( 'Powershell -File path/to/script.ps1');
print_r($res);
If the file script.ps1 does contain Do-Stuff or any of the other defined functions, what I get is the same type of error message.
Now what this tells me is that the calling PHP script is NOT recognized as a Windows machine user, and the currently loaded $Profile is NOT being applied.
So what's the solution to this? How can I get the loaded profile for current user or all users to be applied to the running PHP script?

I have a workaround for this issue, using -NoProfile, then dot-sourcing the Profile file.
In the below example, the profile file is includes.ps1:
$cmdlet = 'Do-Stuff';
$cmd = 'Powershell.exe -ExecutionPolicy Bypass -NoProfile -Command "& { . \includes.ps1; '.$cmdlet.' }"';
shell_exec($cmd);
It may prove useful for you to enclose this in a reusable function:
<?php
function run_ps_command($cmdlet, $async=true){
$cmd = 'Powershell.exe -ExecutionPolicy Bypass -NoProfile -Command "& { . \Includes.ps1; '.$cmdlet.' }"';
if($async){
$WshShell = new COM("WScript.Shell");
$res = $WshShell->Run( $cmd, 0, false);
}else{
$res = shell_exec($cmd);
}
return $res;
}
?>
The $async argument allows you to run this command without having PHP wait for the output of the PowerShell script. The advantage is that PHP code execution is faster, the disadvantage is: you've no idea if your script ran successfully, or got into any trouble on the way ;)

Related

Using exec to run PowerShell script hangs if I try to set credentials

Firstly I apologise but I am pretty new to PHP and PowerShell, we all have to start somewhere! I am creating a utility where everyday IT tasks can be performed from a central web based console. I have managed to query and report on things like password expiry by executing PowerShell scripts but have got stuck on unlocking accounts. I query AD and return a list of locked users with a button next to each user to unlock them. This button posts to a php page which runs another powershell script to unlock the user.
php page is:
<?php
// Get the variables submitted by POST in order to pass them to the PowerShell script:
$lockeduser = $_POST["unlock"];
// Path to the PowerShell script.
$psScriptPath = "C:\\code\\psphp\\ps\\unlock.ps1 $lockeduser 2>&1";
// Execute the PowerShell script:
exec("powershell -command $psScriptPath",$out,$ret);
echo "<pre>";
print_r ($out);
print_r ($ret);
echo "</pre>";
?>
As you can see I'm trying to capture any output but at the moment the page is just hanging.
PowerShell script is:
param([string]$lockeduser)
Import-Module ActiveDirectory
$adminacc = "*myadminaccount*"
$encrypted = Get-Content c:\password1.txt | ConvertTo-SecureString
$credential = New-Object System.Management.Automation.PsCredential($adminacc, $encrypted)
Unlock-ADAccount -Identity $lockeduser -Credential $credential
If I echo the command before passing it to PS it looks fine and can be executed directly from PS.
Edit: This is something to do with exec (or shell_exec) causing an issue when the PS script is setting credentials. If I remove that part of the script i.e.
param([string]$lockeduser)
Import-Module ActiveDirectory
Unlock-ADAccount -Identity $lockeduser
it runs and returns that the script failed due to
Insufficient access rights to perform the operation
Has anyone come across this before, I have searched for anything on this to no avail. Thanks!
Further edit
After a bit more testing it is this PS code that doesn't work
$encrypted = Get-Content c:\password1.txt | ConvertTo-SecureString
If I change the method to
$password = ConvertTo-SecureString "My Password" -AsPlainText -Force
it works with no problems. Plain text passwords in files are obviously not something I want to use. Can someone test and see if they get the same result?
So it turns out that it was all just me being a newb. Executing the script via the php page must not be running it as my account. My account was the one that set the credentials and stored it in the file so is the only one that can decrypt it. I have changed the convertto-securestring method to use keys instead of default and it now works.

Executing powershell script from PHP with arguments

I have been wracking my brain and my pc on this one, hoping someone can help me out here.
I have a PHP site that needs to execute a powershell script which in turn executes and executable.
the PHP that calls the .ps1 is
define('updaterPath','C:\\artemis\\DbUpdateUtility1.0.12.2');
define('outputLocation',updaterPath.'\\Output');
$CMD = updaterPath . '\\ArtemisXmlUtil.exe';
$action = 'import';
$args= ' -f' . outputLocation;
$psScriptPath = '.\\get-process.ps1';
$query = shell_exec("powershell -command $psScriptPath -cmd '$CMD' -action '$action' -paras '$args' < NUL");
my .ps1 script is:
param(
[string]$CMD,
[string]$action,
[string]$paras
)
& $CMD $action $paras
Right now, when I echo the full command, it looks perfect and can be copied and pasted into powershell and runs successfully, but when I try to actually execute it from php, it runs with only the $CMD variable and ignores the $action and $paras.
I tried concactenating the variables all into 1 string, but that fails as not even being able to see the executable.
the full command shouls look like:
C:\artemis\DbUpdateUtility1.0.12.2\ArtemisXmlUtil.exe import -fC:\artemis\DbUpdateUtility1.0.12.2\Output
Ok, figured this one out on my own.
The problem is how the command was being executed. Using the "&" forced powershell to ignore the parameters being pass into the script. The correct way, since the command is an executable anyhow, was to build the entire command as a string and pass it all at once to the ps1 file and then change the ps1 file to use the invoke-expression commandlet.
param(
[string]$CMD
)
Invoke-expression $CMD

PHP shell_exec not producing desired output

I'm trying to run a java program and return the output then use that on my page.
if ($program) {
$output = shell_exec("java ftoa 12");
var_dump($output);
}
For some reason, that var_dump puts out null. I have ftoa.class in the same directory by the way. Also, the server my school is using runs an older version of php, not sure which one. Also when I run the command from the php command line, it works fine.
$:php -a
php>$output = shell_exec("java ftoa 12");
php>echo $output;
php>(desired output)

Starting shell process under www-data (apache2, php)

I need to start php process from shell on remove server with some arguments, so i thought that it should be a nice idea to make REST API, that executes some function when user performs GET request.
I wrote a simple bash script for testing and figured out that command-line argument is not being specified, when calling this script from website:
shell_exec('/var/www/test.sh 123')
Bash script source:
#!/bin/sh
echo $1;
When calling this bash script from root (or other existing user) it correctly shows argument it has received. When i call this script from website (that is running under user www-data under apache2), it returns nothing:
Also, if i execute this bash script in my console under www-data user, it also returns nothing:
su -c '/var/www/test.sh 123' www-data
Also i've tried to start process from different user from php (is supposed that this will not work for security reasons, but just in case):
$result = system("su -c '/var/www/test.sh 123' sexyuser", $data);
// var_dump($result): string(0) ""
// var_dump($data): int(1)
So, what privileges should i give to www-data user to run process under php?
You should let php run the script and handle the results
check php.net on exec for example http://www.php.net/manual/en/function.exec.php
//called by example.com/myshell.php?day=today&k=y&whatever=youwant
$arguments = implode(" ", $_GET);
$lastline_of_exec_result = exec ( "/your/command.sh ".$arguments); //sh called with today y youwant
echo $lastline_of_exec;
Where $arguments are the stringified list of ALL information your script got from GET arguments
if you want a ore precise in and output, try this:
//called by example.com/myshell.php?day=today&k=y&whatever=youwant
$argument = $_GET['whatever'];
$output = array();
$last_line = exec("your/command.sh ".$argument, &$output); //sh called with youwant
foreach($output as $line)
echo $line."<br/>".PHP_EOL;
or of course (with shell_exec)
$argument = $_GET['whatever'];
$output = shell_exec("your/command.sh ".$argument);
echo "<pre>".$output."</pre>";
make sure (shell_)exec is not listed under disable_functions in your php.ini

Passing Admin username & password using shell_exec

I have a Powershell script which when I run directly on the Web Server (Windows 2008R2, IIS) completes successfully. I am now trying to launch the script from a php page. The script launches and I can see the process in Task Manager just fine.
When I call the script using shell_exec from a php page, it fails with the error
"Exception calling "FindOne" with "0" argument(s): "An operations error occurred."
I can see from Task Manager that the script is running as the user IUSR. I'm pretty sure that this is the reason the script is failing, as to complete successfully it needs to be run as a domain admin.
I am calling the script with the following command
$username = the currently logged in user on the page (authenticated against AD)
$newPword1 = new user password
$query = shell_exec("powershell -command $psScriptPath '$username' '$newPword1' < NUL");
Powershell Script:
#*=============================================================================
#* PARAMETER DECLARATION
#*=============================================================================
param([string]$username,[string]$pword)
#*=============================================================================
#* INITIALISE VARIABLES
#*=============================================================================
# Increase buffer width/height to avoid Powershell from wrapping the text before
# sending it back to PHP (this results in weird spaces).
$pshost = Get-Host
$pswindow = $pshost.ui.rawui
$newsize = $pswindow.buffersize
$newsize.height = 3000
$newsize.width = 400
$pswindow.buffersize = $newsize
#*=============================================================================
#* SCRIPT BODY
#*=============================================================================
$root = [ADSI]'LDAP://<server>/<DNtoOU>'
$searcher = new-object System.DirectoryServices.DirectorySearcher($root)
$searcher.filter = "(&(objectCategory=person)(sAMAccountName=$username))"
$user = $searcher.findone()
$userDN = $user.path
$user=[ADSI]$($userDN)
$user.SetPassword($pword)
$user.setinfo()
write-host "success"
Is it possible to pass a Domain Admin username/password to run powershell as using the above command, thereby allowing the script to run successfully?
One of several ways is to use PowerShellRunAs.
Edit: It is documented, you can use the powershell script as-is or modify it to suit your needs (e.g. not using a password file).
In more detail, instead of this:
shell_exec("powershell -command $psScriptPath …")
You could use this:
shell_exec("powershell -command "Start-Process powershell.exe -argumentlist '-command $psScriptPath …' -Credential 'TheDomain\TheUser'")
But to avoid the password prompt, use PowerShellRunAs as per the documentation:
shell_exec("powershell -command "Start-Process powershell.exe -argumentlist '-command $psScriptPath …' -Credential (.\PowerShellRunAs.ps1 -get contoso\svc_remoterestart \\fileserver\share\file.pwd)")
Alternatively, you could incorporate the start-process … -credential … into your own script. That way you can keep your shell_exec call simple as before, and only a limited part of your script will run under the user with different (higher) privileges.
Should you use the script as is, remember to first run it with -set instead of -get, in order to set the password file.

Categories