Alles in Deckung, ich mache wieder unschönes Zeugs. Ich hatte ja bereits erklärt wie man über RPC/Samba einen Windows-PC von Linux herunterfahren kann. Leider ist die Installation von Samba aus Embedded-Systemen eine eher ungünstige Sache und ist in meinem Fall wegen Speichermangel nicht drin. Allerdings ist sowohl auf dem Embedded-System als auch dem betroffenen Rechner PHP verfügbar. I aim to misbehave.
Auf dem herunterzufahrenden Windows-Rechner wird per PHP ein TCP-Socket geöffnet. Dieser Code basiert auf einem Beispiel von Michael Kliewe. Um Scriptkiddies nicht direkt Zugang zu geben ist etwas (sehr schlechte) Challenge-Response-Authentifizierung drin – nunja, ist kein kritisches System. Wird der Client bestätigt folgt ein simples Shutdown per exec. Nicht viel, aber schnell Fertig und funktioniert:
<?php
class SocketChatServer {
private $address = '0.0.0.0'; // 0.0.0.0 means all available interfaces
private $port = 12345; // the TCP port that should be used
private $maxClients = 10;
private $clients;
private $socket;
private $salt = 'YourSalt';
private $pass = 'YourSecret';
public function __construct() {
// Set time limit to indefinite execution
set_time_limit(0);
error_reporting(E_ALL ^ E_NOTICE);
}
public function start() {
// Create a TCP Stream socket
$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// Bind the socket to an address/port
socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($this->socket, $this->address, $this->port);
// Start listening for connections
socket_listen($this->socket, $this->maxClients);
$this->clients = array('0' => array('socket' => $this->socket));
while (true) {
// Setup clients listen socket for reading
$read[0] = $this->socket;
for($i=1; $i<count ($this->clients)+1; ++$i) {
if($this->clients[$i] != NULL) {
$read[$i+1] = $this->clients[$i]['socket'];
}
}
// Set up a blocking call to socket_select()
$ready = socket_select($read, $write = NULL, $except = NULL, $tv_sec = NULL);
/* if a new connection is being made add it to the client array */
if(in_array($this->socket, $read)) {
for($i=1; $i < $this->maxClients+1; ++$i) {
if(!isset($this->clients[$i])) {
$this->clients[$i]['socket'] = socket_accept($this->socket);
socket_getpeername($this->clients[$i]['socket'], $ip);
$this->clients[$i]['ipaddy'] = $ip;
socket_write($this->clients[$i]['socket'], 'Welcome to the Shutdown Control System'."\r\n");
socket_write($this->clients[$i]['socket'], 'Your personal code is '.md5($this->salt.strftime('%Y-%m-%d').$ip)."\r\n");
$this->log("New client #$i connected: " . $this->clients[$i]['ipaddy']);
break;
} elseif($i == $this->maxClients - 1) {
$this->log('Too many Clients connected!');
}
if($ready < 1) {
continue;
}
}
}
// If a client is trying to write - handle it now
for($i=1; $i<$this->maxClients+1; ++$i) {
if(in_array($this->clients[$i]['socket'], $read)) {
$data = @socket_read($this->clients[$i]['socket'], 1024, PHP_NORMAL_READ);
if($data === FALSE) {
unset($this->clients[$i]);
$this->log('Client disconnected!');
continue;
}
$data = trim($data);
if(!empty($data)) {
switch ($data) {
case 'exit':
case 'quit':
socket_write($this->clients[$i]['socket'], "Disconnecting without any action, Goodbye.\r\n");
$this->log("Client #$i is exiting");
unset($this->clients[$i]);
continue;
case 'shutdown-'.md5($this->pass.md5($this->salt.strftime('%Y-%m-%d').$ip)):
// first write a message to all connected clients
for($j=1; $j < $this->maxClients+1; ++$j) {
if(isset($this->clients[$j]['socket'])) {
if($this->clients[$j]['socket'] != $this->socket) {
socket_write($this->clients[$j]['socket'], "System will be shut down now...\r\n");
}
}
}
// Close the master sockets, server termination requested
exec('shutdown /s /t 15 /c "System wurde per Fernbedienung ausgeschaltet"');
$this->log("Terminated server (requested by client #$i)");
exit;
case 'whoami':
// first write a message to all connected clients
socket_write($this->clients[$i]['socket'], "You are registred with the source IP ".$this->clients[$i]['ipaddy']." - your Code was ".md5($this->salt.strftime('%Y-%m-%d').$ip)." \r\n");
break(2);
default:
for($j=1; $j < $this->maxClients+1; ++$j) {
if(isset($this->clients[$j]['socket'])) {
if(($this->clients[$j]['socket'] != $this->socket)) {
$this->log($this->clients[$i]['ipaddy'] . ' is sending a message to ' . $this->clients[$j]['ipaddy'] . '!');
socket_write($this->clients[$j]['socket'], '[' . $this->clients[$i]['ipaddy'] . '] says: ' . $data . "\r\n");
}
}
}
break(2);
}
}
}
}
} // end while
}
private function log($msg) {
// instead of echoing to console we could write this to a database or a textfile
echo "[".date('Y-m-d H:i:s')."] " . $msg . "\r\n";
}
}
$srv = new SocketChatServer;
$srv->start();
?>
Der Client bastelt sich eine passende Authentifizierung und spielt Zündknopf:
function pc_shutdown() {
$pass = 'YoutSecret';
$fp = fsockopen('pc.name', 12345, $errno, $errstr, 2);
if(!$fp) return false;
$line = fgets($fp); //Greeting
$line = fgets($fp); //Hash
preg_match('/code is ([\w\d]{32})/', $line, $match);
$seed = $match[1];
$cmd = 'shutdown-'.md5($pass.$seed)."\n";
fwrite($fp, $cmd);
var_dump(fgets($fp));
fclose($fp);
}
Hoffen wir, dass es läuft und ich nie wieder drauf schauen muss, denn irgendwo tut sowas selbst mir weh 😉