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 😉