SSH2 and PHP not working 'Terminal' - php

Whenever I try and execute
if (!($stream = ssh2_exec($con, "cd $dir; screen -S creative java -Xmx500M -Xms500M -jar spigot.jar" ))) {
it keeps returning with 'Must be connected to a terminal.' which I can't seem to get around.
How do I get around/fix this?

You could try to use phpseclib's read() / write() functions. eg.
<?php
include('Net/SSH2.php');
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh->login('username', 'password')) {
exit('Login Failed');
}
echo $ssh->read('username#username:~$');
$ssh->write("ls -la\n"); // note the "\n"
echo $ssh->read('username#username:~$');

Test this :
You should pass a pty emulation name ("vt102", "ansi", etc...) if you want to emulate a pty, or NULL if you don't.
Passing false will convert false to a string, and will allocate a "" terminal.
cf. http://php.net/manual/fr/function.ssh2-exec.php

Starting screen like you did requires a valid TTY because of it get's attached to your session. The ssh implementation of PHP just doesn't create a session like that.
Starting screen detached should do the trick:
screen -d -m [<command>]
So your code would look like:
if (!($stream = ssh2_exec($con, "cd $dir; screen -d -m -S creative java -Xmx500M -Xms500M -jar spigot.jar" ))) {

Related

Running Google Chrome headless with PHP exec doesn’t return output till IIS restarted

My environment is Windows Server 2016 and IIS 10. In my PHP script I’m trying to run Google Chrome in a headless mode to get html code of an external web page:
<?php
$chromeApp = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe";
$command = "\"$chromeApp\" --headless --disable-gpu \
--dump-dom $urladdress > page.html";
exec ($command);
?>
That code works if I run
>C:\php script.php
from the Command line. It also works if I run the actual command:
>"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" \
--headless --disable-gpu --dump-dom https://google.com > page.html
But if I run that script from a browser it creates empty page.html file and hungs till timeout. However if I restart IIS during its execution I get the page.html file filled with the needed data.
What could be a problem here?
this is not an answer, but too much to put in a comment, exec() doesn't really give much feedback,
first don't do this:
$command = "\"$chromeApp\" ";
because different shells can't agree on how stuff should be quoted, so you should use the escapeshellarg() function instead, also don't do this
--dump-dom $urladdress > page.html
because $urladdress may need to be escaped (and if hackers are able to control your $urladdress, then this is actually an arbitrary code execution vulnerability), do this instead:
$command = escapeshellarg($chromeApp)." --headless --disable-gpu \
--dump-dom ".escapeshellarg($urladdress)." > page.html";
(and if your page.html may have names with special characters too, you should run that name through escapeshellarg() as well.)
but replace exec() with proc_open, tell me what you get from running this:
<?php
declare(strict_types=1);
$urladdress="http://google.com";
$chromeApp = _cygwinify_filepath("C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe");
$command = escapeshellarg($chromeApp)." --headless --disable-gpu --dump-dom ".escapeshellarg($urladdress);
$descriptorspec = array(
0 => array("pipe", "rb"), // by default stdin is inherited, we don't want that so we create a stdin pipe just so we can fclose() it.
1 => array("pipe", "wb"), // stdout
2 => array("pipe", "wb"), // stderr
);
$proc=proc_open($command,$descriptorspec,$pipes);
if(!$proc){
throw new \RuntimeException("failed to create process! \"{$command}\"");
}
$stdout="";
$stderr="";
$fetch=function()use(&$stdout,&$stderr,&$pipes){
$tmp=stream_get_contents($pipes[1]);
if(is_string($tmp) && strlen($tmp) > 0){
$stdout.=$tmp;
}
$tmp=stream_get_contents($pipes[2]);
if(is_string($tmp) && strlen($tmp) > 0){
$stderr.=$tmp;
}
};
fclose($pipes[0]);
$status=array();
while(($status=proc_get_status($proc))['running']){
$fetch();
}
$fetch();
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($proc);
var_dump($stdout,$stderr,$status);
function _uncygwinify_filepath(string $path) : string
{
static $is_cygwin_cache = null;
if ($is_cygwin_cache === null) {
$is_cygwin_cache = (false !== stripos(PHP_OS, "cygwin"));
}
if ($is_cygwin_cache) {
return trim(shell_exec("cygpath -aw " . escapeshellarg($path)));
} else {
return $path;
}
}
function _cygwinify_filepath(string $path) : string
{
static $is_cygwin_cache = null;
if ($is_cygwin_cache === null) {
$is_cygwin_cache = (false !== stripos(PHP_OS, "cygwin"));
}
if ($is_cygwin_cache) {
return trim(shell_exec("cygpath -a " . escapeshellarg($path)));
//return "/cygdrive/" . strtr($path, array(':' => '', '\\' => '/'));
} else {
return $path;
}
}
edit: i wrote use(&$stdout,$stderr,&$pipes) instead of use(&$stdout,&$stderr,&$pipes), sorry, fixed. re-run it if you just ran it without this fix.
You have 4 processes in play here.
W3WP.exe
PHP.exe
CMD.exe
Chrome.exe
CMD.exe is taking the output of Chrome.exe and piping it to your file. It will do that upon completion of Chrome.exe or may do it partially intermittently. When I run similar code to yours above, my Chrome.exe does not finish. I can see Chrome.exe still running in TaskManager consuming 25% CPU (100% on one of my cores).
I'm guessing restarting IIS somehow forces the flush in progress of the commands. In most of my cases, there was data inside the page.html file prior to doing IISReset, thought not all of it. (Windows Explorer showed 0KBs, but opening the file showed data in the file nonetheless).
As for things to try, try at --no-sandbox as an argument as that may be interfering since the process is running under a non-interactive session.

phpseclib ssh2 enable tty

my ssh script:
include('Net/SSH2.php');
set_include_path(getcwd());
$ssh_host = "myserver.com";
if($ssh_host != null){
$ssh_user = globals::$ssh_logins[$ssh_host];
$ssh = new Net_SSH2($ssh_host);
//$ssh->enablePTY();
if (!$ssh->login($ssh_user['username'], $ssh_user['password'])) {
echo $ssh->isConnected() ? 'bad username or password' : 'unable to establish connection';
}
echo $ssh->exec('sudo bash /myscript.sh');
}else{
echo "no host: " . $ssh_host;
}
when calling:
echo $ssh->exec('sudo bash /myscript.sh');
i get the output:
stdin: is not a tty
sudo: sorry, you must have a tty to run sudo
how do i enable tty on phpseclib?
Sudo is necessary also it is important to retrieve the output. my script returns multiple lines.
Centos 7
PHP 7
interesting link: https://unix.stackexchange.com/questions/136676/sudo-sorry-you-must-have-a-tty-to-run-sudo-when-using-sudo-in-a-remote-scrip?newreg=f31852b195804fd1b988aeb0a9ce59d9
You're on the right track with your $ssh->enablePTY() but once you do that you'll need to do $ssh->read(). Without a PTY $ssh->exec() will return the commands output. If you do $ssh->enablePTY() you'll need to do $ssh->read() to get output.
The reason for this is that with a PTY data can be sent to and from the shell whereas it can't without a PTY. Without a PTY the output you get is the output you get and no input can change that because you can't send input. With a PTY input can change the output.
Also relevant, phpseclib's docs talk about doing sudo with it. From http://phpseclib.sourceforge.net/ssh/examples.html#sudo :
<?php
include('Net/SSH2.php');
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh->login('username', 'password')) {
exit('Login Failed');
}
echo $ssh->read('username#username:~$');
$ssh->write("sudo ls -la\n");
$output = $ssh->read('#[pP]assword[^:]*:|username#username:~\$#', NET_SSH2_READ_REGEX);
echo $output;
if (preg_match('#[pP]assword[^:]*:#', $output)) {
$ssh->write("password\n");
echo $ssh->read('username#username:~$');
}
If your account doesn't have a password I guess it doesn't matter but if you do have a password then you'll need to do something like the above.

Loop bash script until cURL response doesn't equal string

I'm trying to loop a bash script (cURL below) until my remote PHP script doesn't equal failure. I've tried the following, and it works when $fileName exists.. but doesn't loop when it returns failure.
My PHP:
<?php
$videoFile = $_GET["name"] . ".mp4";
if (file_exists($videoFile)) {
echo "success";
} else {
echo "failure";
}
?>
Bash command:
until $(curl --output /dev/null --silent --head --fail "my-remote-php-script.php"); do
printf '.'
sleep 1
done
This should work:
while [ "$(curl -s 'http://your-url.php')" == "failure" ]; do
echo -n '.'
sleep 1
done
The $() places the curl in a sub-shell. The resulting string, comming from your php script, is compared with failure.
Try this:
url="http://server.tld/foo.php"
until curl -sf "$url"; do echo -n "."; sleep 1; done

stream_set_timeout not working for ssh2_exec in PHP

I am having trouble when try to set timeout for my ssh2_exec connection in PHP, so that when there is something wrong when running the command, it will release the executing thread and will not jam the whole web site. I have tried stream_set_timeout($stream,$seconds) but it seems not to work as expected.
Is there any ideas on this?
//run specified command (ssh)
function runCMD($cmd){
if (!($stream = ssh2_exec($this->conn, $cmd )))
{
echo "fail: unable to execute command\n";
fclose($stream);
return ERROR_SSH_EXEC_COMM;
}
sleep(1);
stream_set_timeout($stream,5);//>>not work
stream_set_blocking($stream, true);
$res = stream_get_contents($stream);
return $res;
}
For anyone wondering how to set the timeout, there's a default_socket_timeout variable in php.ini that can be used to set the timeout for any socket-based connections in your current session.
You can check it by simply running
$timeStart = time();
ini_set("default_socket_timeout", 5);
$ssh = ssh2_connect('123.123.123.123');
echo (time() - $timeStart), PHP_EOL;
Here's how you could do that with phpseclib, a pure PHP SSH2 implementation:
<?php
include('Net/SSH2.php');
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh->login('username', 'password')) {
exit('Login Failed');
}
$ssh->setTimeout(5);
echo $ssh->exec('whatever');
?>
If you want to execute subsequent commands after that one you'll need to do $ssh->reset().
AFAIK, There are no true 'timeout' implementations in the php ssh2 specs.
The only solution that I have found for this problem can be found in this post here:
PHP ssh2_connect() Implement A Timeout

Escaping quotes in a PHP script using ssh2_exec

I need some help with escaping the quotes in this script, I have code above and below, but it does not pertain to this because if I just issue a screen -d -m -S minecraft, it creates one fine. Any help would be amazing because i've been trying for an hour now, Thanks!
if (!($stream = ssh2_exec($con, 'screen -d -m -S minecraft -p 0 -X stuff "\printf "\say This is a test.\r"\"\' ))) {
echo "fail: unable to execute command\n";
} else {
// collect returning data from command
stream_set_blocking($stream, true);
$data = "";
while ($buf = fread($stream,4096)) {
$data .= $buf;
}
echo $data;
fclose($stream);
}
Shouldn't the \ should go before the ". Also, from your description, it sounds like that you only want to run "screen -d -m -S minecraft" and it looks like you have an extra printf in there.
if (!($stream = ssh2_exec($con, "screen -d -m -S minecraft")))
http://php.net/manual/en/language.types.string.php
You only need to change "\ to \".

Categories