summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjvoisin2017-10-09 11:54:31 +0200
committerGitHub2017-10-09 11:54:31 +0200
commit1602780f3c85ca127c77d28ae93a4e2a4eeae180 (patch)
tree1200952d6f20b0dbc94d6252dcdaba5be0bad353
parent7234fdbb0cb0dd45ed1d6e7814c91e596126ee25 (diff)
Better hooking of language constructs (#26)
* Vastly improve the support of language construct hooking
-rw-r--r--src/snuffleupagus.c4
-rw-r--r--src/sp_config.h7
-rw-r--r--src/sp_config_keywords.c67
-rw-r--r--src/sp_execute.c40
-rw-r--r--src/tests/disabled_functions_require.phpt10
5 files changed, 88 insertions, 40 deletions
diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c
index 52b975e..98649d6 100644
--- a/src/snuffleupagus.c
+++ b/src/snuffleupagus.c
@@ -81,9 +81,9 @@ PHP_GINIT_FUNCTION(snuffleupagus) {
81 SP_INIT(snuffleupagus_globals->config.config_disabled_functions); 81 SP_INIT(snuffleupagus_globals->config.config_disabled_functions);
82 SP_INIT(snuffleupagus_globals->config.config_disabled_functions_ret); 82 SP_INIT(snuffleupagus_globals->config.config_disabled_functions_ret);
83 SP_INIT(snuffleupagus_globals->config.config_cookie_encryption); 83 SP_INIT(snuffleupagus_globals->config.config_cookie_encryption);
84 SP_INIT(snuffleupagus_globals->config.config_regexp_inclusion); 84 SP_INIT(snuffleupagus_globals->config.config_disabled_constructs);
85 85
86 snuffleupagus_globals->config.config_regexp_inclusion->regexp_inclusion = sp_new_list(); 86 snuffleupagus_globals->config.config_disabled_constructs->construct_include = sp_new_list();
87 snuffleupagus_globals->config.config_disabled_functions->disabled_functions = sp_new_list(); 87 snuffleupagus_globals->config.config_disabled_functions->disabled_functions = sp_new_list();
88 snuffleupagus_globals->config.config_disabled_functions_ret->disabled_functions = sp_new_list(); 88 snuffleupagus_globals->config.config_disabled_functions_ret->disabled_functions = sp_new_list();
89 89
diff --git a/src/sp_config.h b/src/sp_config.h
index d5cd1dc..e51d6cc 100644
--- a/src/sp_config.h
+++ b/src/sp_config.h
@@ -105,8 +105,9 @@ typedef struct {
105} sp_config_disabled_functions; 105} sp_config_disabled_functions;
106 106
107typedef struct { 107typedef struct {
108 sp_node_t *regexp_inclusion; // list of regexp for inclusion 108 sp_node_t *construct_include; // list of rules for `(include|require)_(once)?`
109} sp_config_regexp_inclusion; 109 sp_node_t *construct_echo;
110} sp_config_disabled_constructs;
110 111
111typedef struct { 112typedef struct {
112 char *script; 113 char *script;
@@ -126,7 +127,7 @@ typedef struct {
126 sp_config_auto_cookie_secure *config_auto_cookie_secure; 127 sp_config_auto_cookie_secure *config_auto_cookie_secure;
127 sp_config_global_strict *config_global_strict; 128 sp_config_global_strict *config_global_strict;
128 sp_config_disable_xxe *config_disable_xxe; 129 sp_config_disable_xxe *config_disable_xxe;
129 sp_config_regexp_inclusion *config_regexp_inclusion; 130 sp_config_disabled_constructs *config_disabled_constructs;
130} sp_config; 131} sp_config;
131 132
132typedef struct { 133typedef struct {
diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c
index d6107c3..8fba868 100644
--- a/src/sp_config_keywords.c
+++ b/src/sp_config_keywords.c
@@ -2,6 +2,51 @@
2 2
3ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) 3ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
4 4
5
6static int get_construct_type(sp_disabled_function const *const df) {
7 const struct {
8 unsigned int type;
9 char *keys[5];
10 } mapping[] = {
11 {
12 .type = ZEND_INCLUDE_OR_EVAL,
13 .keys = {"include", "include_once", "require", "require_once", NULL}
14 },{
15 .type = ZEND_ECHO,
16 .keys = {"echo", NULL}
17 },{
18 .type = ZEND_NEW,
19 .keys = {"new", NULL}
20 },{
21 .type = ZEND_EXIT,
22 .keys = {"exit", NULL}
23 },{
24 .type = ZEND_STRLEN,
25 .keys = {"strlen", NULL}
26 },{
27 .type = 0,
28 .keys = {NULL}
29 }
30 };
31
32 // FIXME: This can be optimized
33 // FIXME the ->function and r_fonction tests are _wrong_
34 for (size_t i=0; 0 != mapping[i].type; i++) {
35 for (size_t j=0; NULL != mapping[i].keys[j]; j++) {
36 if (df->function) {
37 if (0 == strcmp(df->function, mapping[i].keys[j])) {
38 return mapping[i].type;
39 }
40 } else if (df->r_function) {
41 if (true == is_regexp_matching(df->r_function, mapping[i].keys[j])) {
42 return mapping[i].type;
43 }
44 }
45 }
46 }
47 return -1;
48}
49
5static int parse_enable(char *line, bool * restrict retval, bool * restrict simulation) { 50static int parse_enable(char *line, bool * restrict retval, bool * restrict simulation) {
6 bool enable = false, disable = false; 51 bool enable = false, disable = false;
7 sp_config_functions sp_config_funcs[] = { 52 sp_config_functions sp_config_funcs[] = {
@@ -207,22 +252,16 @@ int parse_disabled_functions(char *line) {
207 df->var_is_array = 1; 252 df->var_is_array = 1;
208 } 253 }
209 254
210 bool match = false; 255 switch (get_construct_type(df)) {
211 const char *key[4] = {"include", "include_once", "require", "require_once"}; 256 case ZEND_INCLUDE_OR_EVAL:
212 for (size_t i = 0; i < 4; i++) { 257 sp_list_insert(SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_include, df);
213 if (df->r_function && true == is_regexp_matching(df->r_function, key[i])) { 258 return ret;
214 match = true; 259 case ZEND_ECHO:
215 break; 260 default:
216 } else if (df->function && 0 == strcmp(df->function, key[i])) {
217 match = true;
218 break; 261 break;
219 }
220 } 262 }
221 if (true == match && df->regexp) { 263
222 sp_list_insert( 264 if (df->ret || df->r_ret || df->ret_type) {
223 SNUFFLEUPAGUS_G(config).config_regexp_inclusion->regexp_inclusion,
224 df->regexp);
225 } else if (df->ret || df->r_ret || df->ret_type) {
226 sp_list_insert( 265 sp_list_insert(
227 SNUFFLEUPAGUS_G(config).config_disabled_functions_ret->disabled_functions, 266 SNUFFLEUPAGUS_G(config).config_disabled_functions_ret->disabled_functions,
228 df); 267 df);
diff --git a/src/sp_execute.c b/src/sp_execute.c
index 7d62e88..014a049 100644
--- a/src/sp_execute.c
+++ b/src/sp_execute.c
@@ -28,17 +28,20 @@ ZEND_COLD static inline void terminate_if_writable(const char *filename) {
28 } 28 }
29} 29}
30 30
31static void check_inclusion_regexp(const char * const filename) { 31static void construct_include_handler(const char * const filename) {
32 if (SNUFFLEUPAGUS_G(config).config_regexp_inclusion->regexp_inclusion) { 32 if (SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_include) {
33 const sp_node_t* config = SNUFFLEUPAGUS_G(config).config_regexp_inclusion->regexp_inclusion; 33 const sp_node_t* config = SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_include;
34 if (!config || !config->data) { 34 if (!config || !config->data) {
35 return; 35 return;
36 } 36 }
37
37 while (config) { 38 while (config) {
38 pcre *config_node = (pcre*)(config->data); 39 sp_disabled_function *config_node = (sp_disabled_function*)(config->data);
39 if (false == is_regexp_matching(config_node, filename)) { 40 if (true == is_regexp_matching(config_node->regexp, filename)) {
40 sp_log_msg("include", SP_LOG_DROP, "Inclusion of a forbidden file (%s).", filename); 41 sp_log_disable("include", "inclusion path", filename, config_node);
41 sp_terminate(); 42 if (false == config_node->simulation) {
43 sp_terminate();
44 }
42 } 45 }
43 config = config->next; 46 config = config->next;
44 } 47 }
@@ -68,17 +71,22 @@ execute:
68 orig_execute_ex(execute_data); 71 orig_execute_ex(execute_data);
69} 72}
70 73
71static int sp_stream_open(const char *filename, 74static int sp_stream_open(const char *filename, zend_file_handle *handle) {
72 zend_file_handle *handle) { 75 zend_execute_data const * const data = EG(current_execute_data);
73 const zend_execute_data *data = EG(current_execute_data);
74 76
75 if ((NULL != data) && (NULL != data->opline) && 77 if ((NULL == data) || (NULL == data->opline)) {
76 (ZEND_INCLUDE_OR_EVAL == data->opline->opcode)) { 78 goto end;
77 if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) {
78 terminate_if_writable(filename);
79 }
80 check_inclusion_regexp(filename);
81 } 79 }
80
81 switch(data->opline->opcode) {
82 case ZEND_INCLUDE_OR_EVAL:
83 if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) {
84 terminate_if_writable(filename);
85 }
86 construct_include_handler(filename);
87 }
88
89end:
82 return orig_zend_stream_open(filename, handle); 90 return orig_zend_stream_open(filename, handle);
83} 91}
84 92
diff --git a/src/tests/disabled_functions_require.phpt b/src/tests/disabled_functions_require.phpt
index cc10ef6..f848f8b 100644
--- a/src/tests/disabled_functions_require.phpt
+++ b/src/tests/disabled_functions_require.phpt
@@ -7,17 +7,17 @@ sp.configuration_file={PWD}/config/config_disabled_functions_require.ini
7--FILE-- 7--FILE--
8<?php 8<?php
9$dir = __DIR__; 9$dir = __DIR__;
10file_put_contents($dir . '/test.meh', ""); 10file_put_contents($dir . '/test.bla', "BLA");
11file_put_contents($dir . '/test.bla', ""); 11file_put_contents($dir . '/test.meh', "MEH");
12require $dir . '/test.meh';
13require $dir . '/test.bla'; 12require $dir . '/test.bla';
13require $dir . '/test.meh';
14echo "1337"; 14echo "1337";
15?> 15?>
16--EXPECTF-- 16--EXPECTF--
17[snuffleupagus][0.0.0.0][include][drop] Inclusion of a forbidden file (%a/test.bla). 17BLA[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'include' in %a/disabled_functions_require.php:%d has been disabled, because its argument 'inclusion path' content (%a/test.meh) matched a rule.
18--CLEAN-- 18--CLEAN--
19<?php 19<?php
20$dir = __DIR__; 20$dir = __DIR__;
21unlink($dir . '/test.meh');
22unlink($dir . '/test.bla'); 21unlink($dir . '/test.bla');
22unlink($dir . '/test.meh');
23?> 23?>