Skip to content

Instantly share code, notes, and snippets.

@cbj4074
Created November 1, 2016 20:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cbj4074/7b8b3509150d35a786a51dce395bf9c6 to your computer and use it in GitHub Desktop.
Save cbj4074/7b8b3509150d35a786a51dce395bf9c6 to your computer and use it in GitHub Desktop.
Demonstrates segfault that occurs with PHP 7.0.12 and PECL ssh2 extension, version 1.0
<?php
class ssh2
{
private $host = 'host';
private $user = 'user';
private $port = '22';
private $password = 'password';
private $con = null;
private $pubKeyFile = null;
private $privKeyFile = null;
private $shell_type = 'xterm';
private $shell = null;
private $log = array();
private $sftp = null;
private $knownHost = null;
function __construct($host = '', $port = '', $knownHost = NULL)
{
if ($host != '') {
$this->host = $host;
}
if ($port != '') {
$this->port = $port;
}
if (!is_null($knownHost)) {
$this->knownHost = $knownHost;
}
$this->con = ssh2_connect($this->host, $this->port, array('hostkey'=>'ssh-rsa,ssh-dss'));
if (!$this->con) {
$this->log[] = 'Connection failed!';
$this->con = NULL;
}
else {
//If a host fingerprint has been required, check it.
if (!is_null($this->knownHost)) {
$fingerprint = ssh2_fingerprint($this->con, SSH2_FINGERPRINT_MD5 | SSH2_FINGERPRINT_HEX);
if (is_string($fingerprint)) {
if (strcasecmp($fingerprint, $this->knownHost) != 0) {
$this->log[] = 'HOSTKEY MISMATCH! (Expected "' . $this->knownHost . '" and received "' . $fingerprint . '" [comparison is case-insensitive]; possible Man-In-The-Middle Attack?';
$this->con = NULL;
}
}
else {
$this->log[] = 'Could not determine remote host fingerprint (returned value was "' . $fingerprint . '"); aborting connection';
$this->con = NULL;
}
}
}
}
function __destruct()
{
$this->disconnect();
}
function getCon()
{
return $this->con;
}
function getSftp()
{
return $this->sftp;
}
function authKeyFile($user, $pubKeyFile, $privKeyFile)
{
if ($user != '') {
$this->user = $user;
}
if ($pubKeyFile != '') {
$this->pubKeyFile = $pubKeyFile;
}
if ($privKeyFile != '') {
$this->privKeyFile = $privKeyFile;
}
if (!ssh2_auth_pubkey_file($this->con, $this->user, $this->pubKeyFile, $this->privKeyFile)) {
$this->log[] = 'Public key authorization failed (public key "' . $this->pubKeyFile . '" and private key "' . $this->privKeyFile . '")!';
return FALSE;
}
else {
return TRUE;
}
}
function sftpCon()
{
global $startTime;
echo 'Calling ssh2_sftp(); time elapsed: ' . (microtime(true) - $startTime) . PHP_EOL;
$sftp = ssh2_sftp($this->con);
$this->sftp = $sftp;
}
function getLog()
{
return $this->log;
}
function disconnect()
{
ssh2_disconnect($this->con);
}
}
class SshTest
{
public static function connectToSshServer($server)
{
global $startTime;
//Establish an SSH session, so we can use file_exists()
//on the remote server (requires sftp wrapper).
echo 'Calling "new ssh2()"; time elapsed: ' . ((microtime(true) - $startTime)) . PHP_EOL;
$ssh = new ssh2($server['hostname'], '22', $server['fingerprint']);
echo 'Calling "$ssh->getCon()"; time elapsed: ' . (microtime(true) - $startTime) . PHP_EOL;
if ($ssh->getCon() !== NULL) {
//We were able to open an SSH session and the remote
//server has the fingerprint that we expect. It's safe
//to supply authentication credentials (as safe as possible, anyway).
echo 'Calling "$ssh->authKeyFile()"; time elapsed: ' . (microtime(true) - $startTime) . PHP_EOL;
if ($ssh->authKeyFile($server['sshUser'], $server['publicKey'], $server['privateKey'])) {
//If authenticaton was successful, establish
//an SFTP session so that sftp wrappers are available.
echo 'Calling "$ssh->sftpCon()"; time elapsed: ' . (microtime(true) - $startTime) . PHP_EOL;
$ssh->sftpCon();
}
}
echo 'Returning from SshTest::connectToSshServer(); time elapsed: ' . (microtime(true) - $startTime) . PHP_EOL;
return $ssh;
}
}
################################################################################
$testSshServers = array(
1 => array(
'hostname' => 'localhost',
'fingerprint' => 'DF551756B1D7AE3592AD574AD4D9F462',
'sshUser' => 'user',
'publicKey' => '/var/www/example.com/.ssh/user.pub',
'privateKey' => '/var/www/example.com/.ssh/user',
),
);
global $startTime;
$startTime = microtime(true);
$ssh = SshTest::connectToSshServer($testSshServers[1]);
var_dump($ssh);
echo 'Attempting ssh2_disconnect(); time elapsed: ' . (microtime(true) - $startTime) . PHP_EOL;
$ssh->disconnect();
echo 'Exiting; time elapsed: ' . (microtime(true) - $startTime) . PHP_EOL;
exit;
/*
Typical output:
php -f /var/www/example.com/web/php7-ssh2-segfault-test.php
Calling "new ssh2()"; time elapsed: 6.9141387939453E-6
Calling "$ssh->getCon()"; time elapsed: 0.054915904998779
Calling "$ssh->authKeyFile()"; time elapsed: 0.055253982543945
Calling "$ssh->sftpCon()"; time elapsed: 0.066303968429565
Calling ssh2_sftp(); time elapsed: 0.066792964935303
Returning from SshTest::connectToSshServer(); time elapsed: 0.25690102577209
object(ssh2)#1 (12) {
["host":"ssh2":private]=>
string(9) "localhost"
["user":"ssh2":private]=>
string(3) "user"
["port":"ssh2":private]=>
string(2) "22"
["password":"ssh2":private]=>
string(8) "password"
["con":"ssh2":private]=>
resource(4) of type (SSH2 Session)
["pubKeyFile":"ssh2":private]=>
string(29) "/var/www/example.com/.ssh/user.pub"
["privKeyFile":"ssh2":private]=>
string(25) "/var/www/example.com/.ssh/user"
["shell_type":"ssh2":private]=>
string(5) "xterm"
["shell":"ssh2":private]=>
NULL
["log":"ssh2":private]=>
array(0) {
}
["sftp":"ssh2":private]=>
resource(5) of type (SSH2 SFTP)
["knownHost":"ssh2":private]=>
string(32) "DF551756B1D7AE3592AD574AD4D9F462"
}
Attempting ssh2_disconnect(); time elapsed: 0.26224684715271
Exiting; time elapsed: 0.26915097236633
Segmentation fault (core dumped)
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment