Recently I came across one of Securify‘s “spot the bug” challenges. The goal is to find one (or possibly more) critical vulnerabilities in the following code:
<?php if (empty($_POST['hmac'] || empty($_POST['host'])) { header('HTTP/1.0 400 Bad Request'); exit; } $secret = getenv("SECRET"); if (isset($_POST['nonce'])) $secret = hash_hmac('sha256', $_POST['nonce'], $secret); $hmac = hash_hmac('sha256', $_POST['host'], $secret); if ($hmac !== $_POST['hmac']){ header('HTTP/1.0 403 Forbidden'); exit; } echo exec("host "._POST['host']); ?>
The immediate line that sticks out to me is the last one — the exec() call. This function is used to execute an external program (its output will be displayed by the echo call). Thinking like an attacker, finding a way to somehow inject our own command into this exec call would give us the ability to directly interact with the underlying system. Let’s see how we can manipulate this value with the code available to us.
Starting from the top, the first if statement is simply a check to see if the host or hmac variables have not been set in the POST request. All it does is return a HTTP 400 error and then exit. Nothing of real interest here.
Next the secret key value is being set to equal some environment variable on the system named SECRET. Again, not much we can do here other than perhaps attempt to brute-force the secret key value, but this could take a lifetime.
If the user sets a nonce value, the script will call the hash_hmac function to return a keyed hash value using the nonce provided by the user. The hash_hmac function in PHP takes three parameters: the encryption algorithm, the data to encrypt, and the secret key.
This calculated value is then compared with an hmac value that the user provides in the POST request. This assumes that only a user with knowledge of the correct secret value would be able to generate a valid hash…
However, passing an array as the second argument causes the function to return NULL and display a warning message. What this means is that if we pass in an array as the nonce value in the POST request, the $secret value will always evaluate to NULL. We can fire up php in interactive mode to demonstrate the bug:
The second hash_hmac call will look something like this:
hash_hmac(‘sha256’, ‘hostvalue’, NULL);
Even with a secret value of NULL, the hash_hmac function still computes a value. This lets an attacker calculate the hash value for anything. If an attacker calls this hash_hmac function on their own machine, entering a command in place of the data parameter, a valid hash will be generated that will cause the php exec call to run the attackers input command. The command will have to be preceded by a ‘;’ character.
For example, to call the linux program whoami, an attacker would do the following:
1. Calculate the hash_hmac value for hash_hmac(‘sha256’, ‘;whoami’, NULL). This value will be passed to the web server in a POST request as the hmac value.
2. Pass ‘;whoami’ as the host value in the POST request.
3. Pass an array in for the nonce value in the POST request.
Leave a Reply