From e3e8bac1387f0a19ac7db9d28acab3a15637b7ba Mon Sep 17 00:00:00 2001 From: Felipe Zimmerle Date: Thu, 22 Oct 2015 17:20:31 -0300 Subject: [PATCH] Adds support to URL decode transformation --- src/actions/transformations/transformation.cc | 2 +- src/actions/transformations/url_decode.cc | 87 ++++++++++++++++--- src/actions/transformations/url_decode.h | 3 + src/utils.cc | 9 +- src/utils.h | 11 ++- test/test-cases/secrules-language-tests | 2 +- test/unit/unit_test.cc | 24 ++++- 7 files changed, 113 insertions(+), 25 deletions(-) diff --git a/src/actions/transformations/transformation.cc b/src/actions/transformations/transformation.cc index 043ccf22..1efd7061 100644 --- a/src/actions/transformations/transformation.cc +++ b/src/actions/transformations/transformation.cc @@ -106,7 +106,7 @@ Transformation* Transformation::instantiate(std::string a) { IF_MATCH(trimLeft) { return new TrimLeft(a); } IF_MATCH(trimRight) { return new TrimRight(a); } IF_MATCH(trim) { return new Trim(a); } - IF_MATCH(url_decode) { return new UrlDecode(a); } + IF_MATCH(urlDecode) { return new UrlDecode(a); } IF_MATCH(urlDecodeUni) { return new UrlDecodeUni(a); } IF_MATCH(url_encode) { return new UrlEncode(a); } IF_MATCH(utf8_to_unicode) { return new Utf8Unicode(a); } diff --git a/src/actions/transformations/url_decode.cc b/src/actions/transformations/url_decode.cc index 661e3a07..0dc9a312 100644 --- a/src/actions/transformations/url_decode.cc +++ b/src/actions/transformations/url_decode.cc @@ -24,12 +24,71 @@ #include "modsecurity/assay.h" #include "actions/transformations/transformation.h" - +#include "src/utils.h" namespace ModSecurity { namespace actions { namespace transformations { + +int UrlDecode::urldecode_nonstrict_inplace(unsigned char *input, + long int input_len, int *invalid_count, int *changed) { + unsigned char *d = (unsigned char *)input; + long int i, count; + + *changed = 0; + + if (input == NULL) { + return -1; + } + + i = count = 0; + while (i < input_len) { + if (input[i] == '%') { + /* Character is a percent sign. */ + + /* Are there enough bytes available? */ + if (i + 2 < input_len) { + char c1 = input[i + 1]; + char c2 = input[i + 2]; + if (VALID_HEX(c1) && VALID_HEX(c2)) { + unsigned long uni = x2c(&input[i + 1]); + + *d++ = (wchar_t)uni; + count++; + i += 3; + *changed = 1; + } else { + /* Not a valid encoding, skip this % */ + *d++ = input[i++]; + count ++; + (*invalid_count)++; + } + } else { + /* Not enough bytes available, copy the raw bytes. */ + *d++ = input[i++]; + count ++; + (*invalid_count)++; + } + } else { + /* Character is not a percent sign. */ + if (input[i] == '+') { + *d++ = ' '; + *changed = 1; + } else { + *d++ = input[i]; + } + count++; + i++; + } + } + + *d = '\0'; + + return count; +} + + UrlDecode::UrlDecode(std::string action) : Transformation(action) { this->action_kind = 1; @@ -37,17 +96,25 @@ UrlDecode::UrlDecode(std::string action) std::string UrlDecode::evaluate(std::string value, Assay *assay) { - /** - * @todo Implement the transformation UrlDecode - */ - if (assay) { -#ifndef NO_LOGS - assay->debug(4, "Transformation UrlDecode is not implemented yet."); -#endif - } - return value; + unsigned char *val = NULL; + int invalid_count; + int changed; + + val = (unsigned char *) malloc(sizeof(char) * value.size() + 1); + memcpy(val, value.c_str(), value.size() + 1); + val[value.size()] = '\0'; + + int size = urldecode_nonstrict_inplace(val, value.size(), &invalid_count, &changed); + std::string out; + + out.append((const char *)val, size); + + free(val); + + return out; } + } // namespace transformations } // namespace actions } // namespace ModSecurity diff --git a/src/actions/transformations/url_decode.h b/src/actions/transformations/url_decode.h index a3861c29..fdde03c9 100644 --- a/src/actions/transformations/url_decode.h +++ b/src/actions/transformations/url_decode.h @@ -33,6 +33,9 @@ class UrlDecode : public Transformation { explicit UrlDecode(std::string action); std::string evaluate(std::string exp, Assay *assay) override; + + int urldecode_nonstrict_inplace(unsigned char *input, long int input_len, + int *invalid_count, int *changed); }; } // namespace transformations diff --git a/src/utils.cc b/src/utils.cc index d8ee03cc..ae2a53d8 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -43,11 +43,6 @@ #include "modsecurity/modsecurity.h" -#define VALID_HEX(X) (((X >= '0') && (X <= '9')) || \ - ((X >= 'a') && (X <= 'f')) || ((X >= 'A') && (X <= 'F'))) -#define ISODIGIT(X) ((X >= '0') && (X <= '7')) -#define NBSP 160 - namespace ModSecurity { std::string phase_name(int x) { @@ -827,7 +822,7 @@ length: /** * Converts a single hexadecimal digit into a decimal value. */ -static unsigned char xsingle2c(unsigned char *what) { +unsigned char xsingle2c(unsigned char *what) { register unsigned char digit; digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); @@ -836,7 +831,7 @@ static unsigned char xsingle2c(unsigned char *what) { } -static unsigned char x2c(unsigned char *what) { +unsigned char x2c(unsigned char *what) { register unsigned char digit; digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); diff --git a/src/utils.h b/src/utils.h index 979ebe9d..c5b21a14 100644 --- a/src/utils.h +++ b/src/utils.h @@ -22,6 +22,13 @@ #ifndef SRC_UTILS_H_ + +#define VALID_HEX(X) (((X >= '0') && (X <= '9')) || \ + ((X >= 'a') && (X <= 'f')) || ((X >= 'A') && (X <= 'F'))) +#define ISODIGIT(X) ((X >= '0') && (X <= '7')) +#define NBSP 160 + + namespace ModSecurity { std::vector split(std::string str, char delimiter); double random_number(const double from, const double to); @@ -35,9 +42,9 @@ namespace ModSecurity { std::string toupper(std::string str); double cpu_seconds(void); int js_decode_nonstrict_inplace(unsigned char *input, int64_t input_len); - static unsigned char x2c(unsigned char *what); + unsigned char x2c(unsigned char *what); int css_decode_inplace(unsigned char *input, int64_t input_len); - static unsigned char xsingle2c(unsigned char *what); + unsigned char xsingle2c(unsigned char *what); int html_entities_decode_inplace(unsigned char *input, int input_len); int normalize_path_inplace(unsigned char *input, int input_len, int win, int *changed); diff --git a/test/test-cases/secrules-language-tests b/test/test-cases/secrules-language-tests index 1f6f5235..76c4bd64 160000 --- a/test/test-cases/secrules-language-tests +++ b/test/test-cases/secrules-language-tests @@ -1 +1 @@ -Subproject commit 1f6f5235485e4a68f9cd57d2ac1ad4a0d4a7a3c4 +Subproject commit 76c4bd6460a870b481eb4d36fcead7818b20d7d9 diff --git a/test/unit/unit_test.cc b/test/unit/unit_test.cc index dcdc4e4e..48545546 100644 --- a/test/unit/unit_test.cc +++ b/test/unit/unit_test.cc @@ -93,6 +93,11 @@ UnitTest *UnitTest::from_yajl_node(yajl_val &node) { u->param = YAJL_GET_STRING(val); } else if (strcmp(key, "input") == 0) { u->input = YAJL_GET_STRING(val); + /* + * Converting \\u0000 to \0 due to the following gcc bug: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53690 + * + */ replaceAll(&(u->input), "\\0", '\0'); replaceAll(&(u->input), "\\xe4", '\xe4'); replaceAll(&(u->input), "\\x03", '\x03'); @@ -100,7 +105,7 @@ UnitTest *UnitTest::from_yajl_node(yajl_val &node) { replaceAll(&(u->input), "\\xc9", '\xc9'); replaceAll(&(u->input), "\\x3b", '\x3b'); replaceAll(&(u->input), "\\xFF", '\xff'); - replaceAll(&(u->input), "\\u0000", '\u0000'); + replaceAll(&(u->input), "\\u0000", '\0'); } else if (strcmp(key, "name") == 0) { u->name = YAJL_GET_STRING(val); } else if (strcmp(key, "type") == 0) { @@ -108,9 +113,20 @@ UnitTest *UnitTest::from_yajl_node(yajl_val &node) { } else if (strcmp(key, "ret") == 0) { u->ret = YAJL_GET_INTEGER(val); } else if (strcmp(key, "output") == 0) { - u->output = YAJL_GET_STRING(val); - replaceAll(&(u->output), "\\u0000", '\u0000'); - replaceAll(&(u->output), "\\0", '\u0000'); + u->output = std::string(YAJL_GET_STRING(val)); + /* + * Converting \\u0000 to \0 due to the following gcc bug: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53690 + * + */ + replaceAll(&(u->output), "\\u0000", '\0'); + replaceAll(&(u->output), "\\xe4", '\xe4'); + replaceAll(&(u->output), "\\x03", '\x03'); + replaceAll(&(u->output), "\\xbf", '\xbf'); + replaceAll(&(u->output), "\\xc9", '\xc9'); + replaceAll(&(u->output), "\\x3b", '\x3b'); + replaceAll(&(u->output), "\\xFF", '\xff'); + replaceAll(&(u->output), "\\0", '\0'); } }