39

PHP 8: Override internal functions with `disable_functions` INI directive • PHP....

 3 years ago
source link: https://php.watch/articles/php8-override-internal-functions
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

PHP 8: Override internal functions with disable_functions INI directive

Published On2020-07-06

In PHP, it is possible to disable certain functions and classes with php.ini directives.

  • disable_functions: A comma-separated list of function names that PHP does not allow to be used.
  • disable_classes: Similar to functions, a list of classes that is now allowed to use.

These directives were used commonly to disable potentially unsafe functions from being used even if a malicious actor gets code-execution access in a server.

In PHP 8, disabled functions are not included in the functions table, which means function_exists() calls will return false for disabled functions, and most importantly, it will be possible to redefine the disabled functions.

Example

Here is an example of redefining sleep() function with a user-land implementation.

1. Create the new function definitions

Once we disable the functions, they will not be available to anywhere in the code under test. If you override certain features that your testing framework itself uses (such as microtime(), which is often used to calculate the execution time of tests), you will need to be careful to not break that functionality.

// test.php
function sleep() {
    return;
}

\sleep(1662815);

In a test.php file, we redefine the sleep() function to immediately return, without actually holding the execution.

Notice how we used a fully-qualified function name and it even works there.

2. Execute the script with disable_functions INI directive

php -d disable_functions=sleep test.php

When you run the code, PHP will first disable the internal sleep function, and due to PHP 8 changes, our custom test.php file is able to define a new sleep function.

Use cases

¯\(ツ)

It appears that disable_functions + redefine is the only way to effectively override a PHP internal function with user-land code.

Log/Fail unsafe internal functions

You can redefine functions such as system and create a log entry to detect whenever this function is used.

Note that language constructs, such as echo, include cannot be redefined. This unfortunately includes eval as well.

// troll.php
function system() {
    echo 'Ha!';
}
# php.ini file
[PHP]
disable_functions=system
auto_prepend_file=troll.php

Seamlessly polyfill legacy code with modern libraries

While it's not the most convenient approach, you can overwrite legacy functionality such as the whole mcrypt_* suite, or even mysql_* functions with modern libraries such as Sodium and PDO.

A user-land implementation that wraps an alternative library/extension with legacy function names can be technically done. Note that neither mcrypt, nor mysql extensions are in MySQL 8, so you are not actually redefining anything in this particular example.

function mcrypt_encrypt(string $cipher, string $key, string $data): string {
    return sodium_crypto_secretbox(...);
}

Make bad decisions

// hackers-go-brrrr.php
function password_hash(string $password): string {
    return md5($password);
}

function password_verify(string $password , string $hash): bool {
    return md5($password) === $hash;
}

var_dump(password_hash('hunter2'));
var_dump(password_verify('hunter2', '2ab96390c7dbe3439de74d0c9b0b1767'));
php -d disable_functions=password_hash,password_verify hackers-go-brrrr.php

More Sane approaches

Symfony PHPUnit Bridge Mocks

Symfony PHPUnit Bridge has ClockMock and DnsMock that dynamically declares time and DNS related features right before PHPUnit tests are run.

It is not possible to use fully-qualified function names with PHPUnit Bridge.

Autoload modified code

Another approach albeit being a bit more complicated, would be override the class autoloader mechanism, and load (automatically) modified code converts fully-qualified function names to namespaced function names, and define those functions in the same namespace.

PHP autoload override library does exactly that.

Advanced PHP Debugger

APD extension has features to override an internal function.

Updated

This post was updated updated on 2020 July 08. A prior version pointed that it was not possible to override certain functions that are inlined in PHP. This has then been fixed.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK