GithubHelp home page GithubHelp logo

jvoisin / snuffleupagus Goto Github PK

View Code? Open in Web Editor NEW
731.0 39.0 83.0 13.3 MB

Security module for php7 and php8 - Killing bugclasses and virtual-patching the rest!

Home Page: https://snuffleupagus.readthedocs.io

License: GNU Lesser General Public License v3.0

Makefile 0.70% PHP 48.45% M4 0.43% C 48.31% Python 0.19% Shell 0.01% C++ 1.91%
php7 security hardening c php elephant php-module security-hardening

snuffleupagus's Introduction


Snuffleupagus' logo
Snuffleupagus

Security module for php7 and php8 - Killing bugclasses and virtual-patching the rest!

Testing PHP7 on various Linux distributions Testing PHP8 on various Linux distributions Coverity CII Best Practises readthedocs.org coveralls twitter Packaging status CodeQL

Key FeaturesDownloadExamplesDocumentationLicenseThanks

Snuffleupagus is a PHP 7+ and 8+ module designed to drastically raise the cost of attacks against websites, by killing entire bug classes. It also provides a powerful virtual-patching system, allowing administrator to fix specific vulnerabilities and audit suspicious behaviours without having to touch the PHP code.

Key Features

Download

We've got a download page, where you can find packages for your distribution, but you can of course just git clone this repo, or check the releases on github.

Examples

We're providing various example rules, that are looking like this:

# Harden the `chmod` function
sp.disable_function.function("chmod").param("mode").value_r("^[0-9]{2}[67]$").drop();

# Mitigate command injection in `system`
sp.disable_function.function("system").param("command").value_r("[$|;&`\\n]").drop();

Upon violation of a rule, you should see lines like this in your logs:

[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in /var/www/index.php:2, because the return value (0) of the function 'strpos' matched a rule.

Documentation

We've got a comprehensive website with all the documentation that you could possibly wish for. You can of course build it yourself.

Thanks

Many thanks to:

snuffleupagus's People

Contributors

arpd avatar avkarenow avatar bef avatar bohwaz avatar brown-midas avatar cgzones avatar crkontrol avatar iamluc avatar jakubonderka avatar jvoisin avatar karantan avatar kjojo-zz avatar kkadosh avatar mdeous avatar mdplusplus avatar patrickallaert avatar petecooper avatar pfdutot avatar remicollet avatar rowdyelectron avatar smagnin avatar szepeviktor avatar timgates42 avatar tomcodes avatar travispaul avatar tristan971 avatar vedranmiletic avatar wargio avatar whitewinterwolf avatar xxx-caillou-xxx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

snuffleupagus's Issues

Weird behavior on bad regular expression test in cookies

Having the following test :

$ cat tests/encrypt_regexp_cookies_bad_regexp.phpt 
--TEST--
Cookie decryption in ipv4
--SKIPIF--
<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
--INI--
sp.configuration_file={PWD}/config/config_encrypted_regexp_cookies_bad_regexp.ini
--COOKIE--
super_cookie=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP3gV9YJZL/pUeNAjCKFW0U2ywmf1CwHzwd2pWM=;awful_cookie=awful_cookie_value;
--ENV--
return <<<EOF
REMOTE_ADDR=127.0.0.1
HTTP_USER_AGENT=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/59.0.3071.109 Chrome/59.0.3071.109 Safari/537.36
EOF;
--FILE--
<?php var_dump($_COOKIE); ?>
--EXPECT--
[snuffleupagus][0.0.0.0][config][error] Failed to compile '^super_co[a-z+$': missing terminating ] for character class on line %d.
[snuffleupagus][0.0.0.0][config][error] '.name_r()' is expecting a valid regexp, and not '"^super_co[a-z+$"' on line %d.
array(0) {
}

with the associated .ini (notice the invalid regexp) :

sp.global.secret_key("abcdef").cookie_env_var("REMOTE_ADDR");
sp.cookie.name_r("^super_co[a-z+$").encrypt();
sp.auto_cookie_secure.enable();

Leads to the fail testing with the following .out :

array(2) {
  ["super_cookie"]=>
  string(92) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP3gV9YJZL/pUeNAjCKFW0U2ywmf1CwHzwd2pWM="
  ["awful_cookie"]=>
  string(18) "awful_cookie_value"
}

While launching by hand gives the expected result :

$ sh tests/encrypt_regexp_cookies_bad_regexp.sh 
[snuffleupagus][0.0.0.0][config][error] Failed to compile '^super_co[a-z+$': missing terminating ] for character class on line 2.
[snuffleupagus][0.0.0.0][config][error] '.name_r()' is expecting a valid regexp, and not '"^super_co[a-z+$"' on line 2.
array(0) {
}

Shall we deploy rules files in the packages?

Since we didn't manage to internally reach an acceptable consensus about this, we're asking the community: There are currently no well accepted usage with regard to where to place non-.ini files for php, since every other module is using .ini. Snuffleupagus is special, as it uses .rules ones. So the question is:

Shall we place the default.rules files on the filesystem during the installation of the packages, and if yes, where?

@fichtner, @fr33tux, … any opinion about this?

Implement `simulation` on cookie encryption

This could be useful for two use-cases:

  1. When deploying Snuffleupagus on big CMS like Magento, and not knowing what cookies are modified via javascript.
  2. When deploying Snuffleupagus on big websites: you don't want to disconnect every single user at once.

When simulation is enabled, if the decryption fails, a log message should be issued, and the cookie value taken as it (since odds are that it's non-encrypted).

Add a `.line` filter

I'm currently trying to patch a vulnerability that looks like this:

$var = $_GET['pif'];
// […]
echo "<b>" . $var . "</b>";

The obvious way to patch it would be something like sp.disabled_function.function("echo").var("_GET[pif]").value_r("[^a-z0-9]").drop();, but since echo is used a lot in the file, and not all the paths are malicious/exploitable, it would be nice to be able to match on line numbers too.

It's in a CMS, so if the lines are moving, odds are that the vuln is patched anyway. This would also speed up the matching, and allow a more fine-grained rules

Show the problematic line number when parsing an invalid config file

This is what we're currently displaying when something goes wrong with the configuration parsing:

jvoisin@mim 15:09 ~/Dev/snuffleupagus cat src/tests/example_configuration.out
[snuffleupagus][0.0.0.0][error][error] There is an issue with the parsing of '"^[^\"]+$").drop();': it doesn't look like a valid string.
[snuffleupagus][0.0.0.0][config][error] '.value_r()' is expecting a valid regexp, and not '"^[^\"]+$"'.

We should at least show the line number.

Allow hooking of language constructs

  • array
  • die
    • This is equivalent to exit, which boils down to basically printing the parameter if it's not a number, and calling the unhookable function zend_bailout.
  • echo
  • eval There is an extensions to precisely do this
  • exit
  • print
  • include/include_once/require/require_once
  • isset
  • list
  • unset
  • is_array / is_bool / is_double / is_float / is_int / is_integer / is_long / is_null / is_object / is_real / is_resource / is_string
  • is_countable / is_iterable for PHP 7.3?

Reduce the size of the encrypted cookie on the wire

Currently, we're passing something like this on the wire: base64(000…00||C), instead of base64(C), making our cookie look like this: super_cookie=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP3gV9YJZL/pUeNAjCKFW0U2ywmf1CwHzwd2pWM=, instead of super_cookie=P3gV9YJZL/pUeNAjCKFW0U2ywmf1CwHzwd2pWM=

This is because libnacl needs this padding:

Similarly, ciphertexts in the C NaCl API are 0-padded versions of messages in the C++ NaCl API. Specifically: The crypto_secretbox function ensures that the first crypto_secretbox_BOXZEROBYTES bytes of the ciphertext c are all 0.

We should not transmit the padding, but prefix C with it right before decryption.

Shall we rename `disabled_function` to `virtual_patching` ?

@watw00t pointed out that sp.disable_function.function("exec").filename("Cache/MySQL.php").hash("3c5e41[…]e8444cc").allow(); is a bit confusion, since we're not disabling anything, but are allowing a call to a function.

What about using virtual_patching or something like that instead?

Fuzz the configuration parser

It would be fun/nice to fuzz a bit our configuration parser, either via the int sp_parse_config(const char *conf_file) function, or via static int parse_line(char *line), with something like libfuzer.

This won't have any security implication, but since users are usually super-creative people, I would prefer to find crashes before they do.

build error on FreeBSD 11

Hi,

First of all congrats to 0.1.0 and thank you for making that happen! :)

A FreeBSD port was created today:

https://svnweb.freebsd.org/ports?view=revision&revision=457156

There is one slight issue in the build addressed by:

https://svnweb.freebsd.org/ports/head/security/snuffleupagus/files/patch-sp__network__utils.c?view=markup&pathrev=457156

It seems that s6_addr32 is simply not provided to userspace in FreeBSD and may need an if-else guard or similar like currently commented out. Or it could be defined if not already defined by the userspace?

Cheers,
Franco

Support reload

  • We do need to walk the linked-list of sp_node, and free their members, in the PHP_MSHUTDOWN_FUNCTION(snuffleupagus) function.
  • Partially done in 1476b0d

Shall we cache data during rules check

Currently, when we check if a given function matches our set of rules, we're recomputing the file hash every time there is a rule that needs it. Shall we cache it?

I do think we should do it:

  • It will reduce the window for possible race-condition, where an attacker could swap the current while with a known whitelisted one.
  • It will greatly improve performances for people using out magical script to whitelist every single version of wordpress stuff

Though?

Fix harden_rand

Apparently, travis is failling on harden_rand in a reproductive way.

I took a look at it, but wasn't able to figure out what's going on :/

Check for owner in terminate_if_writable

The implementation is currently only limiting by the mode of the file, but not the ownership.

https://github.com/nbs-system/snuffleupagus/blob/3c14dba94e837322e030b19b41654952624cb485/src/sp_execute.c#L12-L29

It's still possible the file that is executed in read-only mode has been written by www-data if the file was chmod'd afterwards.

# touch snuffle
# python
[...]
>>> 
>>> os.access('snuffle', os.W_OK)
True
>>> 
>>> # prepare for execution
>>> os.chmod('snuffle', 0o444)
>>> os.access('snuffle', os.W_OK)
False
>>> 
>>> # prepare for write
>>> os.chmod('snuffle', 0o600)
>>> os.access('snuffle', os.W_OK)
True
>>> 

I don't think this is realistic in a real world setting though, as somebody would need legit code that is abusable in a way to remove the write permissions after the file has been written. Might be worth it to check for the owner regardless as it would be a non-invasive solution for that problem. :)

Do we want to implement a `bailout` option?

Currently, the .drop() option will change the call to a nop (except when applied on .ret, where it'll effectively bailout). Do we want to add a .bailout() to effectively bailout (as in stop processing, call zend_bailout`)?

Make sure that snuffleupagus works on any operating systems

We should leverage GitLab CI/CD for external repositories to test Snuffleupagus in various dockers.

Linux

BSD

  • FreeBSD: Because "a lot" of people are using it, HardenedBSD and OPNSense are based on it.
    • We fixed a build error on FreeBSD #100
  • NetBSD, because I don't think that anyone uses it in production to be honest :/
  • OpenBSD: Since they are paranoid, they might want to use mitigations that are working, for once.

Misc

  • Windows: It would be nice to use appveyor to prove that Snuffleupagus is working on Windows. (@ylmrx)

SIGSEGV in src/sp_execute.c

Hi,
Snuffleupagus crashes when used with Joomla.
Here are some infos:

[----------------------------------registers-----------------------------------]
RAX: 0x2                                                      
RBX: 0x7ffd3e6da8f8 --> 0x7fa4d7e128a0 --> 0x7fa4cb2f68b8 --> 0x7fa4da3b5ad0 --> 0x55f0894c54415541
RCX: 0x7fa4da293b90 --> 0x41c6894d55415641             
RDX: 0x7ffd3e6da900 --> 0x2800000028 ('(') 
RSI: 0x7ffd3e6da990 --> 0x0                                                     
RDI: 0x7fa4d7ebcf68 ("/var/www/html/installation/language/en-GB/en-GB.ini")
RBP: 0x7ffd3e6da7e0 --> 0x7ffd3e6da900 --> 0x2800000028 ('(')
RSP: 0x7ffd3e6da7c0 --> 0x7ffd3e6da990 --> 0x0
RIP: 0x7fa4d79873f0 --> 0x83c0b60f1c40b60f                                        
R8 : 0x7fa4d7e12940 --> 0x7fa4d7ec1f88 --> 0x700000001          
R9 : 0x0                                                           
R10: 0x1              
R11: 0x0                                             
R12: 0x7ffd3e6da990 --> 0x0
R13: 0x7fa4d7e12960 --> 0x2
R14: 0x7fa4d7e128a0 --> 0x7fa4cb2f68b8 --> 0x7fa4da3b5ad0 --> 0x55f0894c54415541
R15: 0x7fa4cb2f68b8 --> 0x7fa4da3b5ad0 --> 0x55f0894c54415541
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7fa4d79873e7 <sp_stream_open+51>:  je     0x7fa4d7987426 <sp_stream_open+114>
   0x7fa4d79873e9 <sp_stream_open+53>:  mov    rax,QWORD PTR [rbp-0x8]
   0x7fa4d79873ed <sp_stream_open+57>:  mov    rax,QWORD PTR [rax]
=> 0x7fa4d79873f0 <sp_stream_open+60>:  movzx  eax,BYTE PTR [rax+0x1c]
   0x7fa4d79873f4 <sp_stream_open+64>:  movzx  eax,al
   0x7fa4d79873f7 <sp_stream_open+67>:  cmp    eax,0x49
   0x7fa4d79873fa <sp_stream_open+70>:  jne    0x7fa4d7987426 <sp_stream_open+114>
   0x7fa4d79873fc <sp_stream_open+72>:
    mov    rax,QWORD PTR [rip+0x20c015]        # 0x7fa4d7b93418
[------------------------------------stack-------------------------------------]
0000| 0x7ffd3e6da7c0 --> 0x7ffd3e6da990 --> 0x0
0008| 0x7ffd3e6da7c8 --> 0x7fa4d7ebcf68 ("/var/www/html/installation/language/en-GB/en-GB.ini")
0016| 0x7ffd3e6da7d0 --> 0x7ffd3e6da8f8 --> 0x7fa4d7e128a0 --> 0x7fa4cb2f68b8 --> 0x7fa4da3b5ad0 --> 0x55f0894c54415541
0024| 0x7ffd3e6da7d8 --> 0x7fa4d7e12960 --> 0x2
0032| 0x7ffd3e6da7e0 --> 0x7ffd3e6da900 --> 0x2800000028 ('(')
0040| 0x7ffd3e6da7e8 --> 0x7fa4da378c94 --> 0x153840ffff883
0048| 0x7ffd3e6da7f0 --> 0x7fa4d7ebcf50 --> 0x600000004
0056| 0x7ffd3e6da7f8 --> 0x77ed5bd4b65fed00
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007fa4d79873f0 in sp_stream_open (
    filename=0x7fa4d7ebcf68 "/var/www/html/installation/language/en-GB/en-GB.ini",
    handle=0x7ffd3e6da990) at /snuffleupagus/src/sp_execute.c:84
84      /snuffleupagus/src/sp_execute.c: No such file or directory.
#0  0x00007fa4d79873f0 in sp_stream_open (filename=0x7fa4d7ebcf68 "/var/www/html/installation/language/en-GB/en-GB.ini", handle=0x7ffd3e6da990) at /snuffleupagus/src/sp_execute.c:84
#1  0x00007fa4da378c94 in zend_stream_fixup () from target:/usr/lib/apache2/modules/libphp7.1.so
#2  0x00007fa4da32b9fa in zend_ini_open_file_for_scanning () from target:/usr/lib/apache2/modules/libphp7.1.so
#3  0x00007fa4da32b68c in zend_parse_ini_file () from target:/usr/lib/apache2/modules/libphp7.1.so
#4  0x00007fa4da294659 in zif_parse_ini_file () from target:/usr/lib/apache2/modules/libphp7.1.so
#5  0x00007fa4da3b5b16 in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#6  0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#7  0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e128a0) at /snuffleupagus/src/sp_execute.c:74
#8  0x00007fa4da3fe3a3 in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#9  0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#10 0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e127f0) at /snuffleupagus/src/sp_execute.c:74
#11 0x00007fa4da3fe3a3 in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#12 0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#13 0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e126d0) at /snuffleupagus/src/sp_execute.c:74
#14 0x00007fa4da3fe80a in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#15 0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#16 0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e125b0) at /snuffleupagus/src/sp_execute.c:74
#17 0x00007fa4da3fe80a in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#18 0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#19 0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e12490) at /snuffleupagus/src/sp_execute.c:74
#20 0x00007fa4da3fe80a in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#21 0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#22 0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e123f0) at /snuffleupagus/src/sp_execute.c:74
#23 0x00007fa4da3fe3a3 in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#24 0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#25 0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e12350) at /snuffleupagus/src/sp_execute.c:74
#26 0x00007fa4da3fe3a3 in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#27 0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#28 0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e122e0) at /snuffleupagus/src/sp_execute.c:74
#29 0x00007fa4da3fe3a3 in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#30 0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#31 0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e12220) at /snuffleupagus/src/sp_execute.c:74
#32 0x00007fa4da3fe80a in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#33 0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#34 0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e12170) at /snuffleupagus/src/sp_execute.c:74
#35 0x00007fa4da3fe80a in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#36 0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#37 0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e12120) at /snuffleupagus/src/sp_execute.c:74
#38 0x00007fa4da3fe80a in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#39 0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#40 0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e120b0) at /snuffleupagus/src/sp_execute.c:74
#41 0x00007fa4da3fe80a in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#42 0x00007fa4da3a453b in execute_ex () from target:/usr/lib/apache2/modules/libphp7.1.so
#43 0x00007fa4d79873b2 in sp_execute_ex (execute_data=0x7fa4d7e12030) at /snuffleupagus/src/sp_execute.c:74
#44 0x00007fa4da4009f0 in zend_execute () from target:/usr/lib/apache2/modules/libphp7.1.so
#45 0x00007fa4da35afd3 in zend_execute_scripts () from target:/usr/lib/apache2/modules/libphp7.1.so
#46 0x00007fa4da2f6f00 in php_execute_script () from target:/usr/lib/apache2/modules/libphp7.1.so
#47 0x00007fa4da40291a in ?? () from target:/usr/lib/apache2/modules/libphp7.1.so
#48 0x000055a61df998a0 in ap_run_handler ()
#49 0x000055a61df99de9 in ap_invoke_handler ()
#50 0x000055a61dfafcc2 in ap_process_async_request ()
#51 0x000055a61dfafe60 in ap_process_request ()
#52 0x000055a61dfac762 in ?? ()
#53 0x000055a61dfa3150 in ap_run_process_connection ()
#54 0x00007fa4da9677ba in ?? () from target:/usr/lib/apache2/modules/mod_mpm_prefork.so
#55 0x00007fa4da967a01 in ?? () from target:/usr/lib/apache2/modules/mod_mpm_prefork.so
#56 0x00007fa4da968667 in ?? () from target:/usr/lib/apache2/modules/mod_mpm_prefork.so
#57 0x000055a61df7d84e in ap_run_mpm ()
#58 0x000055a61df76673 in main ()
#59 0x00007fa4dd460b45 in __libc_start_main () from target:/lib/x86_64-linux-gnu/libc.so.6
#60 0x000055a61df767a6 in _start ()
gdb-peda$ p *data
$1 = {
  opline = 0x2,
  call = 0x2,
  return_value = 0x7fa4cb1c0448,
  func = 0x55a61f955d50,
  This = {
    value = {
      lval = 0x0,
      dval = 0,
      counted = 0x0,
      str = 0x0,
      arr = 0x0,
      obj = 0x0,
      res = 0x0,
      ref = 0x0,
      ast = 0x0,
      zv = 0x0,
      ptr = 0x0,
      ce = 0x0,
      func = 0x0,
      ww = {
        w1 = 0x0,
        w2 = 0x0
      }
    },
    u1 = {
      v = {
        type = 0x0,
        type_flags = 0x0,
        const_flags = 0x0,
        reserved = 0x0
      },
      type_info = 0x0
    },
    u2 = {
      next = 0x1,
      cache_slot = 0x1,
      lineno = 0x1,
      num_args = 0x1,
      fe_pos = 0x1,
      fe_iter_idx = 0x1,
      access_flags = 0x1,
      property_guard = 0x1,
      extra = 0x1
    }
  },
  prev_execute_data = 0x7fa4d7e128a0,
  symbol_table = 0x200000006,
  run_time_cache = 0x55a61f9d0af0,
  literals = 0x7fa400001406
}

Value in an array

Hi,
It would be great to be able to match a value in an array. Like using .var("foo[bar][]") to match any value in $foo[bar]'s array.

[DEB] Package should provide a config file

Installing the debian package should create a snuffleupagus.ini file in /etc/php/*/mods-available/
with this minimum content :

extension=snuffleupagus.so

Tested with package 0.1

Thank you !

SIGSEGV in arrays handling

Drop the follwing file in your src/tests folder:

--TEST--
Disable functions - match on an array value buried in several levels
--SKIPIF--
<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
--INI--
sp.configuration_file={PWD}/config/config_disabled_functions_param_array.ini
--FILE--
<?php
function foo($arr) {
	 echo $arr["a"]."\n";
}
$a=Array("test2"=>Array("pof"=>"pif", "foo"=>Array(Array("nope"=>"Nupe"), "lol"=>"bbb")), "a"=>"cccc");
foo($a);

$a=Array("test2"=>Array("foo"=>Array("lol"=>"aaa")), "a"=>"dddd");
foo($a);
?>
--EXPECTF--
cccc
[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'foo' in %a/tests/disabled_functions_param_array_several_levels.php:3 has been disabled, because its argument 'arr' content (Array) matched the rule '4'.
gdb-peda$ r
Starting program: /usr/bin/php7.0 -n -c /home/jvoisin/Dev/snuffleupagus/src/tmp-php.ini -d output_handler= -d open_basedir= -d safe_mode=0 -d disable_functions= -d output_buffering=Off -d error_reporting=32767 -d display_errors=1 -d display_startup_errors=1 -d log_errors=0 -d html_errors=0 -d track_errors=1 -d report_memleaks=1 -d report_zend_debug=0 -d docref_root= -d docref_ext=.html -d error_prepend_string= -d error_append_string= -d auto_prepend_file= -d auto_append_file= -d ignore_repeated_errors=0 -d precision=14 -d memory_limit=128M -d log_errors_max_len=0 -d opcache.fast_shutdown=0 -d opcache.file_update_protection=0 -d extension_dir=/home/jvoisin/Dev/snuffleupagus/src/modules/ -d extension=snuffleupagus.so -d session.auto_start=0 -d zlib.output_compression=Off -d sp.configuration_file=/home/jvoisin/Dev/snuffleupagus/src/tests/config/config_disabled_functions_param_array.ini -f /home/jvoisin/Dev/snuffleupagus/src/tests/disabled_functions_param_array_several_levels.php
warning: the debug information found in "/lib64/ld-2.24.so" does not match "/lib64/ld-linux-x86-64.so.2" (CRC mismatch).

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.

 [----------------------------------registers-----------------------------------]
RAX: 0x18 
RBX: 0x7ffff38140e0 --> 0x7ffff3877160 --> 0x55555581c900 (sub    rsp,0x8)
RCX: 0x0 
RDX: 0x0 
RSI: 0x7ffff0e72010 --> 0xe4006c6f6c 
RDI: 0x18 
RBP: 0x7fffffffa0a0 --> 0x7fffffffa120 --> 0x7fffffffa1a0 --> 0x7fffffffa2b0 --> 0x7fffffffa2d0 --> 0x7ffff3804000 (--> ...)
RSP: 0x7fffffffa028 --> 0x7ffff2d6328e (<sp_match_array_key_recurse+207>:	test   eax,eax)
RIP: 0x7ffff64930da (<__strcmp_sse2_unaligned+26>:	movdqu xmm1,XMMWORD PTR [rdi])
R8 : 0x7ffff385eba0 --> 0x0 
R9 : 0xce 
R10: 0xd2 
R11: 0x7ffff2d631bf (<sp_match_array_key_recurse>:	push   rbp)
R12: 0x7ffff38040e8 --> 0x800000000000002 
R13: 0x555555ba6740 --> 0x0 
R14: 0x7ffff3814030 --> 0x7ffff385f0c0 --> 0x55555583cda0 (push   r13)
R15: 0x7ffff385f0c0 --> 0x55555583cda0 (push   r13)
EFLAGS: 0x10283 (CARRY parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7ffff64930ca <__strcmp_sse2_unaligned+10>:	and    eax,0xfff
   0x7ffff64930cf <__strcmp_sse2_unaligned+15>:	cmp    eax,0xfc0
   0x7ffff64930d4 <__strcmp_sse2_unaligned+20>:	jg     0x7ffff6493352 <__strcmp_sse2_unaligned+658>
=> 0x7ffff64930da <__strcmp_sse2_unaligned+26>:	movdqu xmm1,XMMWORD PTR [rdi]
   0x7ffff64930de <__strcmp_sse2_unaligned+30>:	movdqu xmm0,XMMWORD PTR [rsi]
   0x7ffff64930e2 <__strcmp_sse2_unaligned+34>:	pcmpeqb xmm0,xmm1
   0x7ffff64930e6 <__strcmp_sse2_unaligned+38>:	pminub xmm0,xmm1
   0x7ffff64930ea <__strcmp_sse2_unaligned+42>:	pxor   xmm1,xmm1
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa028 --> 0x7ffff2d6328e (<sp_match_array_key_recurse+207>:	test   eax,eax)
0008| 0x7fffffffa030 --> 0x0 
0016| 0x7fffffffa038 --> 0x7ffff0e78010 --> 0x616161 ('aaa')
0024| 0x7fffffffa040 --> 0x7ffff0e71010 --> 0x0 
0032| 0x7fffffffa048 --> 0x7ffff385e900 --> 0x7ffff3857428 --> 0x700000001 
0040| 0x7fffffffa050 --> 0x7ffff385ea20 --> 0x7ffff38573f0 --> 0x700000001 
0048| 0x7fffffffa058 --> 0x7ffff0e71010 --> 0x0 
0056| 0x7fffffffa060 --> 0x7ffff385ea60 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
__strcmp_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcmp-sse2-unaligned.S:31
31	../sysdeps/x86_64/multiarch/strcmp-sse2-unaligned.S: No such file or directory.
gdb-peda$ bt
#0  __strcmp_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcmp-sse2-unaligned.S:31
#1  0x00007ffff2d6328e in sp_match_array_key_recurse (arr=0x7ffff385e900, keys=0x7ffff0e71010, to_match=0x7ffff0e78010 "aaa", rx=0x0) at /home/jvoisin/Dev/snuffleupagus/src/sp_utils.c:343
#2  0x00007ffff2d632ad in sp_match_array_key_recurse (arr=0x7ffff385f1a0, keys=0x7ffff0e73010, to_match=0x7ffff0e78010 "aaa", rx=0x0) at /home/jvoisin/Dev/snuffleupagus/src/sp_utils.c:344
#3  0x00007ffff2d632ad in sp_match_array_key_recurse (arr=0x7ffff3814140, keys=0x7ffff0e76010, to_match=0x7ffff0e78010 "aaa", rx=0x0) at /home/jvoisin/Dev/snuffleupagus/src/sp_utils.c:344
#4  0x00007ffff2d648f8 in should_disable (execute_data=0x7ffff38140e0) at /home/jvoisin/Dev/snuffleupagus/src/sp_disabled_functions.c:232
#5  0x00007ffff2d6536b in sp_execute_ex (execute_data=0x7ffff38140e0) at /home/jvoisin/Dev/snuffleupagus/src/sp_execute.c:59
#6  0x000055555583d13d in ?? ()
#7  0x00005555557f7ddb in execute_ex ()
#8  0x00007ffff2d653c7 in sp_execute_ex (execute_data=0x7ffff3814030) at /home/jvoisin/Dev/snuffleupagus/src/sp_execute.c:74
#9  0x000055555584c617 in zend_execute ()
#10 0x00005555557b7203 in zend_execute_scripts ()
#11 0x00005555557560b0 in php_execute_script ()
#12 0x000055555584e2d0 in ?? ()
#13 0x000055555563885e in main ()
#14 0x00007ffff64123f1 in __libc_start_main (main=0x5555556383f0 <main>, argc=0x42, argv=0x7fffffffdd38, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdd28) at ../csu/libc-start.c:291
#15 0x00005555556389aa in _start ()
gdb-peda$ p key
$1 = 0x0
gdb-peda$

It looks like there is a NULL-deref in the sp_match_array_key_recurse due to an array value without a key.

It would be nice to add a lot of different testcases for every different form of array in the commit fixing this issue ♥

Improve the dumping capabilities/usage

Currently, the .dump() filter is barely useful, this needs to be changed.

  • Add a way to limit the dumps, to prevent DoS by filling the file system
  • Figure a nice way to represent data in the dump
  • Refactor the code, since it's currently idiotic and possibly dangerous (memory allocation, copy into a buffer, flushing to disk, instead of directly flushing to disk)
  • Keep a trace of the rule that matched, maybe by storing a plain-text version of it internally?

Ensure invalid conf stops loading

Currently, even if the SP configuration is invalid, the php startup will continue (see disabled_functions_param_invalid_pos.phpt tests for example), but configuration parsing will be halted.

We need to make sure we fail to start to avoid invalid/broken configuration to go live.

Improve our linked-list implementation

Our currently implementation is suboptimal, and php's one is overkill, we do need to refactor what we have, like removing the head member. This shouldn't be hard.

Improve cookie encryption

This issue is based on the feedback from @nextgens, many thanks to him.

The cookie-jar encryption key is currently based on the IP of the user, its user-agent and a static secret key.

Assuming the user is using TLS, we should derive the static-key from the extended master secret (RFC7627), to make the encryption TLS-session dependant rather than IP-dependant, with something like key = hmac(global_secret, SSL_SESSION_ID);. Worst case, we can always fallback to what we're currently using anyway.

In Apache, it possible to access it with the SSL_SESSION_ID environment variable by adding SSLOptions StdEnvVars in the Apache2 configuration.

For nginx, the user has to set fastcgi_param SSL_SESSION_ID $ssl_session_id if_not_empty;.

Using a static-key isn't a bad idea, but it should be possible to have a random one picked at startup (no configuration needed) ... and the option to specify one for those in clustered environments, because having a value the user has to set and no safe default will lead to bad defaults being picked, no matter how much we document it.

It would make sense to store it in a file, instead of using an hex-encoded secret, to improve proper randomness (people will be less likely to use 123123123123213213 as a secret if they have to write data in a file with head -n 64 /dev/urandom > secret.key as example in the doc.).
The file could be copy/pasted between different servers in a clustered environment.

Add more rules examples

Those rules should be added in our config folder, and tested via the testsuite.

Vulnerabilities

  • Some vulns from the RIPS blogposts

Minimal set of rules that we want to publish

  • Typo3

global simulation() mode

It would be nice to get a global simulation() to set all rules in simulation mode, instead of appending simulation() to each rule (which is a bit time-consuming and boring).

It may be an evolution to the current .ini format to set several rules in simulation at once - something like:

## Rules in simulation() mode
sp.begin.simulation()
sp.disable_functions.function("chmod").param("mode").value_r("^[0-9]{2}[67]$").drop();
sp.disable_functions.function("chmod").param("mode").value_r("o\\+w$").drop();
sp.end.simulation()

sp.disable_functions.function_r("^(?:require|include)_once$").value_r("\\.(?:php|php7|inc|tpl)$").allow();
sp.disable_functions.function_r("^require|include$").value_r("\\.(?:php|php7|inc|tpl)$").allow();
sp.disable_functions.function_r("^(?:require|include)_once$").drop();
sp.disable_functions.function_r("^require|include$").drop();

The same principle could be extended to other actions (drop(), allow(), dump()).

(if it's not a good idea after all, feel free to close)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.