Call a PERL script which connect to a host with SSH - php

I've created a Perl script which connects to a host and executes some commands, and it works fine! I'm kinf of proud 'cause I'm a real newb with Perl ^^...
A overview of the perl script:
use Expect;
$|=0;
$Expect::Debug=0;
$Expect::Exp_Internal=0;
$Expect::Log_Stdout=1;
my $ip = $ARGV[0];
my $file = $ARGV[1];
my $username = $ARGV[2];
my $password = $ARGV[3];
open(CONF,$file) || die "File not found";
while(<CONF>){
$con .= $_;
}
my #conf = split("#",$con);
my $ssh = Expect->spawn("ssh -q -l $username $ip") || die "Spawn ssh failed, $!";
if($ssh->expect(5,"yes")) {
print $ssh "yes\r";
if($ssh->expect(10,"assword")) {
print $ssh "$password\r";
}
else {
warn $ssh->exp_error()."\n";
next;
}
}
elsif($ssh->expect(10,"assword")) {
print $ssh "$password\r";
}
else {
warn $ssh->exp_error()."\n";
next;
}
#Variables Globales
my $rcmd;
my #lcmd;
my $lrcmd;
$regExpCmd = "\#";
$regExpCmd2 = "^(A|B).*(\$|\#)";
$regExp = "\n";
$ssh->expect(10,$regExpCmd);
my $cmd0 = "environment no more\r";
my $cmdExit = "logout\r";
$ssh->send($cmd0);
$ssh->expect(5,$regExpCmd);
foreach my $step (#conf) {
my #lines = split("\n",$step);
foreach my $val (#lines){
$val =~ s/^\s+//;
$val =~ s/\r//;
$ssh->send("$val\r");
$i *= 1;
if(!$ssh->expect(2,$regExpCmd2)){
$i *= 0;
# if($ssh->expect(1,"MINOR")){
# die "Erreur mineur: $val";}
if($ssh->expect(2,"Error")){
die "Erreur majeur: $val";
}
}
}
$ssh->expect(1,$regExpCmd2);
}
$ssh->send($cmdExit);
print $i;
Now, I'd like to call it from PHP...
I have tried different way:
Like calling my perl script with the exec() function :
<?php
$arg1 = "MY.ADD.IP";
$arg2 = "MY/FILE";
$arg3 = "USERNAME";
$arg4 = "PASSWORD";
$result = exec("perl /path/of/perl/script.pl $arg1 $arg2 $arg3 $arg4");
if($result == 1) {
return true: }
else {
return false;
} ?>
but it is not doing anything (Checked on the remote host and so SSH connexion at all)...
I also tried using the PECL Perl interpreter for PHP, calling my script like that:
<?php
$perl = new Perl();
$perl->require('myperl.pl'); ?>
but I didn't figure how to send some arg to my script..
The fact is that I need to call it with an jQuery $.ajax request and I need to wait for the end of the script before sending back any "answer" to jQuery.
Everything I tried did not work, as the PHP script ends "before" the Perl Script...
PS: I also tried to create a Package in PERL called with PHP, like below:
package Connect;
sub new{
#Init some var... }
sub connect {
#Something like the script above.....
}
<?php
$perl = new Perl();
$perl->require('myscript.pl');
$perl->call('connect',$args);
?>
Have you ever succeeded in something like that? I really don't know what to do :(

Why don't you use ssh from php? It looks like the ssh part would be easier than what you've done in perl, and you can still get the perl regexes using preg_ functions.
PHP.net ssh2 manual page
PHP.net preg_match manual page

phpseclib, a pure PHP SSH implementation, has something very similar to expect.
An example follows:
<?php
include('Net/SSH2.php');
$sftp = new Net_SSH2('www.domain.tld');
$sftp->login('username', 'password');
echo $sftp->read('username#username:~$');
$sftp->write("sudo ls -la\n");
$output = $sftp->read('#Password:|username#username:~\$#', NET_SSH2_READ_REGEX);
echo $output;
if (preg_match('#Password:#', $lines)) {
$ssh->write("password\n");
echo $sftp->read('username#username:~$');
}
?>
It does "sudo ls -la" and waits for either "Password:" or "username#username:~".

Related

shell_exec not giving any result, and not doing anything

So I have question which in my head should seem very simple to solve.
I want to ssh to a server, which I have done a ton of times, and then make a shell execute which I have done a ton of times as well, but it is not working.
The code i am using
<?php
$ip = '1.2.3.4';
$cmd = "ssh user#".$ip;
$result = shell_exec($cmd." 'sudo /bin/systemctl stop wildfly.service'");
echo "<pre>output: $result</pre>";
echo "<div class='alert alert-success'><strong>SUCCESS</strong><br>Wildfly node has now restarted</div>";
?>
Running the command directly from the terminal
ssh user#1.2.3.4 sudo /bin/systemctl stop wildfly.service
It works, but running it within php gives me nothing, and it not doing anything.
Can someone maybe guide me to what I am doing wrong with my shell_exec?
Thanks in advance!
function execPrint($command) {
try {
$result = array();
exec($command.' 2>&1', $result);
foreach ($result as $line) {
print($line . "\n");
}
echo '------------------------' . "\n" . "\n";
} catch (\Exception $e) {
print($e);
}
http_response_code(200);
}
i made this function to get result
add 2>&1 in last of the CMD
use print with every line
use try and catch to catch any error
The user attempting to execute those shell commands from php is likely _www and not you. Try this code in your php to gain insight:
$shellscript = 'whoami';
$sr = shell_exec($shellscript);
echo '['.$sr.']';
Make sure the shell_exec function is not disabled. It usually is disabled by default in CPanel accounts PHP.ini and PHP-FPM .ini files.
You can check it using this validation
if (is_callable('shell_exec') && (false === stripos(ini_get('disable_functions'), 'shell_exec'))) {
echo "shell_exec enabled";
} else {
echo "shell_exec disabled";
}
It's the most common reason i've found for shell_exec to return always empty
You can also execute a quick command for testing purpouses
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
echo "Command: shell_exec('ls -lart')";
try {
$output = shell_exec('ls -lart');
echo "<pre>$output</pre>";
} catch (Exception $e) {
echo $e->getMessage();
}

PHP Telnet/SSH dynamic login

I have an issue that's stumped me.
I'm trying to automate a CLI login to a router and run some commands obtained via a webpage. However I don't know if the router has telnet or SSH enabled (might be one,the other, or both) and I have a list of possible username/password combos that I need to try to gain access.
Oh, and I can't change either the protocol type or the credentials on the device, so that's not really an option.
I was able to figure out how to login to a router with a known protocol and login credentials and run the necessary commands(included below), but I don't know if I should use an if/else block to work through the telnet/ssh decisions, or if a switch statement might be better? Would using Expect inside PHP be an easier way to go?
function tunnelRun($commands,$user,$pass, $yubi){
$cpeIP = "1.2.3.4";
$commands_explode = explode("\n", $commands);
$screenOut = "";
$ssh = new Net_SSH2('router_jumphost');
if (!$ssh->login($user, $pass . $yubi)) {
exit('Login Failed');
}
$ssh->setTimeout(2);
$ssh->write("ssh -l username $cpeIP\n");
$ssh->read("assword:");
$ssh->write("password\n");
$ssh->read("#");
$ssh->write("\n");
$cpePrompt = $ssh->read('/.*[#|>]/', NET_SSH2_READ_REGEX);
$cpePrompt = str_replace("\n", '', trim($cpePrompt));
$ssh->write("config t\n");
foreach ($commands_explode as $i) {
$ssh->write("$i\n"); // note the "\n"
$ssh->setTimeout(2);
$screenOut .= $ssh->read();
}
$ssh->write("end\n");
$ssh->read($cpePrompt);
$ssh->write("exit\n");
echo "Router Update completed! Results below:<br><br>";
echo "<div id=\"text_out\"><textarea style=\" border:none; width: 700px;\" rows=\"20\">".$screenOut."</textarea></div>";
Update:
The solution I went with was a while/switch loop. I would of gone the Expect route, but I kept running into issues on getting the Expect module integrated into PHP on my server (Windows box.) If I had been using a Unix/Linux server Expect would of been the simplest way to achieve this.
I just made it into a working demo for now, so there are a lot of variations missing from the case statements still, and error-handling still needs to bef figured out, but the basic idea is there. I still want to move the preg_match statements around a bit more to do the matching at the top of the while loop (so I don't spam the whole case section with different preg_match lines), but that may prove to be more work than I want for now. Hope this might help someone else trying to do the same!
<?php
include('Net/SSH2.php');
define('NET_SSH2_LOGGING', NET_SSH2_LOG_COMPLEX);
ini_set('display_errors', 1);
$conn = new Net_SSH2('somewhere.outthere.com');
if (!$conn->login($user, $pass . $yubi)) {
exit('Login Failed');
}
$prompt = "Testing#";
$conn->setTimeout(2);
$conn->write("PS1=\"$prompt\"");
$conn->read();
$conn->write("\n");
$screenOut = $conn->read();
//echo "$screenOut is set on the shell<br><br>";
echo $login_db[3][0]. " ". $login_db[3][1];
$logged_in = false;
$status = "SSH";
$status_prev = "";
$login_counter = 0;
while (!$logged_in && $login_counter <=3) {
switch ($status) {
case "Telnet":
break;
case "SSH":
$conn->write("\n");
$conn->write("ssh -l " . $login_db[$login_counter][0] . " $cpeIP\n");
$status_prev = $status;
$status = $conn->read('/\n([.*])$/', NET_SSH2_READ_REGEX);
break;
case (preg_match('/Permission denied.*/', $status) ? true : false):
$conn->write(chr(3)); //Sends Ctrl+C
$status = $conn->read();
if (strstr($status, "Testing#")) {
$status = "SSH";
$login_counter++;
break;
} else {
break 2;
}
case (preg_match('/[pP]assword:/', $status) ? true : false):
$conn->write($login_db[$login_counter][1] . "\n");
$status_prev = $status;
$status = $conn->read('/\n([.*])$/', NET_SSH2_READ_REGEX);
break;
case (preg_match('/yes\/no/', $status) ? true : false):
$conn->write("yes\n");
$status_prev = $status;
$status = $conn->read('/\n([.*])$/', NET_SSH2_READ_REGEX);
break;
case (preg_match('/(^[a-zA-Z0-9_]+[#]$)|(>)/', $status,$matches) ? true : false):
$conn->write("show version\n");
$status = $conn->read(">");
if(preg_match('/ADTRAN|Adtran|Cisco/', $status)? true:false){
$logged_in = true;
break;
}
default:
echo "<br>Something done messed up! Exiting";
break 2;
}
//echo "<pre>" . $conn->getLog() . "</pre>";
}
if ($logged_in === true) {
echo "<br> Made it out of the While loop cleanly";
} else {
echo "<br> Made it out of the While loop, but not cleanly";
}
echo "<pre>" . $conn->getLog() . "</pre>";
$conn->disconnect();
echo "disconnected cleanly";
}
?>
If statements might make your code become unreadable.
In that case I would suggest you to use switch-case blocks,
since switch case will allow you to write clearer code, and will allow you to catch exceptional values more efficiently.
Using Expect in php is simple:
<?php>
ini_set("expect.loguser", "Off");
$stream = fopen("expect://ssh root#remotehost uptime", "r");
$cases = array (
array (0 => "password:", 1 => PASSWORD)
);
switch (expect_expectl ($stream, $cases)) {
case PASSWORD:
fwrite ($stream, "password\n");
break;
default:
die ("Error was occurred while connecting to the remote host!\n");
}
while ($line = fgets($stream)) {
print $line;
}
fclose ($stream);
?>
There are some complication using the expect file_wrapper. If it were me, I'd just go for a simple socket connection for telnet and poll for the prompts (with a timeout) if the ssh connection fails.
On a casual inspection, the telnet client here seems to be sensibly written - and with a bit of renaming could provide the same interface as the ssh2 client extension (apart from the connect bit).

Process multiple PHP scripts in one array

I have multiple php scripts to ping each of our locations, and I'm trying to list all results on one page.
Here's the ping script:
<?php
$host = "10.10.10.10"; //IP adress to ping
$loc = ("HQ"); //Name of location
$output = array();
echo("<b>$loc</b> <i>(IP: $host)</i> is ");
exec("ping -n 1 $host 2>&1", $output);
//print_r($output);
//you can use print_r($output) to view the output result
if (count($output) > 7) {
$output = null;
die ("<font color='green'><b>up</b></font>");
}
else {
$output = null;
die ("<font color='red'><b>down</b></font>");
}
?>
So, I have many php files with this script, where the only difference is host and loc.
I've tried to include each file in a new php file using include like this:
<?php
include "file1.php";
include "file2.php";
include "file3.php";
...and so on...
?>
But this only outputs the result of the first file.
How can I do this in any other way?
Thanks!
die(), or its equivalent exit() ends the script. Simply change die() to echo as in:
if (count($output) > 7) {
$output = null;
echo "<font color='green'><b>up</b></font>";
} else {
$output = null;
echo "<font color='red'><b>down</b></font>";
}

How to mimic a command line run with arguements inside a PHP script?

How can you mimic a command line run of a script with arguements inside a PHP script? Or is that not possible?
In other words, let's say you have the following script:
#!/usr/bin/php
<?php
require "../src/php/whatsprot.class.php";
function fgets_u($pStdn) {
$pArr = array($pStdn);
if (false === ($num_changed_streams = stream_select($pArr, $write = NULL, $except = NULL, 0))) {
print("\$ 001 Socket Error : UNABLE TO WATCH STDIN.\n");
return FALSE;
} elseif ($num_changed_streams > 0) {
return trim(fgets($pStdn, 1024));
}
}
$nickname = "WhatsAPI Test";
$sender = ""; // Mobile number with country code (but without + or 00)
$imei = ""; // MAC Address for iOS IMEI for other platform (Android/etc)
$countrycode = substr($sender, 0, 2);
$phonenumber=substr($sender, 2);
if ($argc < 2) {
echo "USAGE: ".$_SERVER['argv'][0]." [-l] [-s <phone> <message>] [-i <phone>]\n";
echo "\tphone: full number including country code, without '+' or '00'\n";
echo "\t-s: send message\n";
echo "\t-l: listen for new messages\n";
echo "\t-i: interactive conversation with <phone>\n";
exit(1);
}
$dst=$_SERVER['argv'][2];
$msg = "";
for ($i=3; $i<$argc; $i++) {
$msg .= $_SERVER['argv'][$i]." ";
}
echo "[] Logging in as '$nickname' ($sender)\n";
$wa = new WhatsProt($sender, $imei, $nickname, true);
$url = "https://r.whatsapp.net/v1/exist.php?cc=".$countrycode."&in=".$phonenumber."&udid=".$wa->encryptPassword();
$content = file_get_contents($url);
if(stristr($content,'status="ok"') === false){
echo "Wrong Password\n";
exit(0);
}
$wa->Connect();
$wa->Login();
if ($_SERVER['argv'][1] == "-i") {
echo "\n[] Interactive conversation with $dst:\n";
stream_set_timeout(STDIN,1);
while(TRUE) {
$wa->PollMessages();
$buff = $wa->GetMessages();
if(!empty($buff)){
print_r($buff);
}
$line = fgets_u(STDIN);
if ($line != "") {
if (strrchr($line, " ")) {
// needs PHP >= 5.3.0
$command = trim(strstr($line, ' ', TRUE));
} else {
$command = $line;
}
switch ($command) {
case "/query":
$dst = trim(strstr($line, ' ', FALSE));
echo "[] Interactive conversation with $dst:\n";
break;
case "/accountinfo":
echo "[] Account Info: ";
$wa->accountInfo();
break;
case "/lastseen":
echo "[] Request last seen $dst: ";
$wa->RequestLastSeen("$dst");
break;
default:
echo "[] Send message to $dst: $line\n";
$wa->Message(time()."-1", $dst , $line);
break;
}
}
}
exit(0);
}
if ($_SERVER['argv'][1] == "-l") {
echo "\n[] Listen mode:\n";
while (TRUE) {
$wa->PollMessages();
$data = $wa->GetMessages();
if(!empty($data)) print_r($data);
sleep(1);
}
exit(0);
}
echo "\n[] Request last seen $dst: ";
$wa->RequestLastSeen($dst);
echo "\n[] Send message to $dst: $msg\n";
$wa->Message(time()."-1", $dst , $msg);
echo "\n";
?>
To run this script, you are meant to go to the Command Line, down to the directory the file is in, and then type in something like php -s "whatsapp.php" "Number" "Message".
But what if I wanted to bypass the Command Line altogether and do that directly inside the script so that I can run it at any time from my Web Server, how would I do that?
First off, you should be using getopt.
In PHP it supports both short and long formats.
Usage demos are documented at the page I've linked to. In your case, I suspect you'll have difficulty detecting whether a <message> was included as your -s tag's second parameter. It will probably be easier to make the message a parameter for its own option.
$options = getopt("ls:m:i:");
if (isset($options["s"] && !isset($options["m"])) {
die("-s needs -m");
}
As for running things from a web server ... well, you pass variables to a command line PHP script using getopt() and $argv, but you pass variables from a web server using $_GET and $_POST. If you can figure out a sensible way to map $_GET variables your command line options, you should be good to go.
Note that a variety of other considerations exist when taking a command line script and running it through a web server. Permission and security go hand in hand, usually as inverse functions of each other. That is, if you open up permissions so that it's allowed to do what it needs, you may expose or even create vulnerabilities on your server. I don't recommend you do this unless you'll more experienced, or you don't mind if things break or get attacked by script kiddies out to 0wn your server.
You're looking for backticks, see
http://php.net/manual/en/language.operators.execution.php
Or you can use shell_exec()
http://www.php.net/manual/en/function.shell-exec.php

Creating IP layer packets in PHP?

I'm wondering if it possible to create network layer packets (i.e. define my own IP headers) using PHP? It seems like socket_create with SOCK_RAW only lets you define the contents of the IP packet, not the headers itself.
Thanks in advance for your replies!
I was able to successfully create a socket using SOCK_RAW on Mac OS X, as long as I ran the script as root.
The example I used was taken from Jean Charles MAMMANA's ping.inc.php
I created a ping.php wrapper, and executed: sudo ping.php www.google.com.
Here's my ping.php wrapper:
<?php
$default_timeout = 15;
require("ping.inc.php");
if (count($argv) < 2) usage();
$timeout = count($argv) >= 3 ? intval($argv[2]) : $default_timeout;
$host = $argv[1];
$result = ping($host, $timeout);
if ($result < 0) {
echo "Error: " . $g_icmp_error . "\n";
} else {
echo "$result ms\n";
}
function usage() {
global $argv;
echo "Usage: {$argv[0]} <host> [timeout]\n";
die();
}

Categories