From 522f195aa06f083179388cf4d04e9abb2b5a6b7f Mon Sep 17 00:00:00 2001 From: Felipe Zimmerle Date: Wed, 5 Aug 2015 22:54:48 -0300 Subject: [PATCH] Adds support to urlDecodeUni transformation --- headers/modsecurity/assay.h | 3 +- headers/modsecurity/rules.h | 20 ++- src/actions/transformations/transformation.cc | 2 +- src/actions/transformations/url_decode_uni.cc | 22 +-- src/actions/transformations/url_decode_uni.h | 4 +- src/utils.cc | 132 ++++++++++++++++++ src/utils.h | 2 + 7 files changed, 171 insertions(+), 14 deletions(-) diff --git a/headers/modsecurity/assay.h b/headers/modsecurity/assay.h index d4ea4333..fa3ac059 100644 --- a/headers/modsecurity/assay.h +++ b/headers/modsecurity/assay.h @@ -191,9 +191,10 @@ class Assay { clock_t start; int highest_severity; + Rules *m_rules; + private: std::ofstream myfile; - Rules *m_rules; ModSecurity *m_ms; const char *m_clientIpAddress; diff --git a/headers/modsecurity/rules.h b/headers/modsecurity/rules.h index 68d74ea6..0e495972 100644 --- a/headers/modsecurity/rules.h +++ b/headers/modsecurity/rules.h @@ -13,6 +13,9 @@ * */ +#include +#include + #ifdef __cplusplus #include #include @@ -47,10 +50,20 @@ class Driver; class Rules : public RulesProperties { public: Rules() - : RulesProperties(NULL) { } + : RulesProperties(NULL), + unicode_codepage(0) { + unicode_map_table = reinterpret_cast( + malloc(sizeof(int)*65536)); + memset(unicode_map_table, -1, (sizeof(int)*65536)); + } explicit Rules(DebugLog *customLog) - : RulesProperties(customLog) { } + : unicode_codepage(0), + RulesProperties(customLog) { + unicode_map_table = reinterpret_cast( + malloc(sizeof(int)*65536)); + memset(unicode_map_table, -1, (sizeof(int)*65536)); + } ~Rules(); @@ -76,6 +89,9 @@ class Rules : public RulesProperties { DebugLog *debugLog; + int *unicode_map_table; + int64_t unicode_codepage; + private: int m_referenceCount; }; diff --git a/src/actions/transformations/transformation.cc b/src/actions/transformations/transformation.cc index 00cec3c6..eaa97708 100644 --- a/src/actions/transformations/transformation.cc +++ b/src/actions/transformations/transformation.cc @@ -105,7 +105,7 @@ Transformation* Transformation::instantiate(std::string a) { IF_MATCH(trim_left) { return new TrimLeft(a); } IF_MATCH(trim_right) { return new TrimRight(a); } IF_MATCH(url_decode) { return new UrlDecode(a); } - IF_MATCH(url_decode_uni) { return new UrlDecodeUni(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_uni.cc b/src/actions/transformations/url_decode_uni.cc index d9cbe2ae..d315ae7d 100644 --- a/src/actions/transformations/url_decode_uni.cc +++ b/src/actions/transformations/url_decode_uni.cc @@ -15,6 +15,8 @@ #include "actions/transformations/url_decode_uni.h" +#include + #include #include #include @@ -24,26 +26,28 @@ #include "modsecurity/assay.h" #include "actions/transformations/transformation.h" +#include "src/utils.h" namespace ModSecurity { namespace actions { namespace transformations { -UrlDecodeUni::UrlDecodeUni(std::string action) - : Transformation(action) { - this->action_kind = 1; -} std::string UrlDecodeUni::evaluate(std::string value, Assay *assay) { - /** - * @todo Implement the transformation UrlDecodeUni - */ - assay->debug(4, "Transformation UrlDecodeUni is not implemented yet."); - return value; + int changed = 0; + char *tmp = strdup(value.c_str()); + int res = urldecode_uni_nonstrict_inplace_ex(assay, (unsigned char *)tmp, + value.size(), &changed); + std::string ret(""); + ret.assign(tmp); + free(tmp); + + return ret; } + } // namespace transformations } // namespace actions } // namespace ModSecurity diff --git a/src/actions/transformations/url_decode_uni.h b/src/actions/transformations/url_decode_uni.h index 1203ce28..847e27ce 100644 --- a/src/actions/transformations/url_decode_uni.h +++ b/src/actions/transformations/url_decode_uni.h @@ -30,7 +30,9 @@ namespace transformations { class UrlDecodeUni : public Transformation { public: - explicit UrlDecodeUni(std::string action); + explicit UrlDecodeUni(std::string action) + : Transformation(action) { } + std::string evaluate(std::string exp, Assay *assay) override; }; diff --git a/src/utils.cc b/src/utils.cc index 52f255e2..f27d8ceb 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -824,5 +824,137 @@ std::string string_to_hex(const std::string& input) { } +/** + * + * IMP1 Assumes NUL-terminated + */ +int urldecode_uni_nonstrict_inplace_ex(Assay *assay, unsigned char *input, + int64_t input_len, int *changed) { + unsigned char *d = input; + int64_t i, count, fact, j, xv; + int Code, hmap = -1; + + *changed = 0; + + if (input == NULL) return -1; + + i = count = 0; + while (i < input_len) { + if (input[i] == '%') { + if ((i + 1 < input_len) && + ((input[i + 1] == 'u') || (input[i + 1] == 'U'))) { + /* Character is a percent sign. */ + /* IIS-specific %u encoding. */ + if (i + 5 < input_len) { + /* We have at least 4 data bytes. */ + if ((VALID_HEX(input[i + 2])) && + (VALID_HEX(input[i + 3])) && + (VALID_HEX(input[i + 4])) && + (VALID_HEX(input[i + 5]))) { + Code = 0; + fact = 1; + + if (assay + && assay->m_rules->unicode_map_table != NULL + && assay->m_rules->unicode_codepage > 0) { + for (j = 5; j >= 2; j--) { + if (isxdigit((input[i+j]))) { + if (input[i+j] >= 97) { + xv = (input[i+j] - 97) + 10; + } else if (input[i+j] >= 65) { + xv = (input[i+j] - 65) + 10; + } else { + xv = (input[i+j]) - 48; + } + Code += (xv * fact); + fact *= 16; + } + } + + if (Code >= 0 && Code <= 65535) { + hmap = assay->m_rules->unicode_map_table[Code]; + } + } + + if (hmap != -1) { + *d = hmap; + } else { + /* We first make use of the lower byte here, + * ignoring the higher byte. */ + *d = x2c(&input[i + 4]); + + /* Full width ASCII (ff01 - ff5e) + * needs 0x20 added */ + if ((*d > 0x00) && (*d < 0x5f) + && ((input[i + 2] == 'f') + || (input[i + 2] == 'F')) + && ((input[i + 3] == 'f') + || (input[i + 3] == 'F'))) { + (*d) += 0x20; + } + } + d++; + count++; + i += 6; + *changed = 1; + } else { + /* Invalid data, skip %u. */ + *d++ = input[i++]; + *d++ = input[i++]; + count += 2; + } + } else { + /* Not enough bytes (4 data bytes), skip %u. */ + *d++ = input[i++]; + *d++ = input[i++]; + count += 2; + } + } else { + /* Standard URL encoding. */ + /* Are there enough bytes available? */ + if (i + 2 < input_len) { + /* Yes. */ + + /* Decode a %xx combo only if it is valid. + */ + char c1 = input[i + 1]; + char c2 = input[i + 2]; + + if (VALID_HEX(c1) && VALID_HEX(c2)) { + *d++ = x2c(&input[i + 1]); + count++; + i += 3; + *changed = 1; + } else { + /* Not a valid encoding, skip this % */ + *d++ = input[i++]; + count++; + } + } else { + /* Not enough bytes available, skip this % */ + *d++ = input[i++]; + count++; + } + } + } else { + /* Character is not a percent sign. */ + if (input[i] == '+') { + *d++ = ' '; + *changed = 1; + } else { + *d++ = input[i]; + } + + count++; + i++; + } + } + + *d = '\0'; + + return count; +} + + } // namespace ModSecurity diff --git a/src/utils.h b/src/utils.h index c975403f..3f3dccee 100644 --- a/src/utils.h +++ b/src/utils.h @@ -41,6 +41,8 @@ namespace ModSecurity { int normalize_path_inplace(unsigned char *input, int input_len, int win, int *changed); std::string string_to_hex(const std::string& input); + int urldecode_uni_nonstrict_inplace_ex(Assay *assay, unsigned char *input, + int64_t input_len, int *changed); } // namespace ModSecurity #define SRC_UTILS_H_