Nginx and PHP fastcgi, security through obscurity or full disclosure?

Lulu's picture

This article covers an issue which is common to any nginx and php-fcgi default installation. I wonder why it wasnt covered at nginx wiki and completely ignored over the net, except very few writings mostly in russian language and few mentions on nginx forum.

I'd like to skip introduction here and just give this link tips on building a high load webserver , with php-fpm included into core PHP distribution the number of installations will grow, some distributions already started to provide php5-fpm packages, such as Ubuntu maverick, i am not aware if they ship vulnerable config examples or make any comments in the stock configs, i just dont recommend you to use Ubuntu at all, but if you search for HOWTO's on the net or look into vendor shipped defaults, you will find:

  • nginx-0.9.4 default config example:

  • # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    # root html;
    # fastcgi_pass 127.0.0.1:9000;
    # fastcgi_index index.php;
    # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
    # include fastcgi_params;
    #}

    While Igor Sysoev does not recommend using that piece of config and advises to use @try_files directive,
    it is still in default config example and even more, its in almost every howto article you'll find around the net. The basic of that config section is clear: any request that is ended at .php will be passed to fastcgi_pass specified location (an PHP interpreter). Yes, it does look normal, and its just like it should be.

    Lets continue with PHP:

    Since PHP has implemented CGI and FastCGI, there are some control variables, they have their default values, so if you dont specify a value in php.ini , a reasonable default will be used. Yes. Thats okey, that means to be normal and its just like it should be. The interesting for us variable is cgi.fix_pathinfo , which is set to 1 by default, it means that PHP interpreter will fix (rewrite) pathinfo to passed script, it may be has been needed for CGI, but FastCGI is not like CGI and it is not needed for nginx + php-fpm. The main idea is: PHP will fix path info by default.
    Another interesting thing for PHP is: PHP will look for <?php ANY CODE HERE; ?> pieces of code inside any files.

    Okey, you just got your shiny new server up and configured, it is using nginx+php-fpm and proudly says that it footer or its somehow else discoverable. Most likely you will allow upload of something to your server, the most obvious example - users can register at your CMS or forum and upload an small picture as their avatar, an innocent thing ? Yes! Its just an example, any upload will go, the uploaded file must be available under PHP-enabled server root (you dont have separate subdomain configured for it? yes, thats the simplest approach to do not have one).

    And now lets do some artwork, some special avatar, thats it , do you like it? It is uploaded in /sites/default/files/elwy_E.gif , just right there! For your CMS or forum it will be another path, but it can be still under the same DOCUMENT_ROOT that is served by PHP as well.
    How easy to "craft" such a picture? Just look - , quite easy isnt it? ;) It is a valid picture, that will be nicely displayed by browsers and will not look anyhow suspicious, i'll remind - it can be just any file, the avatar picture is just the most innocent example of allowed uploads, you can use comment fields, exif tags, or just append code at the file end, PHP will find it.... and execute... What ? unexpected? Your server config just passes only .php to PHP ? Of course nobody will let .gif .jpg or .png be executed by PHP, but default nginx config and PHP pathinfo rewrite will do the trick for you. If you can access http://flexichat.net/sites/default/files/elwy_E.gif , you can try to access it as PHP http://flexichat.net/sites/default/files/elwy_E.gif/hackme-scotty.php , this path does not exists, but an request (it ends at .php) will be passed to PHP by nginx, and PHP will fix the pathinfo... and look for code in avatar pic instead

    Thats it! I just pointed my FireFox to that url, I see some junk... but.. see phpinfo() output ? Yes, the code inside picture was sucessfully found and executed by PHP interpreter, and it was just an innocent phpinfo(); now imagine system("rm -rf ~/*"); or a series of shell commands which will fetch authorized ssh key, or otherwise do more harmful (for you) things that just revealing your phpinfo() ?

    Chapter Next: "How do i live now?"

    First, thats just an article, its better to learn lessons reading than dealing with the apocalypse that was made on your server.
    Second, learn to search for info, and... do not blindly trust every HOWTO, the most sad thing here is that... you cant even use snippets of the default configs.
    Howto fix? Easy, set in your php.ini cgi.fix_pathinfo=0 and PHP will not rewrite paths anymore, the second solution is to make nginx check for passed requests, yes, use @try_files.

    I hope you've enjoyed this article, no matter if you use nginx+PHP-fcgi or not, its a nice demonstration of how multiple default conditions can help gaining unauthorized access in easy way.

    Got comments?

    PS: Of course the "hackme-up-scotty.php" link does not work, dont you expect from me to leave that issue unpatched ? ;) So its 404, just as it should be.

    Update: almost the same issue with "too much trust" in parseable URL, one could append %00.php to uploaded file and ask nginx to pass it to PHP interpreter (affect nginx versions 0.5, 0.6, 0.7 < 0.7.66, and
    0.8 < 0.8.38), more information in RedHat bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=717078
    Solutions: 1) dont run old nginx 2) prohibit php execution in folders with user uploadable files.

    Another Update: PHP 5.4 FPM SAPI introduced config file directive security.limit_extensions which prevents php-fpm from executing other than allowed files. Please note - this is optional.

    Netster's picture

    I am feeling safe :D

    I am feeling safe :D hahahahaha

    <?php Thank_you; ?>

    Lulu's picture

    <?php printf('%s', "Thank

    <?php printf('%s', "Thank you"); ?>

    something like this ) or if Thank_you is defined as function, then you'll need () ,

    Thank_you();

    ;)

    Comment viewing options

    Select your preferred way to display the comments and click "Save settings" to activate your changes.

    Post new comment

    The content of this field is kept private and will not be shown publicly.
    • Web page addresses and e-mail addresses turn into links automatically.
    • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img><i><b><h1><h2><h3>
    • Lines and paragraphs break automatically.

    More information about formatting options

    CAPTCHA
    This question is for testing whether you are a human visitor and to prevent automated spam submissions. There is no CAPTCHA shown for registered and logged in users.
    Image CAPTCHA
    Enter the characters shown in the image.