summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorkkadosh2018-03-02 13:31:36 +0000
committerjvoisin2018-03-02 14:31:36 +0100
commitbdd2cfc430d4b841c24a6c08e7934d667bdc6637 (patch)
tree271d91df0af647cec436252097e51d10696f6bc8 /src
parente822be3a2e9919663cac74463b98e208db742bcf (diff)
Implement dump() for execution of writable PHP files
Diffstat (limited to 'src')
-rw-r--r--src/sp_config.h2
-rw-r--r--src/sp_config_keywords.c33
-rw-r--r--src/sp_execute.c7
-rw-r--r--src/sp_unserialize.c5
-rw-r--r--src/sp_utils.c6
-rw-r--r--src/tests/config/dump_deny_writable_execution.ini1
-rw-r--r--src/tests/dump_deny_writable_execution.phpt67
7 files changed, 113 insertions, 8 deletions
diff --git a/src/sp_config.h b/src/sp_config.h
index ba1542c..0ccd11e 100644
--- a/src/sp_config.h
+++ b/src/sp_config.h
@@ -45,6 +45,8 @@ typedef struct {
45typedef struct { 45typedef struct {
46 bool enable; 46 bool enable;
47 bool simulation; 47 bool simulation;
48 char *dump;
49 char *textual_representation;
48} sp_config_readonly_exec; 50} sp_config_readonly_exec;
49 51
50typedef struct { bool enable; } sp_config_global_strict; 52typedef struct { bool enable; } sp_config_global_strict;
diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c
index c58a227..a04c88f 100644
--- a/src/sp_config_keywords.c
+++ b/src/sp_config_keywords.c
@@ -82,7 +82,8 @@ int parse_global_strict(char *line) {
82 82
83int parse_unserialize(char *line) { 83int parse_unserialize(char *line) {
84 bool enable = false, disable = false; 84 bool enable = false, disable = false;
85 sp_config_unserialize *unserialize = SNUFFLEUPAGUS_G(config).config_unserialize; 85 sp_config_unserialize *unserialize =
86 SNUFFLEUPAGUS_G(config).config_unserialize;
86 87
87 sp_config_functions sp_config_funcs[] = { 88 sp_config_functions sp_config_funcs[] = {
88 {parse_empty, SP_TOKEN_ENABLE, &(enable)}, 89 {parse_empty, SP_TOKEN_ENABLE, &(enable)},
@@ -110,9 +111,33 @@ int parse_unserialize(char *line) {
110} 111}
111 112
112int parse_readonly_exec(char *line) { 113int parse_readonly_exec(char *line) {
113 return parse_enable( 114 bool enable = false, disable = false;
114 line, &(SNUFFLEUPAGUS_G(config).config_readonly_exec->enable), 115 sp_config_readonly_exec *readonly_exec =
115 &(SNUFFLEUPAGUS_G(config).config_readonly_exec->simulation)); 116 SNUFFLEUPAGUS_G(config).config_readonly_exec;
117
118 sp_config_functions sp_config_funcs[] = {
119 {parse_empty, SP_TOKEN_ENABLE, &(enable)},
120 {parse_empty, SP_TOKEN_DISABLE, &(disable)},
121 {parse_empty, SP_TOKEN_SIMULATION, &(readonly_exec->simulation)},
122 {parse_str, SP_TOKEN_DUMP, &(readonly_exec->dump)},
123 {0}};
124
125 readonly_exec->textual_representation = estrdup(line);
126 int ret = parse_keywords(sp_config_funcs, line);
127
128 if (0 != ret) {
129 return ret;
130 }
131
132 if (!(enable ^ disable)) {
133 sp_log_err("config", "A rule can't be enabled and disabled on line %zu.",
134 sp_line_no);
135 return -1;
136 }
137
138 SNUFFLEUPAGUS_G(config).config_readonly_exec->enable = enable;
139
140 return ret;
116} 141}
117 142
118int parse_global(char *line) { 143int parse_global(char *line) {
diff --git a/src/sp_execute.c b/src/sp_execute.c
index 20fe509..5cf139a 100644
--- a/src/sp_execute.c
+++ b/src/sp_execute.c
@@ -1,5 +1,4 @@
1#include "php_snuffleupagus.h" 1#include "php_snuffleupagus.h"
2
3#include <errno.h> 2#include <errno.h>
4#include <string.h> 3#include <string.h>
5 4
@@ -14,6 +13,12 @@ static int (*orig_zend_stream_open)(const char *filename,
14// FIXME handle symlink 13// FIXME handle symlink
15ZEND_COLD static inline void terminate_if_writable(const char *filename) { 14ZEND_COLD static inline void terminate_if_writable(const char *filename) {
16 if (0 == access(filename, W_OK)) { 15 if (0 == access(filename, W_OK)) {
16 if (SNUFFLEUPAGUS_G(config).config_readonly_exec->dump) {
17 sp_log_request(
18 SNUFFLEUPAGUS_G(config).config_readonly_exec->dump,
19 SNUFFLEUPAGUS_G(config).config_readonly_exec->textual_representation,
20 SP_TOKEN_READONLY_EXEC);
21 }
17 if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->simulation) { 22 if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->simulation) {
18 sp_log_msg("readonly_exec", SP_LOG_SIMULATION, 23 sp_log_msg("readonly_exec", SP_LOG_SIMULATION,
19 "Attempted execution of a writable file (%s).", filename); 24 "Attempted execution of a writable file (%s).", filename);
diff --git a/src/sp_unserialize.c b/src/sp_unserialize.c
index b2875e2..60ef7be 100644
--- a/src/sp_unserialize.c
+++ b/src/sp_unserialize.c
@@ -96,7 +96,10 @@ PHP_FUNCTION(sp_unserialize) {
96 } 96 }
97 } 97 }
98 if (SNUFFLEUPAGUS_G(config).config_unserialize->dump) { 98 if (SNUFFLEUPAGUS_G(config).config_unserialize->dump) {
99 sp_log_request(SNUFFLEUPAGUS_G(config).config_unserialize->dump, SNUFFLEUPAGUS_G(config).config_unserialize->textual_representation, SP_TOKEN_UNSERIALIZE_HMAC); 99 sp_log_request(
100 SNUFFLEUPAGUS_G(config).config_unserialize->dump,
101 SNUFFLEUPAGUS_G(config).config_unserialize->textual_representation,
102 SP_TOKEN_UNSERIALIZE_HMAC);
100 } 103 }
101 efree(serialized_str); 104 efree(serialized_str);
102 return; 105 return;
diff --git a/src/sp_utils.c b/src/sp_utils.c
index 1b707d0..0f58fbb 100644
--- a/src/sp_utils.c
+++ b/src/sp_utils.c
@@ -225,7 +225,8 @@ void sp_log_disable(const char* restrict path, const char* restrict arg_name,
225 } 225 }
226 } 226 }
227 if (dump) { 227 if (dump) {
228 sp_log_request(config_node->dump, config_node->textual_representation, SP_TOKEN_DISABLE_FUNC); 228 sp_log_request(config_node->dump, config_node->textual_representation,
229 SP_TOKEN_DISABLE_FUNC);
229 } 230 }
230} 231}
231 232
@@ -252,7 +253,8 @@ void sp_log_disable_ret(const char* restrict path,
252 zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?", path); 253 zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?", path);
253 } 254 }
254 if (dump) { 255 if (dump) {
255 sp_log_request(dump, config_node->textual_representation, SP_TOKEN_DISABLE_FUNC); 256 sp_log_request(dump, config_node->textual_representation,
257 SP_TOKEN_DISABLE_FUNC);
256 } 258 }
257} 259}
258 260
diff --git a/src/tests/config/dump_deny_writable_execution.ini b/src/tests/config/dump_deny_writable_execution.ini
new file mode 100644
index 0000000..c49f893
--- /dev/null
+++ b/src/tests/config/dump_deny_writable_execution.ini
@@ -0,0 +1 @@
sp.readonly_exec.enable().simulation().dump("/tmp/dump_result/");
diff --git a/src/tests/dump_deny_writable_execution.phpt b/src/tests/dump_deny_writable_execution.phpt
new file mode 100644
index 0000000..c6dd6cd
--- /dev/null
+++ b/src/tests/dump_deny_writable_execution.phpt
@@ -0,0 +1,67 @@
1--TEST--
2Readonly execution attempt (simulation mode)
3--SKIPIF--
4<?php
5if (!extension_loaded("snuffleupagus")) print "skip";
6
7// root has write privileges on any file
8if (TRUE == function_exists("posix_getuid")) {
9 if (0 == posix_getuid()) {
10 print "skip";
11 }
12} elseif (TRUE == function_exists("shell_exec")) {
13 if ("root" == trim(shell_exec("whoami"))) {
14 print "skip";
15 }
16}
17?>
18--POST--
19post_a=data_post_a_readonly&post_b=data_post_b_readonly
20--GET--
21get_a=data_get_a_readonly&get_b=data_get_b_readonly
22--COOKIE--
23cookie_a=data_cookie_a_readonly&cookie_b=data_cookie_b_readonly
24--INI--
25sp.configuration_file={PWD}/config/dump_deny_writable_execution.ini
26--FILE--
27<?php
28@mkdir("/tmp/dump_result/");
29foreach (glob("/tmp/dump_result/sp_dump.*") as $dump) {
30 @unlink($dump);
31}
32$dir = __DIR__;
33
34// just in case
35@unlink("$dir/non_writable_file.txt");
36@unlink("$dir/writable_file.txt");
37
38file_put_contents("$dir/writable_file.txt", '<?php echo "Code execution within a writable file.\n";');
39file_put_contents("$dir/non_writable_file.txt", '<?php echo "Code execution within a non-writable file.\n";');
40chmod("$dir/writable_file.txt", 0777);
41chmod("$dir/non_writable_file.txt", 0400);
42include "$dir/writable_file.txt";
43include "$dir/non_writable_file.txt";
44
45$filename = glob('/tmp/dump_result/sp_dump.*')[0];
46$res = file($filename);
47if ($res[2] != "GET:get_a='data_get_a_readonly' get_b='data_get_b_readonly' \n") {
48 echo "1\n";
49} elseif ($res[3] != "POST:post_a='data_post_a_readonly' post_b='data_post_b_readonly' \n") {
50 echo "2\n";
51} elseif ($res[4] != "COOKIE:cookie_a='data_cookie_a_readonly&cookie_b=data_cookie_b_readonly' \n") {
52 echo "3\n";
53} else {
54 echo "WIN\n";
55}
56?>
57--EXPECTF--
58%a
59WIN
60--CLEAN--
61<?php
62$dir = __DIR__;
63chmod("$dir/non_writable_file.txt", 0777);
64chmod("$dir/writable_file.txt", 0777);
65unlink("$dir/non_writable_file.txt");
66unlink("$dir/writable_file.txt");
67?>