diff --git a/src/config.m4 b/src/config.m4 index a2301fd1..ddbd7a16 100644 --- a/src/config.m4 +++ b/src/config.m4 @@ -7,7 +7,7 @@ sources="$sources sp_disabled_functions.c sp_execute.c sp_upload_validation.c" sources="$sources sp_cookie_encryption.c sp_network_utils.c tweetnacl.c" sources="$sources sp_config_keywords.c sp_var_parser.c sp_var_value.c sp_tree.c" sources="$sources sp_pcre_compat.c sp_crypt.c sp_session.c sp_sloppy.c sp_wrapper.c" -sources="$sources sp_ini.c sp_php_compat.c sp_config_scanner.c" +sources="$sources sp_ini.c sp_php_compat.c sp_config_scanner.c sp_ifilter.c" PHP_ARG_ENABLE(snuffleupagus, whether to enable snuffleupagus support, [ --enable-snuffleupagus Enable snuffleupagus support]) diff --git a/src/php_snuffleupagus.h b/src/php_snuffleupagus.h index 0c7dc4b8..bcb613ce 100644 --- a/src/php_snuffleupagus.h +++ b/src/php_snuffleupagus.h @@ -88,6 +88,7 @@ typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS); #include "sp_sloppy.h" #include "sp_wrapper.h" #include "sp_ini.h" +#include "sp_ifilter.h" extern zend_module_entry snuffleupagus_module_entry; #define phpext_snuffleupagus_ptr &snuffleupagus_module_entry diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c index d2f81ffd..50711f01 100644 --- a/src/snuffleupagus.c +++ b/src/snuffleupagus.c @@ -383,6 +383,8 @@ static PHP_INI_MH(OnUpdateConfiguration) { sp_hook_ini(); } + sp_hook_register_server_variables(); + if (true == SNUFFLEUPAGUS_G(config).config_global_strict->enable) { if (!zend_get_extension(PHP_SNUFFLEUPAGUS_EXTNAME)) { zend_extension_entry.startup = NULL; diff --git a/src/sp_config.h b/src/sp_config.h index ccf23184..af9b905c 100644 --- a/src/sp_config.h +++ b/src/sp_config.h @@ -197,6 +197,8 @@ typedef struct { bool hook_execute; char log_media; u_long max_execution_depth; + bool server_encode; + bool server_strip; HashTable *config_disabled_functions; HashTable *config_disabled_functions_hooked; @@ -288,6 +290,8 @@ typedef struct { #define SP_TOKEN_ENV_VAR "cookie_env_var" #define SP_TOKEN_LOG_MEDIA "log_media" #define SP_TOKEN_MAX_EXECUTION_DEPTH "max_execution_depth" +#define SP_TOKEN_SERVER_ENCODE "server_encode" +#define SP_TOKEN_SERVER_STRIP "server_strip" // upload_validator #define SP_TOKEN_UPLOAD_SCRIPT "script" diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c index 632f9bd0..38b85269 100644 --- a/src/sp_config_keywords.c +++ b/src/sp_config_keywords.c @@ -129,6 +129,8 @@ SP_PARSE_FN(parse_global) { {parse_str, SP_TOKEN_ENV_VAR, &(SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var)}, {parse_log_media, SP_TOKEN_LOG_MEDIA, &(SNUFFLEUPAGUS_G(config).log_media)}, {parse_ulong, SP_TOKEN_MAX_EXECUTION_DEPTH, &(SNUFFLEUPAGUS_G(config).max_execution_depth)}, + {parse_enable, SP_TOKEN_SERVER_ENCODE, &(SNUFFLEUPAGUS_G(config).server_encode)}, + {parse_enable, SP_TOKEN_SERVER_STRIP, &(SNUFFLEUPAGUS_G(config).server_strip)}, {0, 0, 0}}; SP_PROCESS_CONFIG_KEYWORDS_ERR(); diff --git a/src/sp_ifilter.c b/src/sp_ifilter.c new file mode 100644 index 00000000..171138f4 --- /dev/null +++ b/src/sp_ifilter.c @@ -0,0 +1,103 @@ +#include "php_snuffleupagus.h" + +static void (*orig_register_server_variables)(zval *track_vars_array) = NULL; + +static const unsigned char sp_hexchars[] = "0123456789ABCDEF"; + +static const char sp_is_dangerous_char[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static void sp_server_strip(HashTable *svars, char *key, int keylen) { + zval *value = zend_hash_str_find(svars, key, keylen); + if (!value || Z_TYPE_P(value) != IS_STRING) { return; } + + zend_string *tmp_zstr = Z_STR_P(value); + char *tmp = ZSTR_VAL(tmp_zstr); + char *tmpend = tmp + ZSTR_LEN(tmp_zstr); + + for (char *p = tmp; p < tmpend; p++) { + if (sp_is_dangerous_char[(int)*p]) { + *p = '_'; + } + } +} + +static void sp_server_encode(HashTable *svars, char *key, int keylen) { + zval *value = zend_hash_str_find(svars, key, keylen); + if (!value || Z_TYPE_P(value) != IS_STRING) { return; } + + zend_string *tmp_zstr = Z_STR_P(value); + char *tmp = ZSTR_VAL(tmp_zstr); + char *tmpend = tmp + ZSTR_LEN(tmp_zstr); + int extra = 0; + + for (char *p = tmp; p < tmpend; p++) { + extra += sp_is_dangerous_char[(int)*p] * 2; + } + if (!extra) { return; } + + zend_string *new_zstr = zend_string_alloc(ZSTR_LEN(tmp_zstr) + extra, 0); + char *n = ZSTR_VAL(new_zstr); + for (char *p = tmp; p < tmpend; p++, n++) { + if (sp_is_dangerous_char[(int)*p]) { + *n++ = '%'; + *n++ = sp_hexchars[*p >> 4]; + *n = sp_hexchars[*p & 15]; + } else { + *n = *p; + } + } + ZSTR_VAL(new_zstr)[ZSTR_LEN(new_zstr)] = 0; + Z_STR_P(value) = new_zstr; + + zend_string_release_ex(tmp_zstr, 0); +} + +static void sp_register_server_variables(zval *track_vars_array) { + orig_register_server_variables(track_vars_array); + + HashTable *svars; + svars = Z_ARRVAL_P(track_vars_array); + + + if (SNUFFLEUPAGUS_G(config).server_encode) { + sp_server_encode(svars, ZEND_STRL("REQUEST_URI")); + sp_server_encode(svars, ZEND_STRL("QUERY_STRING")); + } + + if (SNUFFLEUPAGUS_G(config).server_strip) { + sp_server_strip(svars, ZEND_STRL("PHP_SELF")); + sp_server_strip(svars, ZEND_STRL("HTTP_HOST")); + sp_server_strip(svars, ZEND_STRL("HTTP_USER_AGENT")); + + // for cgi + fpm + sp_server_strip(svars, ZEND_STRL("PATH_INFO")); + sp_server_strip(svars, ZEND_STRL("PATH_TRANSLATED")); + sp_server_strip(svars, ZEND_STRL("ORIG_PATH_TRANSLATED")); + sp_server_strip(svars, ZEND_STRL("ORIG_PATH_INFO")); + } +} + +void sp_hook_register_server_variables() +{ + if (sapi_module.register_server_variables) { + orig_register_server_variables = sapi_module.register_server_variables; + sapi_module.register_server_variables = sp_register_server_variables; + } +} diff --git a/src/sp_ifilter.h b/src/sp_ifilter.h new file mode 100644 index 00000000..527c41d5 --- /dev/null +++ b/src/sp_ifilter.h @@ -0,0 +1,3 @@ +#pragma once + +void sp_hook_register_server_variables(); diff --git a/src/tests/filter/config/filter.ini b/src/tests/filter/config/filter.ini new file mode 100644 index 00000000..5ebee614 --- /dev/null +++ b/src/tests/filter/config/filter.ini @@ -0,0 +1,3 @@ +sp.global.server_encode.enable(); +sp.global.server_strip.enable(); + diff --git a/src/tests/filter/server_encode.phpt b/src/tests/filter/server_encode.phpt new file mode 100644 index 00000000..f7cc2332 --- /dev/null +++ b/src/tests/filter/server_encode.phpt @@ -0,0 +1,25 @@ +--TEST-- +input filter: server_encode +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/filter.ini +display_errors=1 +display_startup_errors=1 +error_reporting=E_ALL +--ENV-- +return <<"'`!AAA +EOF; +--COOKIE-- +--GET-- +BBB<>"'`!BBB +--POST-- +--FILE-- + +--INI-- +sp.configuration_file={PWD}/config/filter.ini +display_errors=1 +display_startup_errors=1 +error_reporting=E_ALL +--ENV-- +return <<alert('123');Gecko/20100101 Firefox/29.0 +EOF; +--COOKIE-- +--GET-- +--POST-- +--FILE-- +