Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

phpMyAdmin 5 eval silently failing #425

Open
spillkiss opened this issue Jun 20, 2022 · 6 comments
Open

phpMyAdmin 5 eval silently failing #425

spillkiss opened this issue Jun 20, 2022 · 6 comments

Comments

@spillkiss
Copy link

Been using snuffleupagus for some time, and I see that phpMyAdmin 5.2.0 silently fails to load with a single rule:

sp.disable_function.function("eval").drop(); or
sp.disable_function.function("eval").allow(); or
sp.disable_function.function("eval").drop().simulate(); or
sp.disable_function.function("eval").filename_r(".*").allow(); or
sp.disable_function.function("eval").filename_r("var/www/phpmyadmin/.*$").allow();
No error is logged, and I get a blank page with 200 status.

An invalid conf rule such as:
sp.disable_function.function("eval").simulate();
will correctly log as:
[20-Jun-2022 16:09:20 UTC] PHP Fatal error: [snuffleupagus][1.2.3.4][config][log] Unexpected keyword 'simulate' on line 1 in Unknown on line 0

So the question is: How may I selectively enable eval for phpmyadmin?

I'm using snuffleupagus v0.8.2, and I tried this with php7.4-fpm and php-cgi7.4.

By disabling this single eval rule, the phpMyAdmin 5 page(s) will load.
The eval rule might be triggered because of this.

Also found these conversations which may be related:
Snuffleupagus writable execution of eval'd code ? #409
Is there any chance I can avoid using eval in Twig? #2428

@jvoisin
Copy link
Owner

jvoisin commented Jun 20, 2022

The following should work:

sp.disable_function.function("eval").filename_r("^/var/www/phpmyadmin/").allow();
sp.disable_function.function("eval").drop();

it's even tested in the testsuite

You might also want to check out eval white and blacklist.

@jvoisin jvoisin self-assigned this Jun 20, 2022
@jvoisin jvoisin added this to the 0.9.0 - Elephant Gambit milestone Jun 20, 2022
@spillkiss
Copy link
Author

Your suggested rules should have but did not work. I've certainly seen your suggested rules work plently of times, just not on phpMyAdmin 5. I've successfully compiled and configured snuffleupagus at least 7 different instances prior to this and what I'm experiencing now is unusual.

I tried reconfiguring snuffleupagus with flags --enable-debug --enable-debug-stderr hopefully to get more info on what's going on, testing using PHP via CLI and a test.php file with the following: <?php echo "success\n"; . I made sure PHP's CLI has snuffleupagus as the only module and that the php.ini is the default from install, except it includes error logging to a file.

1. When the conf file snuffleupages.rules has a single rule sp.disable_function.function("eval").drop(); and I run php test.php, the output is:

[snuffleupagus][DEBUG] zm_globals_ctor_snuffleupagus(): (GINIT)
[snuffleupagus][DEBUG] zm_startup_snuffleupagus(): (MINIT)
[snuffleupagus][DEBUG] OnUpdateConfiguration(): module name? cli
success
[snuffleupagus][DEBUG] zm_shutdown_snuffleupagus(): (MSHUTDOWN)
[snuffleupagus][DEBUG] zm_globals_dtor_snuffleupagus(): (GSHUTDOWN)

The error log remains empty.

2. Changing snuffleupages.rules for the single rule to be commented out like so #sp.disable_function.function("eval").drop(); outputs:

[snuffleupagus][DEBUG] zm_globals_ctor_snuffleupagus(): (GINIT)
[snuffleupagus][DEBUG] zm_startup_snuffleupagus(): (MINIT)
Could not startup.
[snuffleupagus][DEBUG] zm_shutdown_snuffleupagus(): (MSHUTDOWN)
[snuffleupagus][DEBUG] zm_globals_dtor_snuffleupagus(): (GSHUTDOWN)

And the error log:

[21-Jun-2022 15:07:14 UTC] PHP Fatal error:  [snuffleupagus][0.0.0.0][config][log] Parser error on line 171 in Unknown on line 0
[21-Jun-2022 15:07:14 UTC] PHP Fatal error:  [snuffleupagus][0.0.0.0][config][log] Invalid configuration file in Unknown on line 0

Oddly, if i included extra line(s) at the end of the *.rules file the script execution succeeds.

3. To rule out modifications I've made to the operating environment, I repeated the test scenario above (ensuring only snuffleupagus and test.php file) on a fresh "micro" instance. Initially I was unable to replicate the issue operating entirely from the command line.

However if I run od -x /etc/php/7.4/mods-available/snuffleupages.rules:
on the successful machine there is LF at the tail, whereas on the offending machine the file ends with NULL.

Results in "success":

od -x /etc/php/7.4/mods-available/snuffleupagus.rules 
0000000 7323 2e70 6964 6173 6c62 5f65 7566 636e
0000020 6974 6e6f 662e 6e75 7463 6f69 286e 6522
0000040 6176 226c 2e29 7264 706f 2928 0a3b
0000056

Results in "Could not startup.":

od -x /etc/php/7.4/mods-available/snuffleupagus.rules 
0000000 7323 2e70 6964 6173 6c62 5f65 7566 636e
0000020 6974 6e6f 662e 6e75 7463 6f69 286e 6522
0000040 6176 226c 2e29 7264 706f 2928 003b
0000055

I copied over the offending snuffleupages.rules file and was able to replicate this issue on the new vm instance. So, transferring the configuration file from an IDE fails, but modifying the configuration on the command line succeeds (tried vim, nano).

I really don't know, but to me it looks like the php configuration parser is flubbing this. If the *.rules ends with a non-parseable rule (even if it's a comment), then the configuration fails to load WHEN it is uploaded by an IDE that terminates the file with a NULL byte. This is easily corrected by opening the *.rules file in vim or nano, simply saving the file without making any changes and closing the file.

4. Back full circle to the trouble I was having upstairs: I'm still not able to get phpMyAdmin 5.2.0 to work using your suggested rule(s). I've had trouble getting this to work in the past and I tossed the idea since v4 works just fine. I really appreciate snuffleupagus, the clear documentation, and utility that it provides to harden PHPSEC, so I gave it a bigger effort this time around.

Unfortunately, the way I am installing phpMyAdmin 5.2.0 does not fall in line with their official installation instructions.
Here's how I'm doing it:

wget https://files.phpmyadmin.net/phpMyAdmin/5.2.0/phpMyAdmin-5.2.0-english.zip
unzip phpMyAdmin-5*.zip
mv phpMyAdmin-5.2.0-english /var/www/phpmyadmin && cd /var/www/phpmyadmin
wget https://getcomposer.org/composer.phar
php composer.phar install

Despite it being a completely functional phpMyAdmin 5, since it's not the official way of doing the installation and PHP exits silently when snuffleupagus attempts to conditionally disable eval(), I guess this is the end of the line regarding this.

For what it's worth, it appears that the snufflepagus rule sp.disable_function.function("eval").drop(); makes PHP silently exit when it first reaches this line. I don't have the tools or knowhow/where to investigate this further, so best I can do is list replication steps.

@jvoisin
Copy link
Owner

jvoisin commented Jun 22, 2022

@bef since you refactored the rules processing, can you take a look at this one?

@jvoisin
Copy link
Owner

jvoisin commented Jun 27, 2022

Looks like it chokes on register_shutdown_function, which looks like an horrible edge-case that Snuffleupagus doesn't handle :'(

@jvoisin
Copy link
Owner

jvoisin commented Jun 27, 2022

Also, how the fuck is register_shutdown_function([Config::class, 'fatalErrorHandler']); valid php code? The first parameter for register_shutdown_function is callable, not an array, but it turns out " A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1. Accessing protected and private methods from within a class is allowed. "

What an horrible language.

@bef
Copy link
Collaborator

bef commented Jul 17, 2022

I can confirm that register_shutdown_function() works perfectly fine with eval() disabled. (Btw. passing an array as callable with class/function has been around for a while)
I can also confirm, that NUL-bytes are not allowed in INI files except at the logical beginning of a new rule, where it declares the end of the configuration.
And selectively allowing filenames or other attributes, then dropping all other calls is a default use case that works for me, too.

So, whatever this is, it needs further investigation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants