Snuffleupagus

sp

Snuffleu…what?

Let’s begin by a quote from the authors:

We’re hosting a lot of various █████████████ php applications using CMS ████████ ████████ ██████████████████ ████████████ ████████████, and we’d like to prevent our customers from being pwned.

Snuffleupagus is a PHP7+ extension which takes a lot of inspiration from Suhosin, a nice piece of software which used to work great for previous PHP versions with some limitations (no virtual patching for instance) but it unfortunately doesn’t for the v7 and beyond. It has two ways to mitigate issues:

  • kill classes of bugs,
  • patch PHP functions.

Mitigated bug classes are mail rce, weak prng, permissive chmod… These can be easily fixed using only one or two rules to address the whole bug family.

The virtual patching part is particularly interesting as it allows fine-grained settings: for instance I want to allow a call to system("id") but I don’t want to allow any other system calls. The rules would look like:

sp.disable_functions.function("system").param("cmd").value("id").allow();
sp.disable_functions.function("system").param("cmd").drop();

Since the rules are examined in order, we first allow a call to system with id as the cmd argument, and we then drop all other rules.

It’s also possible to write rules for a specific filename (filename(name)), hash (hash(sha256)), return value (ret(value)) or type (ret_type(type_name)), client ip (cidr(ip/mask))… Also, the behaviour can be adapted. If it triggers a rule:

  • drop(): drop the request,
  • simulation(): only log the event,
  • allow(): allow the request,
  • dump(): dump the request in a directory.

If an event is triggered, an entry in the PHP logfile is written, for instance:

2017/10/08 07:30:19 [error] 625#625: *54641 FastCGI sent in stderr: "PHP message: [snuffleupagus][0.0.0.0][include][drop]
Inclusion of a forbidden file (/a/path/to/a/webroot/../../../)" while reading response header from upstream,
client: <redacted>, server: example.com, request: "GET / HTTP/2.0", upstream: "fastcgi://unix:/var/run/php/php7.0-fpm.sock:", host: "example.com"

A nice way to start experimenting with your application is by using a script which parses your application PHP files, compute the hash of functions containing dangerous functions and generated rules based on the results: only the computed hashes will be allowed to execute these functions.


I'm managing several PHP applications, including some with particularly *high-security* requirements like [ToolsLib](https://toolslib.net). Others are a bit more specific, like the file hosting service [Up2Share](https://up2sha.re).

It’s currently deployed for all services in their staging environment without major issues in the last few weeks, so it’s now live on Up2Sha.re. I’m waiting a few annoying issues to be resolved before deploying it further.

So far, I haven’t observed any performances and issues nor spikes in resources between before and after the extension deployment but I haven’t tested with a huge set of rules - excercise left to the reader.

Being notified when SP triggers something (with Monit)

I also like using a watchdog to monitor my logfiles for specific events. I want to be alerted when a request triggers a SP drop() or simulate() rule. In this case I’m using monit and a simple configuration:

check file snuffleupagus with path /var/log/php/errors.log
if match "snuffleupagus" then alert

Coupled with a mail alert handler it’s then possible to be notified in my mailbox. The following email example shows the triggering rule:

Event : Content match - Service snuffleupagus
             Date:        Sun, 08 Oct 2017 07:30:49
             Action:      alert
             Serveur:        example
             Description:   content match:

...
2017/10/08 07:30:19 [error] 625#625: *54641 FastCGI sent in stderr: "PHP message: [snuffleupagus][0.0.0.0][include][drop]
Inclusion of a forbidden file (/a/path/to/a/webroot/../../../)" while reading response header from upstream,
client: <redacted>, server: example.com, request: "GET / HTTP/2.0", upstream: "fastcgi://unix:/var/run/php/php7.0-fpm.sock:", host: "example.com"
...

I’m also using a text-messaging handler to get these alerts I consider critical.

Pseudo management (with Puppet)

I’ve made a very simple Puppet manifest php_snuffleupagus to deploy Snuffleupagus on staging environments. It definitively needs some Puppet-fu love, feel free to clean and improve it:

php_snuffleupagus
├── files
│   ├── 99-snuffleupagus.ini
│   ├── snuffleupagus.ini
│   └── snuffleupagus.so
└── manifests
    ├── files.pp
    ├── init.pp
    └── services.pp

2 directories, 6 files
$ cat 99-snuffleupagus.ini

extension=snuffleupagus.so
sp.configuration_file=/etc/php/7.0/fpm/snuffleupagus.ini

$ cat snuffleupagus.ini

Your very own set of rules.

$ cat init.pp

class php_snuffleupagus {
    include php_snuffleupagus::files
    include php_snuffleupagus::services
}

$ cat files.pp

class php_snuffleupagus::files {

    file { 'snuffleupagus.so':
        path => "/usr/lib/php/20151012/snuffleupagus.so",
        source => "puppet:///modules/php_snuffleupagus/snuffleupagus.so",
        ensure => present,
    }

    file { '99-snuffleupagus.ini':
        path => "/etc/php/7.0/fpm/conf.d/99-snuffleupagus.ini",
        source => "puppet:///modules/php_snuffleupagus/99-snuffleupagus.ini",
        ensure => present,
    }

    file { 'snuffleupagus.ini':
        path => "/etc/php/7.0/fpm/snuffleupagus.ini",
        source => "puppet:///modules/php_snuffleupagus/snuffleupagus.ini",
        ensure => present,
    }

    file { '99-snuffleupagus.ini.cli':
        path => "/etc/php/7.0/cli/conf.d/99-snuffleupagus.ini",
        source => "puppet:///modules/php_snuffleupagus/99-snuffleupagus.ini",
        ensure => present,
    }

}

$ cat services.pp

class php_snuffleupagus::services {

    service { 'php7.0-fpm':
        name => "php7.0-fpm",
        ensure => running,
        enable => true,
        subscribe => [File['snuffleupagus.so'], File['snuffleupagus.ini'], File['99-snuffleupagus.ini']],
    }

}

So feel free to try it yourself especially since as written in the [FAQ](https://snuffleupagus.readthedocs.io/faq.html#should-i-use-snuffleupagus):

Please keep in mind that you are not only protecting yourself and your users/customers, but also other people on the internet that might be attacked by your server if it becomes compromised.

Last but not least, why Snuffleupagus? Quoting one of his authors in addition of being a nice piece of software, Snuffleupagus is…

an elephant as majestic as php itself