Yesterday, I was thinking about a simple way how to issue commands, list files, get files and access database on a remote system. With PHP all these things are simple to do and you don’t need to write TCP server, because it is provided by some HTTP daemon. It is also important that this could do only authenticated user. Next point was, that authenticated user can do anything on remote system and no complicated method implementation is needed on remote system. This might seem less secure but think about ssh… is it not similar?
Key generation
Nothing to say here, just commands:
openssl genrsa -out mykey.pem 2048 openssl rsa -in mykey.pem -pubout > mykey.pub
Client
Client application prepares a code that will be executed on server. Then it adds current timestamp to prevent replay attack. After that, data is encypted with private RSA key and posted to server. For better explanation see code:
$tosend='system ("find /etc/.");'; $tosend.="\n//RPC-TIMESTAMP:".time()."\n"; $pem=@file_get_contents("mykey.pem"); $privkey=openssl_pkey_get_private($pem); $data = openssl_private_encrypt($tosend, $crypted, $privkey); $url = 'http://example.com/rpc/'; $options = array( 'http' => array( 'header' => 'Content-type: application/octet-stream', 'method' => 'POST', 'content' => $crypted, ), ); $context = stream_context_create($options); $result = file_get_contents($url, false, $context); echo $result;
Server
Server simply recieves the data and decrypts them with public key. After decrypting, it checks if the timestamp is not older/younger than few seconds (yes, it has a downside of requiring very stable clock, timezone problems, but all these issues can be solved) . After this check, it executes requested code using eval(). eval() documentation says, that we should never evaluate user submitted data but I think that with using asymmetric cryptography we can be pretty sure that code doesn’t come from an attacker.
$data = @file_get_contents('php://input'); $pem=@file_get_contents("mykey.pub"); $pubkey=openssl_pkey_get_public($pem); openssl_public_decrypt($data , $decrypted , $pubkey); preg_match("/RPC-TIMESTAMP:(\d+)/",$decrypted, $matches); $timestp=$matches[1]; if (abs($timestp-time())>5){ echo "timestamp out of range!"; } else { eval($decrypted); }
Please share your comments or thoughts on this concept in comments.