Blog

How to encrypt and execute your PHP code with MCRYPT or OPENSSL

Hello!

While the scenario may not necessarily be common in which you would want to encrypt your PHP code and execute it, it is something that I would consider an interesting discussion nonetheless.

I fully support free and open source software, however if you are developing an application that manages or monitors systems or services or an application that needs to reside in a “hostile” environment, it might be pertinent to consider encrypting the code before executing it. This protects your code from even being read (and ultimately executed) unless the proper key is passed in order to decrypt it.

In the following example & breakdown, we will be (separately) using both the Mcrypt and OpenSSL to encrypt a block of code with a specified key. We will then use that same key to decrypt the encrypted code in order to run it. The reason why I am giving both examples is, as some users have already pointed out, Mcrypt is being deprecated in PHP 7 and ultimately removed in PHP 7.2.

Its important to note that the key can be passed as a POST variable (which incidentally are not logged by default with most web services like apache or nginx), or it can be passed as a GET variable or any other way really. In our example we will hard code the key in the code to keep things simple.

The first thing we want to do is define our key variable that we will be using to encrypt everything and then the IV size for the encryption method and strength :

Define the encryption key and cipher

For MCRYPT you would define the key and cipher this way :

$key = '1234567891011120';
define('IV_SIZE', mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));

For OPENSSL you would define the key the same way, and the cipher would be declared in the actual openssl_encrypt function (shown later) :

$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));

The above stipulates that we will be using AES 128bit encryption for mcrypt and AES 256bit encryption with openssl, both with cipher block chaining (CBC). Obviously the key is not really that secure, you would want something a bit stronger than just numeric value, but you get the idea.

Encrypt your PHP code

The next thing we want to do is define two functions : one to encrypt input and one to decrypt. We will define the encrypt function first and really this could be part of a separate class or PHP file since we would only have to encrypt the PHP code once.

To encrypt your code with Mcrypt :

function encrypt($key, $payload) {
  $iv = mcrypt_create_iv(IV_SIZE, MCRYPT_DEV_URANDOM);
  $crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $payload, MCRYPT_MODE_CBC, $iv);
  $combo = $iv . $crypt;
  $garble = base64_encode($iv . $crypt);
  return $garble;
}

To encrypt your code with OpenSSL :

function encrypt($key, $payload) {
  $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
  $encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv);
  return base64_encode($encrypted . '::' . $iv);
}

You can see for both examples that ultimately we are encrypting the functions input ($payload) using either the mcrypt_encrypt or openssl_encryptfunction. Further down in both functions you can see that we are also encoding the encrypted content with the base64_encode function, and returning the encoded data.

So what you would want to do is pass whatever php code to this encrypt function and save the output. Since we are ultimately going to be running eval function against this encrypted code, you shouldn’t need to include encapsulating php open/close tags, but that may also depend on your php version and web server environment.

Once you have the php code encrypted and saved (perhaps in a text file in the same folder as this script), you can then work on the bulk of the actual operation of this exercise.

Decrypt your PHP code

Lets establish a decrypt function that we can use to interpret and decrypt the code.

With Mcrypt :

function decrypt($key, $garble) {
  $combo = base64_decode($garble);
  $iv = substr($combo, 0, IV_SIZE);
  $crypt = substr($combo, IV_SIZE, strlen($combo));
  $payload = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $crypt, MCRYPT_MODE_CBC, $iv);
  return $payload;
}

With OpenSSL :

function decrypt($key, $garble) {
    list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2);
    return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
}

You can see that for both the Mcrypt and OpenSSL decrypt functions, it is very similar to the encrypt functions (except in reverse). First we want to use base64_decode to decode the data, then either the mcrypt_decrypt or openssl_decrypt functions to decrypt the data. The data is then returned for us to do whatever we want, which is to execute it.

Execute your decrypted PHP code

In our example, we will simply take the $payload variable returned by the decrypt function and execute it.

$code = "paste encrypted string here";
eval(decrypt($key, $code));

For the $code variable, you could simply paste the encrypted string of text and assign it to this variable. You might want to load it from a file or a remote location. It really doesn’t matter as long as its assigned to this variable.

Then we simply use the eval function to execute it. But the eval has a nested execution of the decrypt function inside it so that the code can be decrypted and returned before eval tries to decrypt it. You can also see that we are applying the $key variable in order to decrypt it.

Alternative ways to pass your encryption key to decrypt and execute your PHP code

If you didn’t want to hard code the $key in your own code (why would you, it would defeat the purpose!), you could pass it as a $_POST variable. Again HTTP POSTs are typically not logged by default in Apache, Nginx or most web services. HTTP GETs are indeed logged so that would open passing the variable to exposure. Of course if your website is encrypted with HTTPS then your variables should be protected further.

If you wanted to run this as a console application, then it could simply be passed as a command line argument.

To pass it as a POST, then you could simply do something along the lines of this :

if (!empty($_POST['dec'])) {
	// Regardless of what the POST was, set it to the cookie. If it isn't the right key, it wont decrypt anyways :)
        setcookie("dec", $_POST['dec'], time() + 3600);
        $_COOKIE['dec'] = $_POST['dec'];
        header("Refresh:0");
}
if (isset($_COOKIE['dec'])) {
	// If the cookie is set , try decrypting and running
        $key = $_COOKIE['dec'];
	$code = "paste encrypted string here";
        eval(decrypt($key, $code));
} else {
	// If no cookie is set or POST received, unset the cookie variables if they already (for some reason) exist
	// Also deliver the input form by default.
        setcookie("dec", "", time() - 3600);
        $_COOKIE['dec'] = null;
        echo '


';
}

Whats going on in the above snippet? Well we’re listening for a POST variable called dec. We have implemented some straightforward logic to establish a session cookie that carries the key within the cookie. If the key is present the value is used to attempt to decrypt the code and execute it. If the key doesn’t match, nothing happens.

If nothing matches then the cookie is cleared (if its even present). This is such a simple and straightforward example, you could add more protections and / or other strategies with how you could pass the key to the code in order to decrypt it. Anything is possible!

I hope this has been useful, its certainly an exercise in how to use encryption with PHP and if for nothing else will help people understand the types of mechanisms that are built into modern web frameworks like Laravel by default.